There are quite a few 'enum' recipes already here. This one is short, cheap, and has neither bells nor whistles :)
1 2 3 4 5 6 7 | def enum(typename, field_names):
"Create a new enumeration type"
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split()
d = dict((reversed(nv) for nv in enumerate(field_names)), __slots__ = ())
return type(typename, (object,), d)()
|
You define an enumeration the same way as collections.namedtuple: with a type name, followed by a sequence of identifiers (it may be a string separated by commas and/or whitespace):
>>> STATE = enum('STATE', 'GET_QUIZ, GET_VERSE, TEACH')
It returns a (single) instance of a newly created type. The enumeration elements are integers, starting at 0, exposed as attributes. There is no way to change that: if you need specific values, look elsewhere. In fact, it is as if the enum were declared as:
# not actual code
class STATE(object):
__slots__ = ()
GET_QUIZ, GET_VERSE, TEACH = range(3)
STATE = STATE()
The enumeration elements are read-only:
>>> STATE.GET_VERSE
1
>>> STATE.GET_VERSE = 8
Traceback (most recent call last):
...
AttributeError: 'STATE' object attribute 'GET_VERSE' is read-only
>>> del STATE.GET_VERSE
Traceback (most recent call last):
...
AttributeError: 'STATE' object attribute 'GET_VERSE' is read-only
This recipe has been tested with Python 2.4 ranging up to 3.1
Thank you for writing this properly! It will definitely get pasted into the module that was using enum. :-)
Thank you! Your recipe is perfect. I was wondering, though: there is
__slots__ = ()
, and the documentation does not appear to have anything that states what this means. Do you have a link to the Python docs that tells was assigning an empty tuple to__slots__
is supposed to do? I think I understand what is happening but would like confirmation from the documentation itself.See __slots__ in the Language Reference: http://docs.python.org/reference/datamodel.html#slots
In this case, __slots__ is (ab)used as a quick way to make the enumeration elements read-only, or at least avoid naïve errors.
Oh, and it's far from being perfect! There are many other recipes out there with more features -- this one is just short and cheap. Thanks anyway... :)
"If defined in a new-style class, __slots__ reserves space for the declared variables and prevents the automatic creation of __dict__ and __weakref__ for each instance." So you defined __slots__ as an empty tuple and the rest of that statement went into effect? If that is true, that would explain why you did not have to set __slots__ equal to the variable names being defined.
Why __slots__: Note that enumeration elements are defined as class attributes; instances of the newly created class have no attributes at all (due to
__slots__=()
), they're as small as any object could be in Python:(In this case this is mostly irrelevant - it does not make sense to have more than a single instance, so the memory usage should not be a concern.)
In addition, setting __slots__ makes the enumeration (almost) read only, for free.
Why not simplify the whole code ?
Here is a shorter form of the above recipe, this time having 2 types of Enums, one with positional arguments and default values and other with user supplied values.
The keyword enum is more of hack, perhaps abusing list comprehensions, but I wanted it to be a one-liner desperately :)
I don't understand the need for one liners, but if you like them, be my guest.
Setting __slots__ as in your recipe is unnecessary and wastes memory; see comment #6 above.
Thanks to Genellina, the code listed above inspired the following usage in the module down below:
Both of Anand's versions can be shortened slightly even more, based on the comments above about setting just setting
__slots__ = ()
.This is trivial, but I thought I'd mention it since I took the time to read all the comments and figure-out how Amand's worked...
Sorry to bother. I really like the suggested implementations here. But I'm having problems running them.
I am have a problem figuring out why I can't run them. I ran the first one on the top of this page and the last one from Martin Miller. In both cases, I get the same result:
I tried this for the first sample
and I get:
For the last one (where the class name is Enum instead of enum):
the result is
In both cases, the function returns a class. I should be able to instantiate that class and assign it as an instance. I happen to be running 2.6.2.
Can someone tell me what I'm doing wrong?
How would you check if an identifier is part of the enum?
Example:
Four and a half years later, this is the function typically used in my projects:
Now that Python has an
enum
module, maybe it is time to work with it instead.