On Fri, 18 Nov 2005 14:32:46 +1100 (EST), Ben Finney <[EMAIL PROTECTED]> wrote:
>Howdy all, > >I've recently packaged 'enum' in PyPI. In its description, I make the >claim that it creates "immutable" enumeration objects, and that the >enumeration values are "constant" values. > >This raises questions. > >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? > >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? > >In the case of 'enum', can anyone argue for or against an enumerated >type being immutable? Its values being constant? > My notion of enum comes from (what I remember of) Pascal, which is basically an ordered set of names of integers forming a type, and ord(one_of_the_names) gets you the index value. Python's ord seems to demand a string of length one, and doesn't seem to attempt coercion, so that might not fly without a mod. But what we have is named integers, much as True and False are built in names for integer subtypes with value 1 and 0. So I'd say enums should also be int subtypes... Anyway, I would hope that the name->ord(name) mapping would be immutable once defined (though not necessarily obsessively preventing the ususal end runs). Hm, might as well be more concrete ... >>> def makeEnum(ename, names): ... names = names.split() ... top = len(names) ... # define method functions outside class so they'll be closures accessing nested names ... def __new__(cls, name=names[0]): ... try: ... i = names.index(name) ... return int.__new__(cls, i) ... except ValueError: ... if isinstance(name, int) and 0< name < top: return int.__new__(cls, name) ... raise ValueError, 'illegal %s enum value %r'%(cls.__name__, name) ... def __repr__(self): return '%s(%s)' %(self.__class__.__name__, names[self]) ... # return names with names attribute of class or instance ... class getnames(object): ... def __set__(*ignore): raise AttributeError, 'names protected' ... getnames.__get__ = lambda *ignore: names[:] ... return type(ename, (int,),{'__new__':__new__, '__repr__':__repr__, '__str__':__repr__, ... 'names':getnames()}) ... ... >>> Colors = makeEnum('Color', 'red green blue') >>> Colors <class '__main__.Color'> >>> Colors() Color(red) >>> Colors(1) Color(green) >>> r,g,b = (Colors(name) for name in Colors.names) >>> r Color(red) >>> g Color(green) >>> b Color(blue) >>> 'ABC'[g] 'B' >>> int(g) 1 >>> Colors(5) Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 11, in __new__ ValueError: illegal Color enum value 5 >>> Colors('blue') Color(blue) >>> Colors(2) Color(blue) >>> Colors('orange') Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 11, in __new__ ValueError: illegal Color enum value 'orange' Just some thoughts. Oh, names are kind of protected by [:], but really should throw an exception if you try to mutate. >>> Colors.names ['red', 'green', 'blue'] >>> Colors.names[2] 'blue' >>> Colors.names[2] = 'indigo' >>> Colors.names[2] 'blue' It would be easy to return a tuple. It's not that easy to protect against Colors.names = something since __metaclass__ skips factory local to global if not in class scope, and passing a '__metaclass__': mcdefinition in the dict arg to type does not result in a call, it just becomes another passive class variable. Must be a way though. Too tired for now... Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list