constant/enum type in stdlib
![](https://secure.gravatar.com/avatar/2240a37aad5f5834a92809a5e5f01fe1.jpg?s=120&d=mm&r=g)
This idea is not new - but it is stalled - Last I remember it came around in Python-devel in 2010, in this thread: http://mail.python.org/pipermail/python-dev/2010-November/thread.html#105967 There is an even older PEP (PEP 354) that was rejected just for not being enough interest at the time - And it was not dismissed at all - to the contrary the last e-mail in the thread is a message from the BDLF for it to **be** ! The discussion happened in a bad moment as Python was mostly freature froozen for 3.2 - and it did not show up again for Python 3.3; The reasoning for wanting enums/ constants has been debated already - but one of the main reasons that emerge from that thread are the ability to have named constants (just like we have "True" and "False". why do I think this is needed in the stdlib, and having itin a 3rd party module is not enough? because they are an interesting thing to have, not only on the stdlib, but on several widely used Python projects that don't have other dependencies. Having a feature like this into the stdlib allow these projects to make use of it, without needing other dependencies, and moreover, users which will benefit the most out of such constants will have a wll known "constant" type which won't come as a surprise in each package he is using interactively or debugging. Most of the discussion on the 2010 thread was summed up in a message by Michael Foord in this link http://mail.python.org/pipermail/python-dev/2010-November/106063.html with some follow up here: http://mail.python.org/pipermail/python-dev/2010-November/106065.html js -><- ---------- --
![](https://secure.gravatar.com/avatar/f3ba3ecffd20251d73749afbfa636786.jpg?s=120&d=mm&r=g)
On Tue, Jan 29, 2013 at 11:50 AM, Joao S. O. Bueno <jsbueno@python.org.br> wrote:
This idea is not new - but it is stalled - Last I remember it came around in Python-devel in 2010, in this thread: http://mail.python.org/pipermail/python-dev/2010-November/thread.html#105967
FWIW, since that last discussion, I've switched to using strings for my special constants, dumping them in a container if I need some kind of easy validity checking or iteration. That said, an enum type may still be useful for interoperability with other systems (databases, C APIs, etc). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Tue, Jan 29, 2013 at 3:50 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Tue, Jan 29, 2013 at 11:50 AM, Joao S. O. Bueno <jsbueno@python.org.br> wrote:
This idea is not new - but it is stalled - Last I remember it came around in Python-devel in 2010, in this thread:
http://mail.python.org/pipermail/python-dev/2010-November/thread.html#105967
FWIW, since that last discussion, I've switched to using strings for my special constants, dumping them in a container if I need some kind of easy validity checking or iteration.
That said, an enum type may still be useful for interoperability with other systems (databases, C APIs, etc).
I really wish there would be an enum type in Python that would make sense. ISTM this has been raised numerous times, but not one submitted a good-enough proposal. Eli
![](https://secure.gravatar.com/avatar/2240a37aad5f5834a92809a5e5f01fe1.jpg?s=120&d=mm&r=g)
On 29 January 2013 14:00, Eli Bendersky <eliben@gmail.com> wrote:
On Tue, Jan 29, 2013 at 3:50 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Tue, Jan 29, 2013 at 11:50 AM, Joao S. O. Bueno <jsbueno@python.org.br> wrote:
This idea is not new - but it is stalled - Last I remember it came around in Python-devel in 2010, in this thread:
http://mail.python.org/pipermail/python-dev/2010-November/thread.html#105967
FWIW, since that last discussion, I've switched to using strings for my special constants, dumping them in a container if I need some kind of easy validity checking or iteration.
That said, an enum type may still be useful for interoperability with other systems (databases, C APIs, etc).
I really wish there would be an enum type in Python that would make sense. ISTM this has been raised numerous times, but not one submitted a good-enough proposal.
As I pointed above, this last discussion was coming to a good term. Bad timing and no one clearly saying, with all the words "Michael Foord, please make this into a PEP" made it fade away, I think. js -><-
Eli
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Eli Bendersky wrote:
I really wish there would be an enum type in Python that would make sense. ISTM this has been raised numerous times, but not one submitted a good-enough proposal.
I think the reason the discussion petered out last time is that everyone has a slightly different idea on what an enum type should be like. A number of proposals were made, but none of them stood out as being the obviously right one to put in the std lib. Also, so far nobody has come up with a really elegant solution to the DRY problem that inevitably arises in connection with enums. Ideally you want to be able to specify the names of the enums as identifiers, and not have to write them again as strings or otherwise provide explicit values for them. That seems to be very difficult to achieve cleanly with Python syntax as it stands. -- Greg
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Tue, Jan 29, 2013 at 3:26 PM, Greg Ewing <greg.ewing@canterbury.ac.nz>wrote:
Eli Bendersky wrote:
I really wish there would be an enum type in Python that would make sense. ISTM this has been raised numerous times, but not one submitted a good-enough proposal.
I think the reason the discussion petered out last time is that everyone has a slightly different idea on what an enum type should be like. A number of proposals were made, but none of them stood out as being the obviously right one to put in the std lib.
Also, so far nobody has come up with a really elegant solution to the DRY problem that inevitably arises in connection with enums. Ideally you want to be able to specify the names of the enums as identifiers, and not have to write them again as strings or otherwise provide explicit values for them. That seems to be very difficult to achieve cleanly with Python syntax as it stands.
Since we're discussing a new language feature, why do we have to be restricted by the existing Python syntax? We have plenty of time before 3.4 at this point. Eli
![](https://secure.gravatar.com/avatar/047f2332cde3730f1ed661eebb0c5686.jpg?s=120&d=mm&r=g)
On Tue, Jan 29, 2013 at 6:45 PM, Eli Bendersky <eliben@gmail.com> wrote:
On Tue, Jan 29, 2013 at 3:26 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Eli Bendersky wrote:
I really wish there would be an enum type in Python that would make sense. ISTM this has been raised numerous times, but not one submitted a good-enough proposal.
I think the reason the discussion petered out last time is that everyone has a slightly different idea on what an enum type should be like. A number of proposals were made, but none of them stood out as being the obviously right one to put in the std lib.
Also, so far nobody has come up with a really elegant solution to the DRY problem that inevitably arises in connection with enums. Ideally you want to be able to specify the names of the enums as identifiers, and not have to write them again as strings or otherwise provide explicit values for them. That seems to be very difficult to achieve cleanly with Python syntax as it stands.
Hm, if people really want to write something like color = enum(RED, WHITE, BLUE) that might still be true, but given that it's likely going to look a little more like a class definition, this doesn't look so bad, and certainly doesn't violate DRY (though it's somewhat verbose): class color(enum): RED = value() WHITE = value() BLUE = value() The Python 3 metaclass can observe the order in which the values are defined easily by setting the class dict to an OrderdDict.
Since we're discussing a new language feature, why do we have to be restricted by the existing Python syntax? We have plenty of time before 3.4 at this point.
Introducing new syntax requires orders of magnitude more convincing than a new library module or even a new builtin. -- --Guido van Rossum (python.org/~guido)
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Guido van Rossum wrote:
this doesn't look so bad, and certainly doesn't violate DRY (though it's somewhat verbose):
class color(enum): RED = value() WHITE = value() BLUE = value()
The verbosity is what makes it fail the "truly elegant" test for me. And I would say that it does violate DRY in the sense that you have to write value() repeatedly for no good reason. Sure, it's not bad enough to make it unusable, but like all the other solutions, it leaves me feeling vaguely annoyed that there isn't a better way. And it *is* bad enough to make writing an enum definition into a dreary chore, rather than the pleasure it should be. -- Greg
![](https://secure.gravatar.com/avatar/8848a81d538f2fc428934988af5c8b42.jpg?s=120&d=mm&r=g)
On 30Jan2013 17:34, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote: | Guido van Rossum wrote: | > this doesn't look so bad, and | > certainly doesn't violate DRY (though it's somewhat verbose): | > | > class color(enum): | > RED = value() | > WHITE = value() | > BLUE = value() | | The verbosity is what makes it fail the "truly elegant" | test for me. And I would say that it does violate DRY | in the sense that you have to write value() repeatedly | for no good reason. | | Sure, it's not bad enough to make it unusable, but like | all the other solutions, it leaves me feeling vaguely | annoyed that there isn't a better way. How about this: Color = enum(RED=None, WHITE=None, BLUE=None, yellow=9) where None means "pick the next natural choice. The __init__ method goes something like this: def __init__(self, style=None, **kw): self._names = {} self._taken = set() for name, value in kw.items: if name in self._names: raise ValueError("name already taken: " + name) if value is None: while seq in self._taken: seq += 1 value = seq elif value in self._taken: raise ValueError("\"%s\": value already taken: %s" % (name, value)) self._names[name] = value self._taken.add(value) Obviously this needs a little work: - you'd allocate the explicit values first and go after the Nones later so that you don't accidentally take an explicit value - you'd support (pluggable?) styles, starting with sequential, allocating 0, 1, 2, ... and bitmask allocating 1, 2, 4, ... but it lets you enumerate the names without quoting and specify explicit values and let the class pick default values. Cheers, -- Cameron Simpson <cs@zip.com.au> ERROR 155 - You can't do that. - Data General S200 Fortran error code list
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Cameron Simpson wrote:
How about this:
Color = enum(RED=None, WHITE=None, BLUE=None, yellow=9)
You see, this is the problem -- there are quite a number of these solutions, all about as good as each other, with none of them standing out as obviously the right choice for stdlib inclusion. Michael Foord's solution has promise, though, as it manages to eliminate *all* of the extraneous cruft and look almost like it's built into the language. Plus it has the bonus of making you go "...??? How the blazes does *that* work?" the first time you see it. :-) -- Greg
![](https://secure.gravatar.com/avatar/d6b9415353e04ffa6de5a8f3aaea0553.jpg?s=120&d=mm&r=g)
On 1/31/2013 3:17 AM, Greg Ewing wrote:
Cameron Simpson wrote:
How about this:
Color = enum(RED=None, WHITE=None, BLUE=None, yellow=9)
You see, this is the problem -- there are quite a number of these solutions, all about as good as each other, with none of them standing out as obviously the right choice for stdlib inclusion.
Michael Foord's solution has promise, though, as it manages to eliminate *all* of the extraneous cruft and look almost like it's built into the language.
Plus it has the bonus of making you go "...??? How the blazes does *that* work?" the first time you see it. :-)
Yeah, I was thinking that if it were added to stdlib, the current metaclass discussion in the reference should be augmented by referring to it as a non-toy example of metaclasses at work. -- Terry Jan Reedy
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
On Jan 31, 2013, at 09:19 AM, Cameron Simpson wrote:
Color = enum(RED=None, WHITE=None, BLUE=None, yellow=9)
Oh, I forgot to mention that flufl.enum has an alternative API that's fairly close to this, although it does not completely eliminate DRY[1]:
from flufl.enum import make make('Animals', ('ant', 'bee', 'cat', 'dog')) <Animals {ant: 1, bee: 2, cat: 3, dog: 4}>
You can also supply the elements as a 2-tuples if you want to specify the values. An example from the docs providing bit flags:
def enumiter(): ... start = 1 ... while True: ... yield start ... start <<= 1 make('Flags', zip(list('abcdefg'), enumiter())) <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}>
Cheers, -Barry [1] The first argument is currently necessary in order to give the right printed representation of the enum.
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that: class Color(Enum): RED, WHITE, BLUE = range(3) However, it's still slightly annoying that you have to specify how many values there are in the range() call. It would be even nicer it we could just use an infinite iterator, such as class Color(Enum): RED, WHITE, BLUE = values() However, the problem here is that the unpacking bytecode anally insists on the iterator providing *exactly* the right number of items, and there is no way for values() to know when to stop producing items. So, suppose we use a slightly extended version of the iterator protocol for unpacking purposes. If the object being unpacked has an __endunpack__ method, we call it after unpacking the last value, and it is responsible for doing appopriate checking and raising an exception if necessary. Otherwise we do as we do now. The values() object can then have an __endunpack__ method that does nothing, allowing you to unpack any number of items from it. -- Greg
![](https://secure.gravatar.com/avatar/db5f70d2f2520ef725839f046bdc32fb.jpg?s=120&d=mm&r=g)
On Wed, 30 Jan 2013 17:58:37 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that:
class Color(Enum): RED, WHITE, BLUE = range(3)
However, it's still slightly annoying that you have to specify how many values there are in the range() call. It would be even nicer it we could just use an infinite iterator, such as
class Color(Enum): RED, WHITE, BLUE = values()
Well, how about: class Color(Enum): values = ('RED', 'WHITE', 'BLUE') ? (replace values with __values__ if you prefer) Regards Antoine.
![](https://secure.gravatar.com/avatar/133b712423066c858d82f5d285a6867a.jpg?s=120&d=mm&r=g)
On 30 January 2013 07:26, Antoine Pitrou <solipsis@pitrou.net> wrote:
On Wed, 30 Jan 2013 17:58:37 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that:
class Color(Enum): RED, WHITE, BLUE = range(3)
With a Python 3 metaclass that provides default values for *looked up* entries you could have this: class Color(Enum): RED, WHITE, BLUE The lookup would create the member - with the appropriate value. Michael
However, it's still slightly annoying that you have to specify how many values there are in the range() call. It would be even nicer it we could just use an infinite iterator, such as
class Color(Enum): RED, WHITE, BLUE = values()
Well, how about:
class Color(Enum): values = ('RED', 'WHITE', 'BLUE')
?
(replace values with __values__ if you prefer)
Regards
Antoine.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
![](https://secure.gravatar.com/avatar/133b712423066c858d82f5d285a6867a.jpg?s=120&d=mm&r=g)
On 30 January 2013 15:22, Michael Foord <fuzzyman@gmail.com> wrote:
On 30 January 2013 07:26, Antoine Pitrou <solipsis@pitrou.net> wrote:
On Wed, 30 Jan 2013 17:58:37 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that:
class Color(Enum): RED, WHITE, BLUE = range(3)
With a Python 3 metaclass that provides default values for *looked up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
The lookup would create the member - with the appropriate value.
class values(dict): def __init__(self): self.value = 0 def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: value = self[key] = self.value self.value += 1 return value class EnumMeta(type): @classmethod def __prepare__(metacls, name, bases): return values() def __new__(cls, name, bases, classdict): result = type.__new__(cls, name, bases, dict(classdict)) return result class Enum(metaclass=EnumMeta): pass class Color(Enum): RED, WHITE, BLUE
Michael
However, it's still slightly annoying that you have to specify how many values there are in the range() call. It would be even nicer it we could just use an infinite iterator, such as
class Color(Enum): RED, WHITE, BLUE = values()
Well, how about:
class Color(Enum): values = ('RED', 'WHITE', 'BLUE')
?
(replace values with __values__ if you prefer)
Regards
Antoine.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
--
May you do good and not evil May you find forgiveness for yourself and forgive others
May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
![](https://secure.gravatar.com/avatar/d6b9415353e04ffa6de5a8f3aaea0553.jpg?s=120&d=mm&r=g)
On 1/30/2013 10:30 AM, Michael Foord wrote:
On 30 January 2013 15:22, Michael Foord
With a Python 3 metaclass that provides default values for *looked up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
The lookup would create the member - with the appropriate value.
class values(dict): def __init__(self): self.value = 0 def __getitem__(self, key):
Adding 'print(self.value, key)' here prints 0 __name__ 0 __name__ 1 RED 2 WHITE 3 BLUE (I do not understand why it is the second and not first lookup of __name__ that increments the counter, but...)
try: return dict.__getitem__(self, key) except KeyError: value = self[key] = self.value self.value += 1 return value
class EnumMeta(type):
@classmethod def __prepare__(metacls, name, bases): return values()
def __new__(cls, name, bases, classdict): result = type.__new__(cls, name, bases, dict(classdict)) return result
class Enum(metaclass=EnumMeta): pass class Color(Enum): RED, WHITE, BLUE
So RED, WHITE, BLUE are 1, 2, 3; not 0, 1, 2 as I and many readers might expect. That aside (which can be fixed), this is very nice. -- Terry Jan Reedy
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 31 January 2013 08:32, Terry Reedy <tjreedy@udel.edu> wrote:
On 1/30/2013 10:30 AM, Michael Foord wrote:
On 30 January 2013 15:22, Michael Foord
With a Python 3 metaclass that provides default values for *looked
up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
The lookup would create the member - with the appropriate value.
class values(dict): def __init__(self): self.value = 0 def __getitem__(self, key):
So RED, WHITE, BLUE are 1, 2, 3; not 0, 1, 2 as I and many readers might expect. That aside (which can be fixed), this is very nice.
Here is a version that I think creates an enum with most of the features of traditional and modern enums. - Enum values are subclasses of int; - Only need to declare the enum key name; - Starts at zero by default; - Can change the start value; - Can have discontiguous values (e.g. 0, 1, 5, 6); - Can have other types of class attributes; - Ensures that there is a 1:1 mapping between key:value (throws an exception if either of these is violated; - Able to obtain the keys, values and items as per the mapping interface (sorted by value); - Lookup an enum by key or value; One thing to note is that *any* class attribute assigned a value which implements __index__ will be considered an enum value assignment. I've done some funky stuff to ensure that you can access all the above either via the enum class, or by an instance of the enum class. Most of the time you would just use the Enum subclass directly (i.e. it's a namespace) but there may be use cases for having instances of the Enum classes. import collections import operator class EnumValue(int): def __new__(cls, key, value): e = super().__new__(cls, value) super().__setattr__(e, 'key', key) return e def __setattr__(self, key, value): raise TypeError("Cannot set attribute of type %r" % (type(self),)) def __repr__(self): return "<%s '%s': %d>" % (self.__qualname__, self.key, self) class EnumValues(collections.OrderedDict): def __init__(self): super().__init__() self.value = 0 self.sealed = False def __getitem__(self, key): try: obj = super().__getitem__(key) if not self.sealed and isinstance(obj, EnumValue): raise TypeError("Duplicate enum key '%s' with values: %d and %d" % (obj.key, obj, self.value)) return obj except KeyError: if key[:2] == '__' and key[-2:] == '__': raise value = self.value super().__setitem__(key, EnumValue(key, value)) self.value += 1 return value def __setitem__(self, key, value): if key[:2] == '__' and key[-2:] == '__': return super().__setitem__(key, value) try: if isinstance(value, EnumValue): assert value.key == key else: value = operator.index(value) except TypeError: return super().__setitem__(key, value) try: o = super().__getitem__(key) if isinstance(o, EnumValue): raise TypeError("Duplicate enum key '%s' with values: %d and %d" % (o.key, o, value)) except KeyError: self.value = value + 1 if isinstance(value, EnumValue): value = value else: value = EnumValue(key, value) super().__setitem__(value.key, value) class EnumMeta(type): @classmethod def __prepare__(metacls, name, bases): return EnumValues() def __new__(cls, name, bases, classdict): classdict.sealed = True result = type.__new__(cls, name, bases, dict(classdict)) enum = [] for v in classdict.values(): if isinstance(v, EnumValue): enum.append(v) enum.sort() result._key_to_enum = collections.OrderedDict() result._value_to_enum = collections.OrderedDict() for e in enum: if e in result._value_to_enum: raise TypeError("Duplicate enum value %d for keys: '%s' and '%s'" % (e, result._value_to_enum[e].key), e.key) if e.key in result._key_to_enum: raise TypeError("Duplicate enum key '%s' with values: %d and %d" % (e.key, result._key_to_enum[e.key]), e) result._key_to_enum[e.key] = e result._value_to_enum[e] = e return result def __getitem__(self, key): try: key = operator.index(key) except TypeError: return self._key_to_enum[key] else: return self._value_to_enum[key] def _items(self): return self._key_to_enum.items() def _keys(self): return self._key_to_enum.keys() def _values(self): return self._key_to_enum.values() def items(self): return self._items() def keys(self): return self._keys() def values(self): return self._values() class Enum(metaclass=EnumMeta): def __getitem__(self, key): cls = type(self) return type(cls).__getitem__(cls, key) def items(cls): return cls._items() def keys(cls): return cls._keys() def values(cls): return cls._values() Enum.items = classmethod(Enum.items) Enum.keys = classmethod(Enum.keys) Enum.values = classmethod(Enum.values) class Color(Enum): RED, WHITE, BLUE GREEN = 4 YELLOW ORANGE = 'orange' BLACK def dump(self): print(self.RED, self.WHITE, self.BLUE, self.GREEN, self.YELLOW, self.BLACK, self.ORANGE, self.dump) print(Color.RED, Color.WHITE, Color.BLUE, Color.GREEN, Color.YELLOW, Color.BLACK, Color.ORANGE, Color.dump) Color().dump() print(repr(Color.RED)) print(repr(Color['RED'])) print(repr(Color().RED)) print(repr(Color()['RED'])) print(repr(Color[0])) print(repr(Color()[0])) print(*Color.items()) print(*Color().items()) print(*Color.keys()) print(*Color().keys()) print(*Color.values()) print(*Color().values()) Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 31 January 2013 12:27, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
On 31 January 2013 08:32, Terry Reedy <tjreedy@udel.edu> wrote:
On 1/30/2013 10:30 AM, Michael Foord wrote:
On 30 January 2013 15:22, Michael Foord
With a Python 3 metaclass that provides default values for *looked
up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
The lookup would create the member - with the appropriate value.
class values(dict): def __init__(self): self.value = 0 def __getitem__(self, key):
So RED, WHITE, BLUE are 1, 2, 3; not 0, 1, 2 as I and many readers might expect. That aside (which can be fixed), this is very nice.
Here is a version that I think creates an enum with most of the features of traditional and modern enums.
- Enum values are subclasses of int;
- Only need to declare the enum key name;
- Starts at zero by default;
- Can change the start value;
- Can have discontiguous values (e.g. 0, 1, 5, 6);
- Can have other types of class attributes;
- Ensures that there is a 1:1 mapping between key:value (throws an exception if either of these is violated;
- Able to obtain the keys, values and items as per the mapping interface (sorted by value);
- Lookup an enum by key or value;
One thing to note is that *any* class attribute assigned a value which implements __index__ will be considered an enum value assignment.
Forgot about making it iterable - an easy-to-ad feature. Obviously it would iterate over the EnumValue instancess. Thought I'd better make it explicit as well that this was based on Michael Foords brilliant work. Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
Wow - this ended up being more difficult that I'd anticipated. Ensuring that decorators work in Michael Foord-inspired enums, etc mandated a fairly big redesign. As it was you couldn't do something like: class A(): a = 1 class B(Enum): B = A.a because when the name 'A' was resolved in the definition of class B, it was returning an int (the next enum value). The end result may well still have some holes, but it's looking pretty good to me. Enums are constructed like: class Color(Enum): RED, GREEN, BLUE CYAN = 10 MAGENTA YELLOW BLACK where the assigned enum starts a new count i.e. the above is 0, 1, 2, 10, 11, 12, 13. Arbitrary attributes may be assigned and not contribute to the enumeration so long as the object being assigned does not implement __index__ (if it does, it creates a discontiguous enumeration). #!/usr/bin/env python3 import builtins import collections import operator class EnumValue(int): def __new__(cls, key, value): e = super().__new__(cls, value) super().__setattr__(e, 'key', key) super().__setattr__(e, 'owner', None) return e def __setattr__(self, key, value): raise TypeError("Cannot set attribute of type %r" % (type(self),)) def __str__(self): return "%s.%s" % (self.owner.__name__, self.key) def __repr__(self): if self.owner is not None: return "<%s '%s.%s': %d>" % (self.__qualname__, self.owner.__qualname__, self.key, int(self)) return "<%s '%s': %d>" % (self.__qualname__, self.key, int(self)) class EnumProxy(object): def __init__(self, key, value=None): self.key = key self.values = [value] self.used = False def __repr__(self): return "<%s '%s': %s>" % (self.__qualname__, self.key, self.values) def _get(self, used=None): if used: self.used = True try: return locals()[self.key] except KeyError: try: return globals()[self.key] except KeyError: try: return getattr(builtins, self.key) except KeyError: raise NameError(self.key, self.values) def __call__(self, *p, **kw): return self._get(True)(*p, **kw) def __getattr__(self, name): return getattr(self._get(True), name) class EnumValues(collections.OrderedDict): def __init__(self): super().__init__() self.sealed = False def __getitem__(self, key): try: obj = super().__getitem__(key) if not self.sealed and isinstance(obj, EnumProxy): obj.values.append(None) return obj except KeyError: # Don't do anything with __dunder__ attributes if key[:2] == '__' and key[-2:] == '__': raise proxy = EnumProxy(key, None) super().__setitem__(key, proxy) return proxy def __setitem__(self, key, value): if key[:2] == '__' and key[-2:] == '__': return super().__setitem__(key, value) try: if isinstance(value, EnumProxy): value = value._get(True) elif not isinstance(value, EnumValue): value = operator.index(value) except TypeError: return super().__setitem__(key, value) try: o = super().__getitem__(key) if isinstance(o, EnumProxy): o.values.append(value) except KeyError: if isinstance(value, EnumProxy): int.__setattr__(value, 'key', key) else: value = EnumProxy(key, value) super().__setitem__(value.key, value) class EnumMeta(type): @classmethod def __prepare__(metacls, name, bases): return EnumValues() def __new__(cls, name, bases, classdict): classdict.sealed = True del_list = [] for v in classdict.values(): if isinstance(v, EnumProxy) and v.used: del_list.append(v) for v in del_list: del classdict[v.key] result = type.__new__(cls, name, bases, dict(classdict)) value = 0 keys = {} values = {} for v in classdict.values(): if isinstance(v, EnumProxy) and not v.used: if len(v.values) > 1: raise AttributeError("Duplicate enum key '%s.%s'" % (result.__qualname__, v.key,)) if v.values[0] is not None: value = v.values[0] if isinstance(value, EnumValue): if (value.key is not None) and (value.key != v.key): raise AttributeError("Assigned enum value to non-matching key '%s': %r" % (v.key, value)) if value.owner is not None: raise AttributeError("Assigned owned enum value to key '%s': %r" % (v.key, value)) int.__setattr__(value, 'key', v.key) v = value else: v = EnumValue(v.key, value) setattr(result, v.key, v) value += 1 if isinstance(v, EnumValue): int.__setattr__(v, 'owner', result) if v in values: raise AttributeError("Duplicate enum value %d for keys: '%s' and '%s'" % (int(v), values[v].key, v.key)) keys[v.key] = v values[v] = v enum = sorted(values) result._key_to_enum = collections.OrderedDict() result._value_to_enum = values for e in enum: result._key_to_enum[e.key] = e return result def __getitem__(self, key): try: key = operator.index(key) except TypeError: return self._key_to_enum[key] else: return self._value_to_enum[key] def _items(self): return self._key_to_enum.items() def _keys(self): return self._key_to_enum.keys() def _values(self): return self._key_to_enum.values() def items(self): return self._items() def keys(self): return self._keys() def values(self): return self._values() def __iter__(self): return iter(self.values()) def __repr__(self): r = super().__repr__() r = ['<enum', r[6:-1], ' '] r.append(str(self)) r.append('>') return ''.join(r) def __str__(self): s = ['{'] for k, v in self.items(): if s[-1][-1:] != '{': s.append(', ') s.extend([k, ':', str(int(v))]) s.append('}') return ''.join(s) class Enum(metaclass=EnumMeta): def __getitem__(self, key): cls = type(self) return type(cls).__getitem__(cls, key) @classmethod def items(cls): return cls._items() @classmethod def keys(cls): return cls._keys() @classmethod def values(cls): return cls._values() def __iter__(self): return iter(self.values()) def __repr__(self): r = super().__repr__() r = r.replace('object at 0x', 'enum at 0x') r = [r[:-1], ' '] r.append(str(self)) r.append('>') return ''.join(r) def __str__(self): return str(type(self)) if __name__ == '__main__': class Color(Enum): RED, GREEN, BLUE ORANGE = "orange" CYAN = 10 MAGENTA YELLOW BLACK import unittest class TestEnum(unittest.TestCase): EXPECTED_KEY_ORDER = ('RED', 'GREEN', 'BLUE', 'CYAN', 'MAGENTA', 'YELLOW', 'BLACK') EXPECTED_INT_VALUE_ORDER = (0, 1, 2, 10, 11, 12, 13) EXPECTED_ENUM_VALUE_ORDER = (Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA, Color.YELLOW, Color.BLACK) EXPECTED_ITEMS_ORDER = tuple(zip(EXPECTED_KEY_ORDER, EXPECTED_ENUM_VALUE_ORDER)) def test_type(self): self.assertIsInstance(Color.RED, int) def test_class_enum_values(self): self.assertEqual(0, Color.RED) self.assertEqual(1, Color.GREEN) self.assertEqual(2, Color.BLUE) self.assertEqual(10, Color.CYAN) self.assertEqual(11, Color.MAGENTA) self.assertEqual(12, Color.YELLOW) self.assertEqual(13, Color.BLACK) self.assertEqual("orange", Color.ORANGE) def test_instance_enum_values(self): e = Color() self.assertIs(Color.RED, e.RED) self.assertIs(Color.GREEN, e.GREEN) self.assertIs(Color.BLUE, e.BLUE) self.assertIs(Color.CYAN, e.CYAN) self.assertIs(Color.MAGENTA, e.MAGENTA) self.assertIs(Color.YELLOW, e.YELLOW) self.assertIs(Color.BLACK, e.BLACK) self.assertIs(Color.ORANGE, e.ORANGE) def test_class_indexing(self): self.assertIs(Color.CYAN, Color['CYAN']) self.assertIs(Color.CYAN, Color[10]) def test_instance_indexing(self): e = Color() self.assertIs(Color.CYAN, e['CYAN']) self.assertIs(Color.CYAN, e[10]) def test_class_keys(self): self.assertEqual(self.EXPECTED_KEY_ORDER, tuple(Color.keys())) def test_instance_keys(self): self.assertEqual(tuple(Color.keys()), tuple(Color().keys())) def test_class_values(self): self.assertEqual(self.EXPECTED_INT_VALUE_ORDER, tuple(Color.values())) self.assertEqual(self.EXPECTED_ENUM_VALUE_ORDER, tuple(Color.values())) def test_instance_values(self): self.assertEqual(tuple(Color.values()), tuple(Color().values())) def test_class_items(self): self.assertEqual(self.EXPECTED_ITEMS_ORDER, tuple(Color.items())) def test_instance_items(self): self.assertEqual(tuple(Color.items()), tuple(Color().items())) def test_owner(self): for e in Color: self.assertIs(e.owner, Color) def test_class_str(self): s = str(Color) for e in Color: self.assertIn('%s:%d' % (e.key, int(e)), s) def test_instance_str(self): self.assertEqual(str(Color), str(Color())) def test_class_repr(self): r = repr(Color) self.assertIn(Color.__qualname__, r) self.assertIn(str(Color), r) def test_instance_repr(self): e = Color() r = repr(e) self.assertIn(Color.__qualname__, r) self.assertIn('at 0x', r) self.assertIn(str(Color()), r) def _create_duplicate_key(self): class DuplicateKey(Enum): KEY, KEY def test_duplicate_key(self): self.assertRaises(AttributeError, self._create_duplicate_key) def _create_duplicate_value(self): class DuplicateValue(Enum): KEY1, KEY2 = 0 def test_duplicate_value(self): self.assertRaises(AttributeError, self._create_duplicate_value) def _assign_wrong_key(self): class WrongKey(Enum): KEY1 = EnumValue('KEY2', 0) def test_wrong_key(self): self.assertRaises(AttributeError, self._assign_wrong_key) def test_unnamed_key1(self): class UnnamedKey(Enum): KEY1 = EnumValue(None, 5) self.assertEqual(UnnamedKey.KEY1, 5) self.assertIs(UnnamedKey, UnnamedKey.KEY1.owner) def test_unnamed_key1(self): unnamed = EnumValue(None, 5) class UnnamedKey(Enum): KEY1 = unnamed self.assertEqual(UnnamedKey.KEY1, 5) self.assertIs(UnnamedKey.KEY1, unnamed) self.assertIs(UnnamedKey, UnnamedKey.KEY1.owner) def _assign_wrong_owner(self): class WrongOwner(Enum): KEY1 = Color.RED def test_wrong_owner(self): self.assertRaises(AttributeError, self._assign_wrong_owner) unittest.main() Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int). - Enums are now subclassable; - Added an Enum.make() method - a bit different to flufl.enum.make since my enums have different semantics - each element must either be a name or a (name, value) pair, and you can have a mix; - Instantiating an enum now returns the appropriate EnumValue - Enums now compare not equal with any enum that is not the same object (but continue to compare equal with ints); - Changed EnumValue.key -> EnumValue.name and EnumValue.owner -> EnumValue.enum. I didn't add __members__ as that use case is covered by having the Enum be iterable + the immutable mapping interface. #!/usr/bin/env python3 import builtins import collections import operator class EnumValue(int): def __new__(cls, key, value): e = super().__new__(cls, value) super().__setattr__(e, 'name', key) super().__setattr__(e, 'enum', None) return e def __setattr__(self, key, value): raise TypeError("can't set attribute") def __eq__(self, other): if isinstance(other, EnumValue): return self is other return int(self) == other def __ne__(self, other): return not (self == other) def __hash__(self): return super().__hash__() def __str__(self): if self.enum is not None: return "%s.%s" % (self.enum.__name__, self.name) return self.name def __repr__(self): if self.enum is not None: return "<%s '%s.%s': %d>" % (self.__qualname__, self.enum.__qualname__, self.name, int(self)) return "<%s '%s': %d>" % (self.__qualname__, self.name, int(self)) class _EnumProxy(object): def __init__(self, key, value=None): self.key = key self.values = [value] self.used = False def __repr__(self): return "<%s '%s': %s>" % (self.__qualname__, self.key, self.values) def _get(self, used=None): if used: self.used = True try: return locals()[self.key] except KeyError: try: return globals()[self.key] except KeyError: try: return getattr(builtins, self.key) except KeyError: raise NameError(self.key, self.values) def __call__(self, *p, **kw): return self._get(True)(*p, **kw) def __getattr__(self, name): return getattr(self._get(True), name) class EnumValues(collections.OrderedDict): def __init__(self): super().__init__() self.sealed = False def __getitem__(self, key): try: obj = super().__getitem__(key) if not self.sealed and isinstance(obj, _EnumProxy): obj.values.append(None) return obj except KeyError: # Don't do anything with __dunder__ attributes if key[:2] == '__' and key[-2:] == '__': raise proxy = _EnumProxy(key, None) super().__setitem__(key, proxy) return proxy def __setitem__(self, key, value): if key[:2] == '__' and key[-2:] == '__': return super().__setitem__(key, value) try: if isinstance(value, _EnumProxy): value = value._get(True) elif not isinstance(value, EnumValue): value = operator.index(value) except TypeError: return super().__setitem__(key, value) try: o = super().__getitem__(key) if isinstance(o, _EnumProxy): o.values.append(value) except KeyError: if not isinstance(value, _EnumProxy): value = _EnumProxy(key, value) super().__setitem__(value.key, value) class EnumMeta(type): @classmethod def __prepare__(metacls, name, bases): return EnumValues() def __new__(cls, name, bases, classdict): classdict.sealed = True del_list = [] for v in classdict.values(): if isinstance(v, _EnumProxy) and v.used: del_list.append(v) for v in del_list: del classdict[v.key] result = type.__new__(cls, name, bases, dict(classdict)) keys = {} values = {} result._key_to_enum = collections.OrderedDict() result._value_to_enum = values value = 0 for b in result.__bases__: if isinstance(b, EnumMeta): keys.update(b._key_to_enum) values.update(b._value_to_enum) if values: value = max(values) + 1 for v in classdict.values(): if isinstance(v, _EnumProxy) and not v.used: if len(v.values) > 1: raise AttributeError("Duplicate enum key '%s.%s'" % (name, v.key,)) elif v.key in keys: raise AttributeError("Duplicate enum key '%s.%s' (overriding '%s')" % (result.__name__, v.key, keys[v.key])) if v.values[0] is not None: value = v.values[0] if isinstance(value, EnumValue): if (value.name is not None) and (value.name != v.key): raise AttributeError("Assigned enum value to non-matching key '%s': %r" % (v.key, value)) if value.enum is not None: raise AttributeError("Assigned owned enum value to key '%s': %r" % (v.key, value)) int.__setattr__(value, 'name', v.key) v = value else: v = EnumValue(v.key, value) setattr(result, v.name, v) value += 1 if isinstance(v, EnumValue): int.__setattr__(v, 'enum', result) int_v = int(v) if int_v in values: raise AttributeError("Duplicate enum value %d for keys: '%s.%s' and '%s.%s'" % ( int_v, values[int_v].enum.__name__, values[int_v].name, result.__name__, v.name)) keys[v.name] = v values[v] = v enum = sorted(values) for e in enum: result._key_to_enum[e.name] = e return result def __getitem__(self, key): try: key = operator.index(key) except TypeError: return self._key_to_enum[key] else: return self._value_to_enum[key] def items(self): return self._key_to_enum.items() def keys(self): return self._key_to_enum.keys() def values(self): return self._key_to_enum.values() def __iter__(self): return iter(self.values()) def __repr__(self): r = super().__repr__() r = ['<enum', r[6:-1], ' '] r.append(str(self)) r.append('>') return ''.join(r) def __str__(self): s = ['{'] for k, v in self.items(): if s[-1][-1:] != '{': s.append(', ') s.extend([k, ':', str(int(v))]) s.append('}') return ''.join(s) class Enum(metaclass=EnumMeta): def __new__(cls, value): return type(cls).__getitem__(cls, value) @staticmethod def make(clsname, elements): classdict = collections.OrderedDict() for e in elements: if isinstance(e, tuple): k, v = e else: k, v = e, None try: e = classdict[k] e.values.append(v) except KeyError: classdict[k] = _EnumProxy(k, v) result = EnumMeta(clsname, (Enum,), classdict) # For some reason, this is set to 'Enum' ... result.__qualname__ = clsname return result if __name__ == '__main__': import unittest class Color(Enum): RED, GREEN, BLUE ORANGE = "orange" CYAN = 10 MAGENTA YELLOW BLACK class TestEnum(unittest.TestCase): EXPECTED_KEY_ORDER = ('RED', 'GREEN', 'BLUE', 'CYAN', 'MAGENTA', 'YELLOW', 'BLACK') EXPECTED_INT_VALUE_ORDER = (0, 1, 2, 10, 11, 12, 13) EXPECTED_ENUM_VALUE_ORDER = (Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA, Color.YELLOW, Color.BLACK) EXPECTED_ITEMS_ORDER = tuple(zip(EXPECTED_KEY_ORDER, EXPECTED_ENUM_VALUE_ORDER)) def test_type(self): self.assertIsInstance(Color.RED, int) def test_enum_values(self): self.assertEqual(0, Color.RED) self.assertEqual(1, Color.GREEN) self.assertEqual(2, Color.BLUE) self.assertEqual(10, Color.CYAN) self.assertEqual(11, Color.MAGENTA) self.assertEqual(12, Color.YELLOW) self.assertEqual(13, Color.BLACK) self.assertEqual("orange", Color.ORANGE) def test_indexing(self): self.assertIs(Color.CYAN, Color['CYAN']) self.assertIs(Color.CYAN, Color[10]) def test_keys(self): self.assertEqual(self.EXPECTED_KEY_ORDER, tuple(Color.keys())) def test_values(self): self.assertEqual(self.EXPECTED_INT_VALUE_ORDER, tuple(Color.values())) self.assertEqual(self.EXPECTED_ENUM_VALUE_ORDER, tuple(Color.values())) def test_items(self): self.assertEqual(self.EXPECTED_ITEMS_ORDER, tuple(Color.items())) def test_owner(self): for e in Color: self.assertIs(e.enum, Color) def test_equality(self): self.assertEqual(0, Color.RED) self.assertEqual(Color.RED, 0) self.assertEqual(Color.RED, Color.RED) self.assertNotEqual(Color.RED, Color.GREEN) class Color2(Enum): RED self.assertEqual(0, Color2.RED) self.assertEqual(Color2.RED, 0) self.assertNotEqual(Color.RED, Color2.RED) def test_str(self): s = str(Color) for e in Color: self.assertIn('%s:%d' % (e.name, int(e)), s) def test_repr(self): r = repr(Color) self.assertIn(Color.__qualname__, r) self.assertIn(str(Color), r) def test_instances(self): for e in Color: self.assertIs(e, Color(e)) self.assertIs(e, Color(int(e))) self.assertIs(e, Color(e.name)) def _create_duplicate_key(self): class DuplicateKey(Enum): KEY, KEY def test_duplicate_key(self): self.assertRaises(AttributeError, self._create_duplicate_key) def _create_duplicate_value(self): class DuplicateValue(Enum): KEY1, KEY2 = 0 def test_duplicate_value(self): self.assertRaises(AttributeError, self._create_duplicate_value) def _assign_wrong_key(self): class WrongKey(Enum): KEY1 = EnumValue('KEY2', 0) def test_wrong_key(self): self.assertRaises(AttributeError, self._assign_wrong_key) def test_unnamed_key1(self): class UnnamedKey(Enum): KEY1 = EnumValue(None, 5) self.assertEqual(UnnamedKey.KEY1, 5) self.assertIs(UnnamedKey, UnnamedKey.KEY1.enum) def test_unnamed_key1(self): unnamed = EnumValue(None, 5) class UnnamedKey(Enum): KEY1 = unnamed self.assertEqual(UnnamedKey.KEY1, 5) self.assertIs(UnnamedKey.KEY1, unnamed) self.assertIs(UnnamedKey, UnnamedKey.KEY1.enum) def _assign_wrong_owner(self): class WrongOwner(Enum): KEY1 = Color.RED def test_wrong_owner(self): self.assertRaises(AttributeError, self._assign_wrong_owner) def test_subclassing(self): class ExtendedColor1(Color): PINK, GREY, WHITE = 5 self.assertIs(Color.RED, ExtendedColor1.RED) for e in Color: self.assertIs(e, ExtendedColor1[e]) self.assertEqual(14, ExtendedColor1.PINK) self.assertEqual(15, ExtendedColor1.GREY) self.assertEqual(5, ExtendedColor1.WHITE) class ExtendedColor2(Color): PINK, GREY, WHITE = 5 self.assertIsNot(ExtendedColor1.PINK, ExtendedColor2.PINK) self.assertIs(ExtendedColor1.RED, ExtendedColor2.RED) def _create_duplicate_key_subclass(self): class ExtendedColor1(Color): RED def test_duplicate_key_subclass(self): self.assertRaises(AttributeError, self._create_duplicate_key_subclass) def _create_duplicate_value_subclass(self): class ExtendedColor1(Color): PINK = 0 def test_duplicate_key_subclass(self): self.assertRaises(AttributeError, self._create_duplicate_value_subclass) def test_make(self): e = Enum.make('e', ('a', 'b', ('c', 3), 'd')) self.assertEqual(0, e.a) self.assertEqual(1, e.b) self.assertEqual(3, e.c) self.assertEqual(4, e.d) def test_duplicate_key_make(self): self.assertRaises(AttributeError, Enum.make, 'e', ('a', 'b', ('b', 3), 'd')) self.assertRaises(AttributeError, Enum.make, 'e', ('a', 'b', ('c', 3), 'b')) def test_duplicate_value_make(self): self.assertRaises(AttributeError, Enum.make, 'e', ('a', 'b', ('c', 1), 'd')) unittest.main() Tim Delaney On 1 February 2013 11:19, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
Wow - this ended up being more difficult that I'd anticipated. Ensuring that decorators work in Michael Foord-inspired enums, etc mandated a fairly big redesign. As it was you couldn't do something like:
class A(): a = 1
class B(Enum): B = A.a
because when the name 'A' was resolved in the definition of class B, it was returning an int (the next enum value).
The end result may well still have some holes, but it's looking pretty good to me.
Enums are constructed like:
class Color(Enum): RED, GREEN, BLUE CYAN = 10 MAGENTA YELLOW BLACK
where the assigned enum starts a new count i.e. the above is 0, 1, 2, 10, 11, 12, 13. Arbitrary attributes may be assigned and not contribute to the enumeration so long as the object being assigned does not implement __index__ (if it does, it creates a discontiguous enumeration).
#!/usr/bin/env python3
import builtins import collections import operator
class EnumValue(int): def __new__(cls, key, value): e = super().__new__(cls, value) super().__setattr__(e, 'key', key) super().__setattr__(e, 'owner', None) return e
def __setattr__(self, key, value): raise TypeError("Cannot set attribute of type %r" % (type(self),))
def __str__(self): return "%s.%s" % (self.owner.__name__, self.key)
def __repr__(self): if self.owner is not None: return "<%s '%s.%s': %d>" % (self.__qualname__, self.owner.__qualname__, self.key, int(self))
return "<%s '%s': %d>" % (self.__qualname__, self.key, int(self))
class EnumProxy(object): def __init__(self, key, value=None): self.key = key self.values = [value] self.used = False
def __repr__(self): return "<%s '%s': %s>" % (self.__qualname__, self.key, self.values)
def _get(self, used=None): if used: self.used = True
try: return locals()[self.key] except KeyError: try: return globals()[self.key] except KeyError: try: return getattr(builtins, self.key) except KeyError: raise NameError(self.key, self.values)
def __call__(self, *p, **kw): return self._get(True)(*p, **kw)
def __getattr__(self, name): return getattr(self._get(True), name)
class EnumValues(collections.OrderedDict): def __init__(self): super().__init__() self.sealed = False
def __getitem__(self, key): try: obj = super().__getitem__(key)
if not self.sealed and isinstance(obj, EnumProxy): obj.values.append(None)
return obj
except KeyError: # Don't do anything with __dunder__ attributes if key[:2] == '__' and key[-2:] == '__': raise
proxy = EnumProxy(key, None) super().__setitem__(key, proxy) return proxy
def __setitem__(self, key, value): if key[:2] == '__' and key[-2:] == '__': return super().__setitem__(key, value)
try: if isinstance(value, EnumProxy): value = value._get(True)
elif not isinstance(value, EnumValue): value = operator.index(value)
except TypeError: return super().__setitem__(key, value)
try: o = super().__getitem__(key)
if isinstance(o, EnumProxy): o.values.append(value)
except KeyError: if isinstance(value, EnumProxy): int.__setattr__(value, 'key', key) else: value = EnumProxy(key, value)
super().__setitem__(value.key, value)
class EnumMeta(type):
@classmethod def __prepare__(metacls, name, bases): return EnumValues()
def __new__(cls, name, bases, classdict): classdict.sealed = True
del_list = []
for v in classdict.values(): if isinstance(v, EnumProxy) and v.used: del_list.append(v)
for v in del_list: del classdict[v.key]
result = type.__new__(cls, name, bases, dict(classdict)) value = 0 keys = {} values = {}
for v in classdict.values(): if isinstance(v, EnumProxy) and not v.used: if len(v.values) > 1: raise AttributeError("Duplicate enum key '%s.%s'" % (result.__qualname__, v.key,))
if v.values[0] is not None: value = v.values[0]
if isinstance(value, EnumValue):
if (value.key is not None) and (value.key != v.key): raise AttributeError("Assigned enum value to non-matching key '%s': %r" % (v.key, value))
if value.owner is not None: raise AttributeError("Assigned owned enum value to key '%s': %r" % (v.key, value))
int.__setattr__(value, 'key', v.key) v = value
else: v = EnumValue(v.key, value)
setattr(result, v.key, v) value += 1
if isinstance(v, EnumValue): int.__setattr__(v, 'owner', result)
if v in values: raise AttributeError("Duplicate enum value %d for keys: '%s' and '%s'" % (int(v), values[v].key, v.key))
keys[v.key] = v values[v] = v
enum = sorted(values)
result._key_to_enum = collections.OrderedDict() result._value_to_enum = values
for e in enum: result._key_to_enum[e.key] = e
return result
def __getitem__(self, key): try: key = operator.index(key) except TypeError: return self._key_to_enum[key] else: return self._value_to_enum[key]
def _items(self): return self._key_to_enum.items()
def _keys(self): return self._key_to_enum.keys()
def _values(self): return self._key_to_enum.values()
def items(self): return self._items()
def keys(self): return self._keys()
def values(self): return self._values()
def __iter__(self): return iter(self.values())
def __repr__(self): r = super().__repr__() r = ['<enum', r[6:-1], ' '] r.append(str(self)) r.append('>') return ''.join(r)
def __str__(self): s = ['{']
for k, v in self.items(): if s[-1][-1:] != '{': s.append(', ')
s.extend([k, ':', str(int(v))])
s.append('}') return ''.join(s)
class Enum(metaclass=EnumMeta): def __getitem__(self, key): cls = type(self) return type(cls).__getitem__(cls, key)
@classmethod def items(cls): return cls._items()
@classmethod def keys(cls): return cls._keys()
@classmethod def values(cls): return cls._values()
def __iter__(self): return iter(self.values())
def __repr__(self): r = super().__repr__() r = r.replace('object at 0x', 'enum at 0x') r = [r[:-1], ' '] r.append(str(self)) r.append('>') return ''.join(r)
def __str__(self): return str(type(self))
if __name__ == '__main__':
class Color(Enum): RED, GREEN, BLUE ORANGE = "orange" CYAN = 10 MAGENTA YELLOW BLACK
import unittest
class TestEnum(unittest.TestCase):
EXPECTED_KEY_ORDER = ('RED', 'GREEN', 'BLUE', 'CYAN', 'MAGENTA', 'YELLOW', 'BLACK') EXPECTED_INT_VALUE_ORDER = (0, 1, 2, 10, 11, 12, 13) EXPECTED_ENUM_VALUE_ORDER = (Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA, Color.YELLOW, Color.BLACK) EXPECTED_ITEMS_ORDER = tuple(zip(EXPECTED_KEY_ORDER, EXPECTED_ENUM_VALUE_ORDER))
def test_type(self): self.assertIsInstance(Color.RED, int)
def test_class_enum_values(self): self.assertEqual(0, Color.RED) self.assertEqual(1, Color.GREEN) self.assertEqual(2, Color.BLUE) self.assertEqual(10, Color.CYAN) self.assertEqual(11, Color.MAGENTA) self.assertEqual(12, Color.YELLOW) self.assertEqual(13, Color.BLACK) self.assertEqual("orange", Color.ORANGE)
def test_instance_enum_values(self): e = Color() self.assertIs(Color.RED, e.RED) self.assertIs(Color.GREEN, e.GREEN) self.assertIs(Color.BLUE, e.BLUE) self.assertIs(Color.CYAN, e.CYAN) self.assertIs(Color.MAGENTA, e.MAGENTA) self.assertIs(Color.YELLOW, e.YELLOW) self.assertIs(Color.BLACK, e.BLACK) self.assertIs(Color.ORANGE, e.ORANGE)
def test_class_indexing(self): self.assertIs(Color.CYAN, Color['CYAN']) self.assertIs(Color.CYAN, Color[10])
def test_instance_indexing(self): e = Color() self.assertIs(Color.CYAN, e['CYAN']) self.assertIs(Color.CYAN, e[10])
def test_class_keys(self): self.assertEqual(self.EXPECTED_KEY_ORDER, tuple(Color.keys()))
def test_instance_keys(self): self.assertEqual(tuple(Color.keys()), tuple(Color().keys()))
def test_class_values(self): self.assertEqual(self.EXPECTED_INT_VALUE_ORDER, tuple(Color.values())) self.assertEqual(self.EXPECTED_ENUM_VALUE_ORDER, tuple(Color.values()))
def test_instance_values(self): self.assertEqual(tuple(Color.values()), tuple(Color().values()))
def test_class_items(self): self.assertEqual(self.EXPECTED_ITEMS_ORDER, tuple(Color.items()))
def test_instance_items(self): self.assertEqual(tuple(Color.items()), tuple(Color().items()))
def test_owner(self): for e in Color: self.assertIs(e.owner, Color)
def test_class_str(self): s = str(Color)
for e in Color: self.assertIn('%s:%d' % (e.key, int(e)), s)
def test_instance_str(self): self.assertEqual(str(Color), str(Color()))
def test_class_repr(self): r = repr(Color) self.assertIn(Color.__qualname__, r) self.assertIn(str(Color), r)
def test_instance_repr(self): e = Color() r = repr(e) self.assertIn(Color.__qualname__, r) self.assertIn('at 0x', r) self.assertIn(str(Color()), r)
def _create_duplicate_key(self): class DuplicateKey(Enum): KEY, KEY
def test_duplicate_key(self): self.assertRaises(AttributeError, self._create_duplicate_key)
def _create_duplicate_value(self): class DuplicateValue(Enum): KEY1, KEY2 = 0
def test_duplicate_value(self): self.assertRaises(AttributeError, self._create_duplicate_value)
def _assign_wrong_key(self): class WrongKey(Enum): KEY1 = EnumValue('KEY2', 0)
def test_wrong_key(self): self.assertRaises(AttributeError, self._assign_wrong_key)
def test_unnamed_key1(self): class UnnamedKey(Enum): KEY1 = EnumValue(None, 5)
self.assertEqual(UnnamedKey.KEY1, 5) self.assertIs(UnnamedKey, UnnamedKey.KEY1.owner)
def test_unnamed_key1(self): unnamed = EnumValue(None, 5)
class UnnamedKey(Enum): KEY1 = unnamed
self.assertEqual(UnnamedKey.KEY1, 5) self.assertIs(UnnamedKey.KEY1, unnamed) self.assertIs(UnnamedKey, UnnamedKey.KEY1.owner)
def _assign_wrong_owner(self): class WrongOwner(Enum): KEY1 = Color.RED
def test_wrong_owner(self): self.assertRaises(AttributeError, self._assign_wrong_owner)
unittest.main()
Tim Delaney
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
On Feb 01, 2013, at 03:18 PM, Tim Delaney wrote:
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int).
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :) -Barry
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Fri, Feb 1, 2013 at 7:36 AM, Barry Warsaw <barry@python.org> wrote:
On Feb 01, 2013, at 03:18 PM, Tim Delaney wrote:
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int).
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
I would actually prefer a place where it's easy to see the code, comment on it and fork it like Bitbucket or Github. PyPI is good for other purposes... Eli
![](https://secure.gravatar.com/avatar/2240a37aad5f5834a92809a5e5f01fe1.jpg?s=120&d=mm&r=g)
On 1 February 2013 17:30, Eli Bendersky <eliben@gmail.com> wrote:
On Fri, Feb 1, 2013 at 7:36 AM, Barry Warsaw <barry@python.org> wrote:
On Feb 01, 2013, at 03:18 PM, Tim Delaney wrote:
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int).
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
I would actually prefer a place where it's easy to see the code, comment on it and fork it like Bitbucket or Github. PyPI is good for other purposes...
Indeed - there are plenty of nice enums on pypi already - my call for getting then to the stdlib is that we can get a reliable enum/constant behavior to be always there - not only for the constants in the stdlib, but for projects for which adding external dependencies would be expensive (as they currently have none) js -><-
Eli
![](https://secure.gravatar.com/avatar/dd4761743695d5efd3692f2a3b35d37d.jpg?s=120&d=mm&r=g)
On Fri, Feb 1, 2013 at 12:30 PM, Eli Bendersky <eliben@gmail.com> wrote:
On Fri, Feb 1, 2013 at 7:36 AM, Barry Warsaw <barry@python.org> wrote:
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
I would actually prefer a place where it's easy to see the code, comment on it and fork it like Bitbucket or Github. PyPI is good for other purposes...
+1 And put it on PyPI to make it more accessible. -eric
![](https://secure.gravatar.com/avatar/2240a37aad5f5834a92809a5e5f01fe1.jpg?s=120&d=mm&r=g)
On 1 February 2013 18:09, Eric Snow <ericsnowcurrently@gmail.com> wrote:
On Fri, Feb 1, 2013 at 7:36 AM, Barry Warsaw <barry@python.org> wrote:
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
I would actually prefer a place where it's easy to see the code, comment on it and fork it like Bitbucket or Github. PyPI is good for other
On Fri, Feb 1, 2013 at 12:30 PM, Eli Bendersky <eliben@gmail.com> wrote: purposes...
+1
And put it on PyPI to make it more accessible.
As soon as there are some more goodies in there beyond the enumerator and constants themselves. I can see that a namedtuple - like utility function could be interesting to create the enums - and a way to import the generated constatnts to the current global namespace that does not violate good pratices. I think that something like: class MyEnum(Enum): RED, GREEN, BLUE load_constants(MyEnum, globals() ) is a good solution that could respect both DRY and "explicit is better than implicit" and "Special cases aren't special enough to break the rules." js -><-
-eric _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
![](https://secure.gravatar.com/avatar/de311342220232e618cb27c9936ab9bf.jpg?s=120&d=mm&r=g)
On 02/01/2013 04:53 PM, Greg Ewing wrote:
Joao S. O. Bueno wrote:
class MyEnum(Enum): RED, GREEN, BLUE
load_constants(MyEnum, globals() )
The "obvious" way to spell this would be
from MyEnum import *
but it would be challenging to make that work, I suspect. :-(
It's not too tough: 8<---- constants.py -------------------------------------------------- class Colors(object): BLACK = 0 RED = 1 GREEN = 2 BLUE = 3 __all__ = ('BLACK','RED','GREEN','BLUE') @classmethod def register(cls): import sys sys.modules['%s.%s' % (__name__, cls.__name__)] = cls() Colors.register() 8<---- constants.py -------------------------------------------------- --> from constants.Colors import * --> RED 1 --> BLUE 3 ~Ethan~
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Ethan Furman wrote:
On 02/01/2013 04:53 PM, Greg Ewing wrote:
The "obvious" way to spell this would be
from MyEnum import *
but it would be challenging to make that work, I suspect. :-(
It's not too tough:
Yeah, I just took up my own challenge and came up with something similar (apologies for the Python 2): class MetaEnum(type): def __init__(self, name, bases, dict): type.__init__(self, name, bases, dict) import sys sys.modules[name] = self class Enum(object): __metaclass__ = MetaEnum class MyEnum(Enum): RED = 0 GREEN = 1 BLUE = 2 from MyEnum import * print RED print GREEN print BLUE I left off the module name so that you don't have to qualify the import. A more general version would qualify it with all but the last component of the module name, so you can import it relative to the containing module. -- Greg
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
On Feb 01, 2013, at 11:30 AM, Eli Bendersky wrote:
I would actually prefer a place where it's easy to see the code, comment on it and fork it like Bitbucket or Github. PyPI is good for other purposes...
Well, sure, having the code in a publicly available vcs is always a good idea. My point really was that stuff ordinarily doesn't land in the stdlib until it's lived on PyPI for a while. -Barry
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 2 February 2013 02:36, Barry Warsaw <barry@python.org> wrote:
On Feb 01, 2013, at 03:18 PM, Tim Delaney wrote:
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int).
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
Yep - I intend to. Was just hacking away initially to see if what I wanted to achieve was feasible using Michael's metaclass as a base. It's in a Mercurial repo so I'll put it up on BitBucket in the next day or two and look at cleaning it up (documentation!) to put on PyPI. Tim Delaney
![](https://secure.gravatar.com/avatar/2240a37aad5f5834a92809a5e5f01fe1.jpg?s=120&d=mm&r=g)
On 1 February 2013 18:28, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
On 2 February 2013 02:36, Barry Warsaw <barry@python.org> wrote:
On Feb 01, 2013, at 03:18 PM, Tim Delaney wrote:
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int).
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
Yep - I intend to. Was just hacking away initially to see if what I wanted to achieve was feasible using Michael's metaclass as a base. It's in a Mercurial repo so I'll put it up on BitBucket in the next day or two and look at cleaning it up (documentation!) to put on PyPI.
Indeed . .as I said, we'd better put together a pre-PEP and making this proof of concept impement most of it before going to Pypy - There are other requisites that I think aren't taken care of that come form the 2010 thread - for example, pickle-ability. js -><-
Tim Delaney
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 2 February 2013 07:28, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
On 2 February 2013 02:36, Barry Warsaw <barry@python.org> wrote:
On Feb 01, 2013, at 03:18 PM, Tim Delaney wrote:
Last version (for now). I'm really interested in people's opinions on this. For this version I've taken some inspiration from flufl.enum (but there remains the major difference that these enums subclass int).
Why not package it up and put it in PyPI? Better there than sitting in an email thread of some mailing list full of crazy people. :)
Yep - I intend to. Was just hacking away initially to see if what I wanted to achieve was feasible using Michael's metaclass as a base. It's in a Mercurial repo so I'll put it up on BitBucket in the next day or two and look at cleaning it up (documentation!) to put on PyPI.
Public repository on BitBucket: https://bitbucket.org/magao/enum Feel free to raise issues there, clone and make pull requests, etc. Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 4 February 2013 08:39, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
Public repository on BitBucket: https://bitbucket.org/magao/enum
Feel free to raise issues there, clone and make pull requests, etc.
As Eli has noted in the issues, no comments or anything except the unit tests yet. Lots of magic. Needs significant cleanup. And I'll note again in case it's not clear - this currently *only* works with Python 3.3. Tim Delaney
![](https://secure.gravatar.com/avatar/345fd2a0955fbab272da7ee594cbe7c1.jpg?s=120&d=mm&r=g)
Hi, about this enum/const thing, The use case I like more is a class where you know all the instances and not just a sequence of names. Particularly It would be nice to have custom attributes and methods besides the value and the name. I have my own implementation with a basic api somewhat borrowed from flufl.enum (plus a lot of other stuff), but with this kind of support: https://github.com/jbvsmo/makeobj I couldn't find the best way to express enums with the current python syntax, so I also wrote a simple regex-parsed language to fit objects with an arbitrary level of complexity. I think, enumeration per se is not much more useful than just a bunch of integers... Having this kind of control IMO is. Although Java is not a good example of anything, they have a similar feature. What do you people think? João Bernardo 2013/2/3 Tim Delaney <timothy.c.delaney@gmail.com>
On 4 February 2013 08:39, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
Public repository on BitBucket: https://bitbucket.org/magao/enum
Feel free to raise issues there, clone and make pull requests, etc.
As Eli has noted in the issues, no comments or anything except the unit tests yet. Lots of magic. Needs significant cleanup.
And I'll note again in case it's not clear - this currently *only* works with Python 3.3.
Tim Delaney
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 4 February 2013 10:53, João Bernardo <jbvsmo@gmail.com> wrote:
Hi, about this enum/const thing, The use case I like more is a class where you know all the instances and not just a sequence of names. Particularly It would be nice to have custom attributes and methods besides the value and the name.
I have my own implementation with a basic api somewhat borrowed from flufl.enum (plus a lot of other stuff), but with this kind of support: https://github.com/jbvsmo/makeobj
I considered it, and in fact you could almost do it with my implementation by using a custom subclass of EnumValue (except trying it has just exposed a design flaw with the whole _EnumProxy bit). Works if you create the enum in the same module as EnumValues, fails otherwise. Going to have to have a rethink. Tim Delaney
![](https://secure.gravatar.com/avatar/345fd2a0955fbab272da7ee594cbe7c1.jpg?s=120&d=mm&r=g)
2013/2/3 Tim Delaney <timothy.c.delaney@gmail.com>
On 4 February 2013 10:53, João Bernardo <jbvsmo@gmail.com> wrote:
Hi, about this enum/const thing, The use case I like more is a class where you know all the instances and not just a sequence of names. Particularly It would be nice to have custom attributes and methods besides the value and the name.
I have my own implementation with a basic api somewhat borrowed from flufl.enum (plus a lot of other stuff), but with this kind of support: https://github.com/jbvsmo/makeobj
I considered it, and in fact you could almost do it with my implementation by using a custom subclass of EnumValue (except trying it has just exposed a design flaw with the whole _EnumProxy bit). Works if you create the enum in the same module as EnumValues, fails otherwise. Going to have to have a rethink.
For attributes, it would probably be easy to do, but for methods you will probably need a new _EnumProxy subclass for each class. I did this with a metaclass factory. João Bernardo
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 4 February 2013 11:17, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
On 4 February 2013 10:53, João Bernardo <jbvsmo@gmail.com> wrote:
Hi, about this enum/const thing, The use case I like more is a class where you know all the instances and not just a sequence of names. Particularly It would be nice to have custom attributes and methods besides the value and the name.
I have my own implementation with a basic api somewhat borrowed from flufl.enum (plus a lot of other stuff), but with this kind of support: https://github.com/jbvsmo/makeobj
I considered it, and in fact you could almost do it with my implementation by using a custom subclass of EnumValue (except trying it has just exposed a design flaw with the whole _EnumProxy bit). Works if you create the enum in the same module as EnumValues, fails otherwise. Going to have to have a rethink.
Fixed the _EnumProxy issue (but it's a kludge - I've used sys._getframe() - there's probably a better way). I've also made it so that you can override the metaclass to return a subclass of EnumValue. Now you can do something like: Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum, EnumValue, EnumMeta
class MyEnumValue1(EnumValue): ... pass ... class MyEnumMeta1(EnumMeta): ... @classmethod ... def _create_value(cls, key, value): ... return MyEnumValue1(key, value) ... class MyEnum1(Enum, metaclass=MyEnumMeta1): ... VALUE1, ... VALUE2 ... class MyEnumValue2(EnumValue): ... pass ... class MyEnumMeta2(MyEnumMeta1): ... @classmethod ... def _create_value(cls, key, value): ... return MyEnumValue2(key, value) ... class MyEnum2(MyEnum1, metaclass=MyEnumMeta2): ... VALUE3, ... VALUE4 ... print(repr(MyEnum1)) <enum '__main__.MyEnum1' {<MyEnumValue1 'MyEnum1.VALUE1': 0>, <MyEnumValue1 'MyEnum1.VALUE2': 1>}> print(repr(MyEnum2)) <enum '__main__.MyEnum2' {<MyEnumValue1 'MyEnum1.VALUE1': 0>, <MyEnumValue1 'MyEnum1.VALUE2': 1>, <MyEnumValue2 'MyEnum2.VALUE3': 2>, <MyEnumValue2 'MyEnum2.VALUE4': 3>}>
Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 11 February 2013 09:43, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
I've also made it so that you can override the metaclass to return a subclass of EnumValue.
Expanded on this a bit to simplify things. Now to use a different type you can just go: Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum, EnumValue
class MyEnumValue1(EnumValue): ... pass ... class MyEnum1(Enum, metaclass=Enum.subtype(MyEnumValue1)): ... VALUE1, ... VALUE2 ... class MyEnumValue2(EnumValue): ... pass ... class MyEnum2(MyEnum1, metaclass=MyEnum1.subtype(MyEnumValue2)): ... VALUE3, ... VALUE4 ... print(repr(MyEnum1)) <enum __main__.MyEnum1 {<MyEnumValue1 'VALUE1': 0>, <MyEnumValue1 'VALUE2': 1>}> print(repr(MyEnum2)) <enum __main__.MyEnum2 {<MyEnumValue1 'VALUE1': 0>, <MyEnumValue1 'VALUE2': 1>, <MyEnumValue2 'VALUE3': 2>, <MyEnumValue2 'VALUE4': 3>}>
The parameter passed to subtype() can be any callable that takes 2 parameters (key, value) and returns an instance of EnumValue. Unfortunately, I can't see any way to avoid specifying the base class twice (once as the base class, once for the metaclass=). Similarly, passing the 'value_type' parameter to Enum.make() creates an appropriate metaclass from the base class: Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum, EnumValue class MyEnumValue1(EnumValue): ... pass ... MyEnum1 = Enum.make('MyEnum1', ('VALUE1', 'VALUE2'), value_type=MyEnumValue1) print(repr(MyEnum1)) <enum __main__.MyEnum1 {<MyEnumValue1 'VALUE1': 0>, <MyEnumValue1 'VALUE2': 1>}>
Whilst the original "use a different metaclass to produce values of a different type" was possibly feature-creep, these modifications at least make it pretty painless to use. Overall, I'm pretty happy with where this is now, except for the use of sys._getframe(). Tim Delaney
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Sun, Feb 10, 2013 at 2:43 PM, Tim Delaney <timothy.c.delaney@gmail.com>wrote:
On 4 February 2013 11:17, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
On 4 February 2013 10:53, João Bernardo <jbvsmo@gmail.com> wrote:
Hi, about this enum/const thing, The use case I like more is a class where you know all the instances and not just a sequence of names. Particularly It would be nice to have custom attributes and methods besides the value and the name.
I have my own implementation with a basic api somewhat borrowed from flufl.enum (plus a lot of other stuff), but with this kind of support: https://github.com/jbvsmo/makeobj
I considered it, and in fact you could almost do it with my implementation by using a custom subclass of EnumValue (except trying it has just exposed a design flaw with the whole _EnumProxy bit). Works if you create the enum in the same module as EnumValues, fails otherwise. Going to have to have a rethink.
Fixed the _EnumProxy issue (but it's a kludge - I've used sys._getframe() - there's probably a better way). I've also made it so that you can override the metaclass to return a subclass of EnumValue.
Now you can do something like:
Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum, EnumValue, EnumMeta
class MyEnumValue1(EnumValue): ... pass ... class MyEnumMeta1(EnumMeta): ... @classmethod ... def _create_value(cls, key, value): ... return MyEnumValue1(key, value) ... class MyEnum1(Enum, metaclass=MyEnumMeta1): ... VALUE1, ... VALUE2 ... class MyEnumValue2(EnumValue): ... pass ... class MyEnumMeta2(MyEnumMeta1): ... @classmethod ... def _create_value(cls, key, value): ... return MyEnumValue2(key, value) ... class MyEnum2(MyEnum1, metaclass=MyEnumMeta2): ... VALUE3, ... VALUE4 ... print(repr(MyEnum1)) <enum '__main__.MyEnum1' {<MyEnumValue1 'MyEnum1.VALUE1': 0>, <MyEnumValue1 'MyEnum1.VALUE2': 1>}> print(repr(MyEnum2)) <enum '__main__.MyEnum2' {<MyEnumValue1 'MyEnum1.VALUE1': 0>, <MyEnumValue1 'MyEnum1.VALUE2': 1>, <MyEnumValue2 'MyEnum2.VALUE3': 2>, <MyEnumValue2 'MyEnum2.VALUE4': 3>}>
Can you elaborate on the utility of this feature? What realistic use cases do you see for it? I think that at this point it's important to weigh all benefits of features vs. implementation complexity, and there's absolutely no need to support every feature every other enum implementation has. I want to stress again that the most important characteristic of your implementation is the clean syntax which means that enums are so easy to define they don't really need special Python syntax and a library feature can do. However, there's a big leap from this to defining custom metaclasses for enums. Eli
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 12 February 2013 00:28, Eli Bendersky <eliben@gmail.com> wrote:
On Sun, Feb 10, 2013 at 2:43 PM, Tim Delaney <timothy.c.delaney@gmail.com>wrote:
On 4 February 2013 11:17, Tim Delaney <timothy.c.delaney@gmail.com>wrote:
On 4 February 2013 10:53, João Bernardo <jbvsmo@gmail.com> wrote:
Particularly It would be nice to have custom attributes and methods besides the value and the name.
I've also made it so that you can override the metaclass to return a subclass of EnumValue.
Can you elaborate on the utility of this feature? What realistic use cases do you see for it? I think that at this point it's important to weigh all benefits of features vs. implementation complexity, and there's absolutely no need to support every feature every other enum implementation has. I want to stress again that the most important characteristic of your implementation is the clean syntax which means that enums are so easy to define they don't really need special Python syntax and a library feature can do. However, there's a big leap from this to defining custom metaclasses for enums.
The custom metaclass is purely a mechanism to make it easy to use a custom class for the enum value. It wold be possible to manually assign a custom enum type to each enum, but then you wold lose the ability to just define the names. By using a custom metaclass, you can have it automatically assign the enum value type that you want. My next email specifies a simplified syntax for specifying the custom metaclass (you don't need to create one at all). Supporting this functionality was actually very simple. However, I am wondering though how useful this is without being able to specify additional parameters for the enums. I've often used enums with quite complex behaviour (e.g. in java) - the enum part is purely to have a unique value assigned to each instance of the class and not repeating myself for no good reason. I couldn't quite do the same with this as it currently is - whilst I can have whatever behaviour I want, there's nothing to key it off except the name and value which probably isn't enough. I've been trying to think of a syntax which would work to pass additional parameters and I can't think of anything cleaner than having a specialised class to pass the additional parameters - but I need to somehow be able to specify the optional integer value of the enum. Maybe have the first parameter be None? class EnumParams(): def __init__(self, value=None, *p, **kw): self.value = value .... def _resolve_proxies(self): # names would have been affected by EnumValues.__getattr__ - need to resolve them from _EnumProxy to the actual values ... class MyEnumValue(EnumValue): def __new__(cls, key, value, *p): e = super().__new__(cls, key, value) e.p = p def dump(self): print(self.p) class MyEnum(Enum, metaclass=Enum.subtype(MyEnumValue)): A = EnumParams(None, 'extra', 'params') B = EnumParams(3, 'more', 'params') Thoughts? Is the extra complexity worth it? The thing is, this doesn't take away from the ability to specify the very simple clean enums - but it would give the enums pretty much the full capabilities of java enums. I'd like to have all these features available so that any PEP could reference them and discuss the pros and cons (including how well they work in practice). Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
And I've just realised that my enums will be broken when assigning a literal containing a name lookup e.g. v = 1 class MyEnum(Enum): A, B other_attr = (v,) other_attr will have an _EnumProxy instance. But I think I can do away with the _EnumProxy entirely now that I'm using sys._getframe(). Let me try something. Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 12 February 2013 07:11, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
And I've just realised that my enums will be broken when assigning a literal containing a name lookup e.g.
v = 1
class MyEnum(Enum): A, B other_attr = (v,)
other_attr will have an _EnumProxy instance. But I think I can do away with the _EnumProxy entirely now that I'm using sys._getframe(). Let me try something.
Too early in the morning. The above would avoid the whole _EnumProxy because 'v' would be successfully looked up in the global namespace. But I may still get rid of some instances of _EnumProxy. Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 12 February 2013 07:09, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
Supporting this functionality was actually very simple. However, I am wondering though how useful this is without being able to specify additional parameters for the enums. I've often used enums with quite complex behaviour (e.g. in java) - the enum part is purely to have a unique value assigned to each instance of the class and not repeating myself for no good reason. I couldn't quite do the same with this as it currently is - whilst I can have whatever behaviour I want, there's nothing to key it off except the name and value which probably isn't enough. I've been trying to think of a syntax which would work to pass additional parameters and I can't think of anything cleaner than having a specialised class to pass the additional parameters - but I need to somehow be able to specify the optional integer value of the enum. Maybe have the first parameter be None?
OK - I've implemented this. EnumValue has grown 'args' and 'kwargs' attributes. Enums can be constructed like: Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum, EnumParams, _
class MyEnum(Enum): ... A, B ... C = EnumParams(None, 'i', 'j', k='k', l='l') ... D = EnumParams(10, 'm', n='n') ... E ... F = EnumParams(None, 'o') ... G = 20 ... H, I ... J = EnumParams(None, p='p') ... print(repr(MyEnum)) <enum __main__.MyEnum {<EnumValue 'A': 0>, <EnumValue 'B': 1>, <EnumValue 'C': 2>, <EnumValue 'D': 10>, <EnumValue 'E': 11>, <EnumValue 'F': 12>, <EnumValue 'G': 20>, <EnumValue 'H': 21>, <EnumValue 'I': 22>, <EnumValue 'J': 23>}>
_ is aliased to EnumParams so you can do:
class MyEnum(Enum):... A, B ... C = _(None, 'i', 'j', k='k', l='l') ... D = _(10, 'm', n='n') ... E ... F = _(None, 'o') ... G = 20 ... H, I ... J = _(None, p='p') ... print(repr(MyEnum)) <enum __main__.MyEnum {<EnumValue 'A': 0>, <EnumValue 'B': 1>, <EnumValue 'C': 2>, <EnumValue 'D': 10>, <EnumValue 'E': 11>, <EnumValue 'F': 12>, <EnumValue 'G': 20>, <EnumValue 'H': 21>, <EnumValue 'I': 22>, <EnumValue 'J': 23>}>
which looks a bit cleaner to me, but might be a little confusing. Thoughts? Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
I'm evil. I just had a very bad thought, and I think I kinda like it. Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum, _
class Color(Enum): ... RED ... GREEN = _(None, 'green') ... BLUE = _(None, 'blue', hex='0000FF') ... def __init__(self): ... print('__init__', repr(self), self.args, self.kwargs) ... def dump(self): ... print(self, self.args, self.kwargs) ... __init__ <EnumValue 'Color.RED': 0> () {} __init__ <EnumValue 'Color.GREEN': 1> ('green',) {} __init__ <EnumValue 'Color.BLUE': 2> ('blue',) {'hex': '0000FF'}
print(repr(Color)) <enum __main__.Color {<EnumValue 'RED': 0>, <EnumValue 'GREEN': 1>, <EnumValue 'BLUE': 2>}>
for e in Color: ... e.dump() ... Color(e).dump() ... Color.RED () {} Color.RED () {} Color.GREEN ('green',) {} Color.GREEN ('green',) {} Color.BLUE ('blue',) {'hex': '0000FF'} Color.BLUE ('blue',) {'hex': '0000FF'}
When you request an attribute on the EnumValue that doesn't exist, it gets it from the owning Enum class. If the attribute is an instance method (or static method - they have the same type) then it gets turned into an instance method of the EnumValue. Also, when the EnumValue becomes owned by Enum, Enum.__init__ is called as an EnumValue instance method (with no parameters). This actually makes a kind of perverse sense. Conceptually the actual EnumValues are the instances of the Enum (and in fact, I've made them pretend to actually be so). I think it also entirely does away with the need for subclasses of EnumValue and EnumMeta - you can do everything by specifying attributes and behaviour in the Enum instance methods. I've pushed this to https://bitbucket.org/magao/enum - have a play and see how evil you think this is. Tim Delaney
![](https://secure.gravatar.com/avatar/d67ab5d94c2fed8ab6b727b62dc1b213.jpg?s=120&d=mm&r=g)
On Tue, Feb 12, 2013 at 7:06 PM, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
This actually makes a kind of perverse sense. Conceptually the actual EnumValues are the instances of the Enum (and in fact, I've made them pretend to actually be so).
Not so perverse. I think that makes very good sense (but then, I know C++ enums, so maybe I'm tainted). Looks good, though I've not actually played with the code. ChrisA
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 13 February 2013 08:36, Chris Angelico <rosuav@gmail.com> wrote:
On Tue, Feb 12, 2013 at 7:06 PM, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
This actually makes a kind of perverse sense. Conceptually the actual EnumValues are the instances of the Enum (and in fact, I've made them pretend to actually be so).
Not so perverse. I think that makes very good sense (but then, I know C++ enums, so maybe I'm tainted). Looks good, though I've not actually played with the code.
And more ... Firstly, handles more edge cases that might have been mis-identified as enum values. EnumParams is now redundant (and will probably be removed), replaced by calling the (undefined) enum value name. Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum class MyEnum(Enum): ... A, B ... C(None, 'c1', c2='2') ... D(10, 'd') ... E(None, e='e') ... repr(MyEnum) "<enum __main__.MyEnum {<EnumValue 'A': 0>, <EnumValue 'B': 1>, <EnumValue 'C': 2>, <EnumValue 'D': 10>, <EnumValue 'E': 11>}>" str(MyEnum) '{A:0, B:1, C:2, D:10, E:11}' MyEnum.A.args, MyEnum.A.kwargs ((), {}) MyEnum.C.args, MyEnum.C.kwargs (('c1',), {'c2': '2'})
Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 13 February 2013 12:51, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
And more ...
And inspired by the other thread, Ellipsis (...) is now used to indicate "use the next value". Ellipsis can either be assigned to an enum value, or passed as the first parameter to EnumParams or when calling the enum value during construction. None is no longer valid. Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
from enum import Enum class MyEnum(Enum): ... A ... B, C, D = ..., 5, ... ... E = ... ... F(..., 'f') ... MyEnum <enum __main__.MyEnum {<EnumValue 'A': 0>, <EnumValue 'B': 1>, <EnumValue 'C': 5>, <EnumValue 'D': 6>, <EnumValue 'E': 7>, <EnumValue 'F': 8>}>
I'm stopping now. Tim Delaney
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 13 February 2013 02:42, Barry Warsaw <barry@python.org> wrote:
On Feb 12, 2013, at 05:49 PM, Tim Delaney wrote:
_ is aliased to EnumParams so you can do:
_() is very commonly used for gettext translations, by long established convention. This is convention is inherited by Python from GNU gettext.
Good point - it's actually what inspired me to use it, but a bad idea for me to do so. I've taken that out. Tim Delaney
![](https://secure.gravatar.com/avatar/60cac87fb9e2b5689242622999656cb0.jpg?s=120&d=mm&r=g)
On Feb 11, 2013, at 5:28 AM, Eli Bendersky <eliben@gmail.com> wrote:
Can you elaborate on the utility of this feature? What realistic use cases do you see for it? I think that at this point it's important to weigh all benefits of features vs. implementation complexity, and there's absolutely no need to support every feature every other enum implementation has. I want to stress again that the most important characteristic of your implementation is the clean syntax which means that enums are so easy to define they don't really need special Python syntax and a library feature can do. However, there's a big leap from this to defining custom metaclasses for enums.
Well said. I agree with you critique. In the absence of compelling use cases, the language is better-off without a complicated new feature. Raymond
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 13 February 2013 14:25, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Feb 11, 2013, at 5:28 AM, Eli Bendersky <eliben@gmail.com> wrote:
Can you elaborate on the utility of this feature? What realistic use cases do you see for it? I think that at this point it's important to weigh all benefits of features vs. implementation complexity, and there's absolutely no need to support every feature every other enum implementation has. I want to stress again that the most important characteristic of your implementation is the clean syntax which means that enums are so easy to define they don't really need special Python syntax and a library feature can do. However, there's a big leap from this to defining custom metaclasses for enums.
Well said. I agree with you critique. In the absence of compelling use cases, the language is better-off without a complicated new feature.
Absolutely. At the moment my implementation has everything in there as I've been incrementally finding syntactically cleaner ways of doing things. I intend to remove a lot of the extra functionality eventually - for example, supporting EnumMeta/EnumValue subclassing. In any case, it looks like Guido is strongly heading towards flufl.enum - doesn't mean I won't keep working on my implementation, but it doesn't look like it will be the basis for a stdlib enum. Tim Delaney
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Sun, Feb 3, 2013 at 3:53 PM, João Bernardo <jbvsmo@gmail.com> wrote:
Hi, about this enum/const thing, The use case I like more is a class where you know all the instances and not just a sequence of names. Particularly It would be nice to have custom attributes and methods besides the value and the name.
I have my own implementation with a basic api somewhat borrowed from flufl.enum (plus a lot of other stuff), but with this kind of support: https://github.com/jbvsmo/makeobj
I couldn't find the best way to express enums with the current python syntax, so I also wrote a simple regex-parsed language to fit objects with an arbitrary level of complexity. I think, enumeration per se is not much more useful than just a bunch of integers... Having this kind of control IMO is.
Although Java is not a good example of anything, they have a similar feature. What do you people think?
Personally, I disagree with the "more features is better" approach. Features have a cost - they complicate the implementation which makes it fragile, harder to maintain and harder to understand. Even more importantly, they make *user* code harder to understand. Therefore it's IMHO best to decide on a basic functionality that brings most of the benefits, and then think about how to make the implementation *simpler*. All I really want from an enum is what I have in C and lack in Python - a nice, minimally type-safe way to name special constants. Having this in hand, I want the simplest and cleanest syntax possible, not more features. Tim's implementation strikes a good balance - the syntax is as minimal as can be in a library implementation, and it provides all the basic features well. It adds some more, possibly at the cost of complexity, which may or may not be good. This is why I think that a PEP weighing features for/against inclusion is a logical next step. Eli
![](https://secure.gravatar.com/avatar/db5f70d2f2520ef725839f046bdc32fb.jpg?s=120&d=mm&r=g)
Le Wed, 30 Jan 2013 15:22:06 +0000, Michael Foord <fuzzyman@gmail.com> a écrit :
On 30 January 2013 07:26, Antoine Pitrou <solipsis@pitrou.net> wrote:
On Wed, 30 Jan 2013 17:58:37 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that:
class Color(Enum): RED, WHITE, BLUE = range(3)
With a Python 3 metaclass that provides default values for *looked up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
This relies on tuple evaluation order, and would also evaluate any other symbol looked up from inside the class body (which means I cannot add anything else than enum symbols to the class). In other words, I'm afraid it would be somewhat fragile ;) Regards Antoine.
![](https://secure.gravatar.com/avatar/133b712423066c858d82f5d285a6867a.jpg?s=120&d=mm&r=g)
On 30 January 2013 16:26, Antoine Pitrou <solipsis@pitrou.net> wrote:
Le Wed, 30 Jan 2013 15:22:06 +0000, Michael Foord <fuzzyman@gmail.com> a écrit :
On 30 January 2013 07:26, Antoine Pitrou <solipsis@pitrou.net> wrote:
On Wed, 30 Jan 2013 17:58:37 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that:
class Color(Enum): RED, WHITE, BLUE = range(3)
With a Python 3 metaclass that provides default values for *looked up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
This relies on tuple evaluation order,
It does if you do them as a tuple.
and would also evaluate any other symbol looked up from inside the class body
Only if they aren't actually defined.
(which means I cannot add anything else than enum symbols to the class).
So not true - it is only *undefined* symbols that are added as enum values.
In other words, I'm afraid it would be somewhat fragile ;)
Well, within specific parameters... Michael
Regards
Antoine.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
![](https://secure.gravatar.com/avatar/de311342220232e618cb27c9936ab9bf.jpg?s=120&d=mm&r=g)
On 01/30/2013 08:26 AM, Antoine Pitrou wrote:
Le Wed, Michael Foord a écrit :
With a Python 3 metaclass that provides default values for *looked up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
This relies on tuple evaluation order, and would also evaluate any other symbol looked up from inside the class body (which means I cannot add anything else than enum symbols to the class).
Probably a dumb question, but why would you want to add non-enum to an enum class? ~Ethan~
![](https://secure.gravatar.com/avatar/0e6cf1b640884b3bd0378810757c1bb0.jpg?s=120&d=mm&r=g)
On Wed, Jan 30, 2013 at 9:19 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Probably a dumb question, but why would you want to add non-enum to an enum class?
class Color(Enum): RED, WHITE, BLUE def translate(language): """Get the name of an enum in the specified language.""" pass --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com
![](https://secure.gravatar.com/avatar/2fc5b058e338d06a8d8f8cd0cfe48376.jpg?s=120&d=mm&r=g)
Am 30.01.2013 17:26, schrieb Antoine Pitrou:
With a Python 3 metaclass that provides default values for *looked up* entries you could have this:
class Color(Enum): RED, WHITE, BLUE
This relies on tuple evaluation order, and would also evaluate any other symbol looked up from inside the class body (which means I cannot add anything else than enum symbols to the class).
In other words, I'm afraid it would be somewhat fragile ;)
And it breaks static code checkers like pyflakes. Georg
![](https://secure.gravatar.com/avatar/d6b9415353e04ffa6de5a8f3aaea0553.jpg?s=120&d=mm&r=g)
On 1/30/2013 2:26 AM, Antoine Pitrou wrote:
On Wed, 30 Jan 2013 17:58:37 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
class color(enum): RED = value() WHITE = value() BLUE = value()
We could do somewhat better than that:
class Color(Enum): RED, WHITE, BLUE = range(3)
However, it's still slightly annoying that you have to specify how many values there are in the range() call.
For small enumerations, not much of a problem. Or, if one does not want to take the time to count, allow RED, WHITE, BLUE, _extras = range(12) # any number >= n and have a metaclass delete _extras.
Well, how about:
class Color(Enum): values = ('RED', 'WHITE', 'BLUE') ? (replace values with __values__ if you prefer)
I had the same idea, and having never written a metaclass that I can remember, decided to try it. class EnumMeta(type): def __new__(cls, name, bases, dic): for i, name in enumerate(dic['_values']): dic[name] = i del dic['_values'] return type.__new__(cls, name, bases, dic) class Enum(metaclass=EnumMeta): _values = () class Color(Enum): _values = 'RED', 'GREEN', 'BLUE' print(Color.RED, Color.GREEN, Color.BLUE)
0 1 2
So this syntax is at least feasible -- today. -- Terry Jan Reedy
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
Hm, if people really want to write something like
color = enum(RED, WHITE, BLUE)
that might still be true, but given that it's likely going to look a little more like a class definition, this doesn't look so bad, and certainly doesn't violate DRY (though it's somewhat verbose):
class color(enum): RED = value() WHITE = value() BLUE = value()
The Python 3 metaclass can observe the order in which the values are defined easily by setting the class dict to an OrderdDict.
Even though I agree that enums lend themselves nicely to "class"-y syntax, the example you provide shows exactly why sticking to existing syntax makes use bend over backwards. Because 'color' is really not a class. And I don't want to explicitly say it's both a class and it subclasses something called 'enum'. And I don't want to specify values when I don't need values. All I really want is: enum color: RED WHITE BLUE Or shorter: enum color: RED, WHITE, BLUE Would adding a new "enum" keyword in Python 3.4 *really* meet that much resistance? ISTM built-in, standard, enums have been on the wishlist of Python developers for a long time. Eli
![](https://secure.gravatar.com/avatar/8b97b5aad24c30e4a1357b38cc39aeaa.jpg?s=120&d=mm&r=g)
Eli Bendersky, 30.01.2013 06:26:
enum color: RED, WHITE, BLUE
Would adding a new "enum" keyword in Python 3.4 *really* meet that much resistance? ISTM built-in, standard, enums have been on the wishlist of Python developers for a long time.
Special cases aren't special enough to break the rules (or even existing code!). Stefan
![](https://secure.gravatar.com/avatar/d91ce240d2445584e295b5406d12df70.jpg?s=120&d=mm&r=g)
On Tue, Jan 29, 2013 at 6:50 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
FWIW, since that last discussion, I've switched to using strings for my special constants, dumping them in a container if I need some kind of easy validity checking or iteration.
Unfortunately, some of the problems with that involve unicode normalization, and won't show up in English. Python has defined a normalization for identifiers; this normalization does not apply to quoted strings. Essentially, this is the same problem string exceptions caused, except that it (sometimes) applies to '==' as well as to 'is'. Essentially, we want the simplicity of: color=enum(red, green, blue) except that we *also* want to able to compare the symbols to (int or str) constants, and to decide when they will be equal. I don't see any good way to support: color=enum(red=15, green, blue) without requiring either that strings be used instead of symbols, or that later entries be explicitly initialized. -jJ
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
On Jan 28, 2013, at 11:50 PM, Joao S. O. Bueno wrote:
And it was not dismissed at all - to the contrary the last e-mail in the thread is a message from the BDLF for it to **be** ! The discussion happened in a bad moment as Python was mostly freature froozen for 3.2 - and it did not show up again for Python 3.3;
I still offer up my own enum implementation, which I've used and has been available for years on PyPI, and hasn't had a new release in months because it hasn't needed one. :) It should be compatible with Pythons from 2.6 to 3.3. http://pypi.python.org/pypi/flufl.enum The one hang up about it the last time this came up was that my enum items are not ints and Guido though they should be. I actually tried at one point to make that so, but had some troublesome test failures that I didn't have time or motivation to fix, mostly because I don't particularly like those semantics. I don't remember the details. However, if someone *else* wanted to submit a branch/patch to have enum items inherit from ints, and that was all it took to have these adopted into the stdlib, I would be happy to take a look. Cheers, -Barry
![](https://secure.gravatar.com/avatar/133b712423066c858d82f5d285a6867a.jpg?s=120&d=mm&r=g)
On 30 January 2013 01:27, Barry Warsaw <barry@python.org> wrote:
On Jan 28, 2013, at 11:50 PM, Joao S. O. Bueno wrote:
And it was not dismissed at all - to the contrary the last e-mail in the thread is a message from the BDLF for it to **be** ! The discussion happened in a bad moment as Python was mostly freature froozen for 3.2 - and it did not show up again for Python 3.3;
I still offer up my own enum implementation, which I've used and has been available for years on PyPI, and hasn't had a new release in months because it hasn't needed one. :) It should be compatible with Pythons from 2.6 to 3.3.
http://pypi.python.org/pypi/flufl.enum
The one hang up about it the last time this came up was that my enum items are not ints and Guido though they should be. I actually tried at one point to make that so, but had some troublesome test failures that I didn't have time or motivation to fix, mostly because I don't particularly like those semantics. I don't remember the details.
However, if someone *else* wanted to submit a branch/patch to have enum items inherit from ints, and that was all it took to have these adopted into the stdlib, I would be happy to take a look.
Being an int subclass (and possibly optionally a strs subclass) is a requirement if any adopted Enum is to be used *within* the standard library in places where integers are currently used as "poor man's enums". I also don't *think* flufl.enum supports flag enums (ones that can be OR'd together), right? Michael
Cheers, -Barry
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
On Jan 30, 2013, at 03:16 PM, Michael Foord wrote:
Being an int subclass (and possibly optionally a strs subclass) is a requirement if any adopted Enum is to be used *within* the standard library in places where integers are currently used as "poor man's enums". I also don't *think* flufl.enum supports flag enums (ones that can be OR'd together), right?
Sure, it does because you have to be explicit about the enum int value to assign the item. This doesn't bother me because the syntax is clear, I almost always want an explicit int value anyway, inheritance is supported, and as you comment, flag values are (mostly) easy to support. class Colors(Enum): red = 1 green = 2 blue = 3 class MoreColors(Colors): cyan = 4 magenta = 5 # chartreuse = 2 would be an error class Flags(Enum): beautiful = 1 fast = 2 elegant = 4 wonderful = 8 Now, it's true that because Flags.fast is not an int, it must be explicitly converted to an int, e.g. `int(Flags.fast)`. That doesn't bother me. What does bother me is that Enum doesn't support automatic conversion to int for OR and AND, so you have to do this:
int(Flags.fast) | int(Flags.elegant) 6
That should be easy enough to fix by adding the appropriate operators so that you could do:
Flags.fast | Flags.elegant 6
Returning an int from such operations is the only sensible interpretation. https://bugs.launchpad.net/flufl.enum/+bug/1110501 As far as autonumbering goes, I think we could support that in Python 3.3+, though I don't have any brilliant ideas on syntax. A couple of suggestions are in this bug: https://bugs.launchpad.net/flufl.enum/+bug/1110507 e.g class Colors(Enum): red = None green = None blue = None or from flufl.enum import Enum, auto class Colors(Enum): red = auto green = auto blue = auto I'm definitely open to suggestions here! Cheers, -Barry
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Wed, Jan 30, 2013 at 7:35 AM, Barry Warsaw <barry@python.org> wrote:
On Jan 30, 2013, at 03:16 PM, Michael Foord wrote:
Being an int subclass (and possibly optionally a strs subclass) is a requirement if any adopted Enum is to be used *within* the standard library in places where integers are currently used as "poor man's enums". I also don't *think* flufl.enum supports flag enums (ones that can be OR'd together), right?
Sure, it does because you have to be explicit about the enum int value to assign the item. This doesn't bother me because the syntax is clear, I almost always want an explicit int value anyway, inheritance is supported, and as you comment, flag values are (mostly) easy to support.
class Colors(Enum): red = 1 green = 2 blue = 3
class MoreColors(Colors): cyan = 4 magenta = 5 # chartreuse = 2 would be an error
class Flags(Enum): beautiful = 1 fast = 2 elegant = 4 wonderful = 8
Now, it's true that because Flags.fast is not an int, it must be explicitly converted to an int, e.g. `int(Flags.fast)`. That doesn't bother me.
What does bother me is that Enum doesn't support automatic conversion to int for OR and AND, so you have to do this:
int(Flags.fast) | int(Flags.elegant) 6
That should be easy enough to fix by adding the appropriate operators so that you could do:
Flags.fast | Flags.elegant 6
Returning an int from such operations is the only sensible interpretation.
https://bugs.launchpad.net/flufl.enum/+bug/1110501
As far as autonumbering goes, I think we could support that in Python 3.3+, though I don't have any brilliant ideas on syntax. A couple of suggestions are in this bug:
https://bugs.launchpad.net/flufl.enum/+bug/1110507
e.g
class Colors(Enum): red = None green = None blue = None
or
from flufl.enum import Enum, auto class Colors(Enum): red = auto green = auto blue = auto
I'm definitely open to suggestions here!
Barry, since you've obviously given this issue a lot of thought, maybe you could summarize it in a PEP so we have a clear way of moving forward for 3.4 ? Eli
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
On Jan 30, 2013, at 08:17 AM, Eli Bendersky wrote:
Barry, since you've obviously given this issue a lot of thought, maybe you could summarize it in a PEP so we have a clear way of moving forward for 3.4 ?
I'm happy to do so if there's a realistic chance of it being accepted. We already have one rejected enum PEP (354) and we probably don't need two. ;) Cheers, -Barry
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
On Wed, Jan 30, 2013 at 8:27 AM, Barry Warsaw <barry@python.org> wrote:
On Jan 30, 2013, at 08:17 AM, Eli Bendersky wrote:
Barry, since you've obviously given this issue a lot of thought, maybe you could summarize it in a PEP so we have a clear way of moving forward for 3.4 ?
I'm happy to do so if there's a realistic chance of it being accepted. We already have one rejected enum PEP (354) and we probably don't need two. ;)
Reading this thread it seems that many core devs are interested in the feature and the discussion is mainly deciding on the exact semantics and implementation. Even Guido didn't really speak against it (only somewhat against adding new syntax). Eli
![](https://secure.gravatar.com/avatar/cb39a0651b7721d2baede607f47adc9d.jpg?s=120&d=mm&r=g)
2013/1/30 Eli Bendersky <eliben@gmail.com>:
On Wed, Jan 30, 2013 at 8:27 AM, Barry Warsaw <barry@python.org> wrote:
On Jan 30, 2013, at 08:17 AM, Eli Bendersky wrote:
Barry, since you've obviously given this issue a lot of thought, maybe you could summarize it in a PEP so we have a clear way of moving forward for 3.4 ?
I'm happy to do so if there's a realistic chance of it being accepted. We already have one rejected enum PEP (354) and we probably don't need two. ;)
Reading this thread it seems that many core devs are interested in the feature and the discussion is mainly deciding on the exact semantics and implementation. Even Guido didn't really speak against it (only somewhat against adding new syntax).
Eli
Personally I'm -1 for a variety of reasons. 1) a const/enum type looks like something which is subject to personal taste to me. I personally don't like, for example, how flufl requires to define constants by using a class. It's just a matter of taste but to me module.FOO looks more "right" than module.Bar.FOO. Also "Colors.red < Colors.blue" raising an exception is something subject to personal taste. 2) introducing something like that (class-based) wouldn't help migrating the existent module-level constants we have in the stdlib. Only new projects or new stdlib modules would benefit from it. 3) other than being subject to personal taste, a const/enum type is also pretty easy to implement. For example, I came up with this: http://code.google.com/p/psutil/source/browse/trunk/psutil/_common.py?spec=svn1562&r=1524#33 ...which is sufficient for my needs. Users having different needs can do a similar thing pretty easily. 4) I'm getting the impression that the language is growing too big. To me, this looks like yet another thing that infrequent users have to learn before being able to read and understand Python code. Also consider that people lived without const/enum for 2 decades now. --- Giampaolo http://code.google.com/p/pyftpdlib/ http://code.google.com/p/psutil/ http://code.google.com/p/pysendfile/
![](https://secure.gravatar.com/avatar/fc761ccaf6c0d7d977e2959f9bfebd06.jpg?s=120&d=mm&r=g)
Reading this thread it seems that many core devs are interested in the
feature and the discussion is mainly deciding on the exact semantics and implementation. Even Guido didn't really speak against it (only somewhat against adding new syntax).
Eli
Personally I'm -1 for a variety of reasons.
1) a const/enum type looks like something which is subject to personal taste to me. I personally don't like, for example, how flufl requires to define constants by using a class. It's just a matter of taste but to me module.FOO looks more "right" than module.Bar.FOO. Also "Colors.red < Colors.blue" raising an exception is something subject to personal taste.
2) introducing something like that (class-based) wouldn't help migrating the existent module-level constants we have in the stdlib. Only new projects or new stdlib modules would benefit from it.
These are more in the domain of implementation details, though, not criticizing the concep?
3) other than being subject to personal taste, a const/enum type is also pretty easy to implement. For example, I came up with this:
http://code.google.com/p/psutil/source/browse/trunk/psutil/_common.py?spec=svn1562&r=1524#33 ...which is sufficient for my needs. Users having different needs can do a similar thing pretty easily.
It is precisely *because* every library defines its own way to create enums that IMHO we should have them in the language (or in the standard library, at the least).
4) I'm getting the impression that the language is growing too big. To me, this looks like yet another thing that infrequent users have to learn before being able to read and understand Python code. Also consider that people lived without const/enum for 2 decades now.
I respectfully disagree. Most folks seem to favor a library solution (i.e. no new syntax, just a new metaclass+class to use). The stdlib has tools for very obscure things. In comparison, enum is something almost every non-trivial program needs to use at some stage or another. Eli
![](https://secure.gravatar.com/avatar/cb39a0651b7721d2baede607f47adc9d.jpg?s=120&d=mm&r=g)
2013/1/30 Eli Bendersky <eliben@gmail.com>:
Reading this thread it seems that many core devs are interested in the
feature and the discussion is mainly deciding on the exact semantics and implementation. Even Guido didn't really speak against it (only somewhat against adding new syntax).
Eli
Personally I'm -1 for a variety of reasons.
1) a const/enum type looks like something which is subject to personal taste to me. I personally don't like, for example, how flufl requires to define constants by using a class. It's just a matter of taste but to me module.FOO looks more "right" than module.Bar.FOO. Also "Colors.red < Colors.blue" raising an exception is something subject to personal taste.
2) introducing something like that (class-based) wouldn't help migrating the existent module-level constants we have in the stdlib. Only new projects or new stdlib modules would benefit from it.
These are more in the domain of implementation details, though, not criticizing the concep?
Personally I'd be +0 for a constant type and -1 for an enum type, which I consider just useless. If a 'constant' type has to be added though, I'd prefer it to be as simple as possible and close to what we've been used thus far, meaning accessing it as "foo.BAR". In everybody's mind it is clear that "foo.BAR" is a constant, and that should be preserved. Something along these lines:
from collections import constant STATUS_IDLE = constant(0, 'idle', doc='refers to the idle state') STATUS_IDLE 0 str(STATUS_IDLE) 'idle'
---- Giampaolo http://code.google.com/p/pyftpdlib/ http://code.google.com/p/psutil/ http://code.google.com/p/pysendfile/
![](https://secure.gravatar.com/avatar/de311342220232e618cb27c9936ab9bf.jpg?s=120&d=mm&r=g)
On 01/30/2013 01:52 PM, � wrote:
2013/1/30 Eli Bendersky <eliben@gmail.com>:
These are more in the domain of implementation details, though, not criticizing the concep?
Personally I'd be +0 for a constant type and -1 for an enum type, which I consider just useless. If a 'constant' type has to be added though, I'd prefer it to be as simple as possible and close to what we've been used thus far, meaning accessing it as "foo.BAR". In everybody's mind it is clear that "foo.BAR" is a constant, and that should be preserved. Something along these lines:
from collections import constant STATUS_IDLE = constant(0, 'idle', doc='refers to the idle state') STATUS_IDLE 0 str(STATUS_IDLE) 'idle'
So you'd have something like: --> from collections import constant --> STATUS_IDLE = constant(0, 'idle', doc='refers to the idle state') --> STATUS_PAUSE = constant(1, 'pause', doc='refers to the pause state') --> STATUS_RUN = constant(2, 'run', doc='refers to the run state') ? Absolutely -1 on this. (Although you can certainly implement it now.) ~Ethan~
![](https://secure.gravatar.com/avatar/01aa7d6d4db83982a2f6dd363d0ee0f3.jpg?s=120&d=mm&r=g)
I'll agree that enums are subject to personal taste, and I am opinionated about the syntax and semantics, as should be evident in my library :). On Jan 30, 2013, at 09:13 PM, Giampaolo Rodolà wrote:
1) a const/enum type looks like something which is subject to personal taste to me. I personally don't like, for example, how flufl requires to define constants by using a class.
In practice, I find this quite nice. In my larger projects, I define the enum class an the interface module and often intersperse comments among the enum values so that more documentation is provided to the reader.
It's just a matter of taste but to me module.FOO looks more "right" than module.Bar.FOO.
I almost always 'from module import MyEnum' so typical use looks something like: if thing.color is Color.red: ... elif thing.color is Color.blue: ... Again, in practice, I find it quite readable and just the right level of verbosity.
Also "Colors.red < Colors.blue" raising an exception is something subject to personal taste.
I guess, if you like blue more than red, but what if you like red more than blue? :) Ordered enums just don't usually make sense, and if they really did, you can coerce to int to do the comparison (but again, I've never needed it, so YAGNI).
2) introducing something like that (class-based) wouldn't help migrating the existent module-level constants we have in the stdlib. Only new projects or new stdlib modules would benefit from it.
Sure, but I don't think this is necessarily about converting the stdlib. We rarely do such mass conversions anyway.
3) other than being subject to personal taste, a const/enum type is also pretty easy to implement.
True, depending on the semantics, syntax, and feature you want.
4) I'm getting the impression that the language is growing too big. To me, this looks like yet another thing that infrequent users have to learn before being able to read and understand Python code. Also consider that people lived without const/enum for 2 decades now.
Well, I would agree that the *language* doesn't need them, but that's different than the stdlib. Maybe the stdlib still doesn't need them either. I don't personally care either way except to save me the trouble of writing up another PEP. :) As for the language growing too big, maybe Pycon 2013 is time for another one of Guido's infamous polls! Cheers, -Barry
![](https://secure.gravatar.com/avatar/db5f70d2f2520ef725839f046bdc32fb.jpg?s=120&d=mm&r=g)
Le Wed, 30 Jan 2013 15:16:49 +0000, Michael Foord <fuzzyman@gmail.com> a écrit :
Being an int subclass (and possibly optionally a strs subclass) is a requirement if any adopted Enum is to be used *within* the standard library in places where integers are currently used as "poor man's enums". I also don't *think* flufl.enum supports flag enums (ones that can be OR'd together), right?
If a flexible solution is desired (with either int or str subclassing, various numbering schemes), may I suggest another kind of syntax: class ErrorFlag(Enum): type = 'symbolic' names = ('strict', 'ignore', 'replace') class SeekFlag(Enum): type = 'sequential' names = ('SET', 'CUR', 'END') class TypeFlag(Enum): type = 'bitmask' names = ('HEAPTYPE', 'HAS_GC', 'INT_SUBCLASS')
ErrorFlag.ignore ErrorFlag.ignore ErrorFlag.ignore == 'ignore' True ErrorFlag('ignore') ErrorFlag.ignore isinstance(ErrorFlag.ignore, str) True isinstance(ErrorFlag.ignore, int) False ErrorFlag(0) [...] ValueError: invalid value for <class 'enum.ErrorFlag'>: 0
SeekFlag('SET') SeekFlag.SET SeekFlag('SET') + 0 0 SeekFlag(0) SeekFlag.SET isinstance(SeekFlag.CUR, int) True isinstance(SeekFlag.CUR, str) False
TypeFlag(1) TypeFlag.HEAPTYPE TypeFlag(2) TypeFlag.HAS_GC TypeFlag.HAS_GC | TypeFlag.INT_SUBCLASS 6
Regards Antoine.
![](https://secure.gravatar.com/avatar/133b712423066c858d82f5d285a6867a.jpg?s=120&d=mm&r=g)
On 30 January 2013 16:56, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 30.01.13 18:23, Antoine Pitrou wrote:
TypeFlag.HAS_GC | TypeFlag.INT_SUBCLASS
6
I prefer something like
TypeFlag.HAS_GC | TypeFlag.INT_SUBCLASS TypeFlag.HAS_GC|INT_SUBCLASS
Indeed - the whole benefit (pretty much) of using an Enum class is that you're no longer dealing with raw ints. Michael
______________________________**_________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/**mailman/listinfo/python-ideas<http://mail.python.org/mailman/listinfo/python-ideas>
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
![](https://secure.gravatar.com/avatar/de311342220232e618cb27c9936ab9bf.jpg?s=120&d=mm&r=g)
On 01/30/2013 08:23 AM, Antoine Pitrou wrote:
If a flexible solution is desired (with either int or str subclassing, various numbering schemes), may I suggest another kind of syntax:
class ErrorFlag(Enum): type = 'symbolic' names = ('strict', 'ignore', 'replace')
class SeekFlag(Enum): type = 'sequential' names = ('SET', 'CUR', 'END')
class TypeFlag(Enum): type = 'bitmask' names = ('HEAPTYPE', 'HAS_GC', 'INT_SUBCLASS')
This I like.
ErrorFlag.ignore ErrorFlag.ignore ErrorFlag.ignore == 'ignore' True ErrorFlag('ignore') ErrorFlag.ignore isinstance(ErrorFlag.ignore, str) True isinstance(ErrorFlag.ignore, int) False ErrorFlag(0) [...] ValueError: invalid value for <class 'enum.ErrorFlag'>: 0
SeekFlag('SET') SeekFlag.SET SeekFlag('SET') + 0 0 SeekFlag(0) SeekFlag.SET isinstance(SeekFlag.CUR, int) True isinstance(SeekFlag.CUR, str) False
TypeFlag(1) TypeFlag.HEAPTYPE TypeFlag(2) TypeFlag.HAS_GC TypeFlag.HAS_GC | TypeFlag.INT_SUBCLASS 6
This should be `TypeFlag.HEAPTYPE|HAS_GC` +1 ~Ethan~
![](https://secure.gravatar.com/avatar/fed4347509b1ae5c11c3151159bcf256.jpg?s=120&d=mm&r=g)
Hello. It should be also possible to specify the values of enum constants explicitly. For 'bitmask' type only powers of 2 should be allowed or maybe the values could be the exponents (as your TypeFlag example indicates). The same way 'symbolic' type acts as str and 'sequential' type acts as int, 'bitmask' type could act both as int and set (or frozenset) since its semantics is like of set. The enum value object could represent both the int value and corresponding singleton set. OR-ing would produce corresponding multivalue set.
isinstance(TypeFlag.HEAPTYPE, int) True isinstance(TypeFlag.HEAPTYTE, set) True
TypeFlag.HAS_GC | TypeFlag.INT_SUBCLASS TypeFlag.HEAPTYPE|HAS_GS # or maybe <TypeFlag {HEAPTYPE, HAS_GS}> TypeFlag.HEAPTYPE in (TypeFlag.HEAPTYPE | TypeFlag.HAS_GC) True TypeFlag.HEAPTYPE in TypeFlag.HEAPTYPE True
TypeFlag(1) TypeFlag.HEAPTYPE TypeFlag(2) TypeFlag.HAS_GC set(TypeFlag.HEAPTYPE) {1} set(TypeFlag.HEAPTYPE | TypeFlag.HAS_GC) {1, 2} int(TypeFlag.HEAPTYPE) 2 int(TypeFlag.HEAPTYPE | TypeFlag.HAS_GC) 6
Note the difference between n and 2 ** n semantics. So there slould be something like
TypeFlag.decompose(2) TypeFlag.HEAPTYPE TypeFlag.decompose(6) TypeFlag.HEAPTYPE|HAS_GS
Regards, Drekin
![](https://secure.gravatar.com/avatar/4c01705256aa2160c1354790e8c154db.jpg?s=120&d=mm&r=g)
I think that instead of the invention of implementation which covers all possible hypothetic scenarios, much more useful for the enums promotion would be if someone were to transform the existing constants in stdlib into enums with a minimum of the necessary capabilities. This will show which features are the most important, which interface is more convenient from user's point of view, and what difficulties have to be faced.
![](https://secure.gravatar.com/avatar/3a5cfb2cee5b7abb9ab4c805e36f2d19.jpg?s=120&d=mm&r=g)
On 1 February 2013 20:27, Serhiy Storchaka <storchaka@gmail.com> wrote:
I think that instead of the invention of implementation which covers all possible hypothetic scenarios
The scenarios I'm really interested in here is DRY; automatic assignment (i.e. not having to specify any values); manual assignment (which should allow any expression that is legal within a class definition) and being able to introspect the enum. Everything else falls out of supporting those scenarios within the normal restrictions of enums (in particular 1:1 mapping) and conforming to obvious interfaces. Tim Delaney
![](https://secure.gravatar.com/avatar/dd4761743695d5efd3692f2a3b35d37d.jpg?s=120&d=mm&r=g)
On Fri, Feb 1, 2013 at 2:27 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
I think that instead of the invention of implementation which covers all possible hypothetic scenarios, much more useful for the enums promotion would be if someone were to transform the existing constants in stdlib into enums with a minimum of the necessary capabilities. This will show which features are the most important, which interface is more convenient from user's point of view, and what difficulties have to be faced.
+1 There's a lot of precedent (and good reasons) for starting with a minimal/basic implementation in the stdlib. -eric
participants (23)
-
Antoine Pitrou
-
Barry Warsaw
-
Bruce Leban
-
Cameron Simpson
-
Chris Angelico
-
drekin@gmail.com
-
Eli Bendersky
-
Eric Snow
-
Ethan Furman
-
Georg Brandl
-
Giampaolo Rodolà
-
Greg Ewing
-
Guido van Rossum
-
Jim Jewett
-
Joao S. O. Bueno
-
João Bernardo
-
Michael Foord
-
Nick Coghlan
-
Raymond Hettinger
-
Serhiy Storchaka
-
Stefan Behnel
-
Terry Reedy
-
Tim Delaney