On Sat, 19 Nov 2005 21:45:40 +1100, Steven D'Aprano <[EMAIL PROTECTED]> wrote:
>On Fri, 18 Nov 2005 14:32:46 +1100, Ben Finney wrote: > >> Is there any difference between a Python immutable value, and a >> constant? I suppose "constant" also implies that the *name* binds >> unchangeably to a particular value. Is that meaningful? > >That's precisely how I understand "constant" to be useful. Consider: > >c = 2.9979246e+08 # speed of light in metres per second > >def energy(mass): > """Converts mass in kilograms to energy in joules""" > return mass*c**2 > >That works fine until the name c is rebound to some other value. The fact >that the float object 2.9979246e+08 is immutable is necessary but not >sufficient. > > >> How does one actually ensure an object is immutable? Is it a matter of >> overriding a bunch of methods, or is ther a neater way? >> >> Is it bad style to make a user-defined class that creates immutable >> objects? Why? > >I can't see why it would be bad style. That's what FrozenSet does. > >> In the case of 'enum', can anyone argue for or against an enumerated >> type being immutable? Its values being constant? > >According to Gary Larson, there are four fundamental types of people. You >can tell them apart from their reaction to being given a half-filled glass: > >"The glass is half full." > >"The glass is half empty." > >"Half full. No, half empty! No, half full. Wait... what was the question?" > >"Hey! I ordered a cheeseburger!" > >We might handle this with: > >states = Enum("full", "empty", "indeterminate", "cheeseburger") > >which then get referred to with: > >states.full >states.empty >states.indeterminate >states.cheeseburger > >I might decide that "argumentative" is a better label than "cheeseburger", >and mutate the appropriate enum value. What should happen? I see three >possibilities: > >(1) states.cheeseburger still hangs around, but is not equivalent to >states.argumentative, in much the same way that reloading a module can >leave obsolete objects in your namespace (reloading doesn't automatically >cause names that point to objects from a module to rebind to new objects). > >(2) references to states.cheeseburger raise an exception > >(3) references to states.cheeseburger are equivalent to >states.argumentative. A rose by any other name. It shouldn't matter what >label we put on the enums, in principle. > >Possibility (1) is obviously Bad with a Capital B, and should be avoided. > >Possibility (2) has the saving grace that it is no different to what >happens if you rebind class attributes. There is an argument that if you >can rebind class attributes, you should be able to rebind enums and face >the consequences (good bad or indifferent) as best you can. > >Possibility (3) is the logical solution. It shouldn't matter what labels >we put on the enums. But I fear not practical unless the enums are >implemented as Singletons (multitons?), and perhaps not even then. > >Alternatively, you could bypass the whole problem by making enums >immutable. > as in >>> from makeenum import makeEnum >>> states = makeEnum('states', 'full empty indeterminate cheeseburger') >>> for state in states: print repr(state) ... states.full states.empty states.indeterminate states.cheeseburger >>> for state in states: print state ... full empty indeterminate cheeseburger >>> states <class 'makeenum.states'> >>> states[1] states.empty >>> states['empty'] states.empty >>> states[states.empty] states.empty >>> list(states) [states.full, states.empty, states.indeterminate, states.cheeseburger] >>> map(str, states) ['full', 'empty', 'indeterminate', 'cheeseburger'] >>> len(states) 4 >>> states.full in states True >>> int(states.full) 0 >>> map(int, states) [0, 1, 2, 3] >>> 'ABC'[states.empty] 'B' >>> states.empty == 1 Traceback (most recent call last): File "<stdin>", line 1, in ? File "makeenum.py", line 80, in __cmp__ assert type(self)==type(other), ( AssertionError: incompatible cmp types: states vs int We can optionally make cmp work on int(self), int(other) for the enum... >>> states = makeEnum('states', 'full empty indeterminate cheeseburger', >>> strict=int) >>> 'ABC'[states.empty] 'B' >>> states.empty == 1 True >>> type(states.empty) <class 'makeenum.states'> >>> a = list(states) >>> b = list(states) >>> [x is y for x,y in zip(a,b)] [True, True, True, True] >>> [x is y for x,y in zip(a,states)] [True, True, True, True] (all the states are effectively singleton instances of the states class) and immutable: >>> states.full = 'topped_off' Traceback (most recent call last): File "<stdin>", line 1, in ? File "makeenum.py", line 34, in __setattr__ raise AttributeError, '%s attributes may not be modified'%cls.__name__ AttributeError: states attributes may not be modified >>> states.full.foo = 'trick' Traceback (most recent call last): File "<stdin>", line 1, in ? File "makeenum.py", line 103, in __setattr__ raise AttributeError, '%s instance attributes may not be set'% type(self).__name__ AttributeError: states instance attributes may not be set >>> states[0] = 'something' Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: object does not support item assignment But a slice returns a list, so the transient list absorbs the assignment and disappears >>> states[:][0] = 'something' >>> states[:] [states.full, states.empty, states.indeterminate, states.cheeseburger] >>> states[:]+['something'] [states.full, states.empty, states.indeterminate, states.cheeseburger, 'something'] >>> Fruit = makeEnum('Fruit', 'apple banana') >>> Fruit[0] Fruit.apple contains demands type equality >>> Fruit[0] in states False >>> states[0] in Fruit False >>> int(Fruit[0]) 0 >>> int(states[0]) 0 Fruit was created strict, so >>> Fruit[0] == states[0] Traceback (most recent call last): File "<stdin>", line 1, in ? File "makeenum.py", line 80, in __cmp__ assert type(self)==type(other), ( AssertionError: incompatible cmp types: Fruit vs states i.e., Fruit was created with default strict cmp requiring equal types, but the last states was done with int casting for comparison, so >>> states[0] == Fruit[0] True This thing is evolving, adding some features from Ben Finney's PyPI enum ;-) Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list