I'm mainly a C# developer, but I'm currently working on a project in Python.
What's the best way to implement the equivalent of an enum in Python?
I'm mainly a C# developer, but I'm currently working on a project in Python.
What's the best way to implement the equivalent of an enum in Python?
Python doesn't have an equivalent but you can implement your own.
Myself, I like keeping it simple (I've seen some horribly complex examples on the net), something like this ...
class Animal:
DOG=1
CAT=2
x = Animal.DOG
What exactly do you want to use an enum for? Is there a more Pythonic way of doing it?
Hi!
Hmmm... I suppose the closest thing to an enum would be a dictionary, defined either like this:
months = {
'January': 1,
'February': 2,
...
}
or
months = dict(
January=1,
February=2,
...
)
Then, you can use the symbolic name for the constants like this:
mymonth = months['January']
There are other options, like a list of tuples, or a tuple of tuples, but the dictionary is the only one that provides you with a "symbolic" (constant string) way to access the value.
Edit: I like Alexandru's answer too!
Python doesn't have a built-in equivalent to enum
, and other answers have ideas for implementing your own (you may also be interested in the over the top version in the Python cookbook).
However, in situations where an enum
would be called for in C, I usually end up just using simple strings: because of the way objects/attributes are implemented, (C)Python is optimized to work very fast with short strings anyway, so there wouldn't really be any performance benefit to using integers. To guard against typos / invalid values you can insert checks in selected places.
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(One disadvantage compared to using a class is that you lose the benefit of autocomplete)
If you need the numeric values, here's the quickest way:
dog, cat, rabbit = range(3)
The typesafe enum pattern which was used in Java pre-JDK 5 has a number of advantages. Much like in Alexandru's answer, you create a class and class level fields are the enum values; however, the enum values are instances of the class rather than small integers. This has the advantage that your enum values don't inadvertently compare equal to small integers, you can control how they're printed, add arbitrary methods if that's useful and make assertions using isinstance:
class Animal:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return "<Animal: %s>" % self
Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")
>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False
davidg recommends using dicts. I'd go one step further and use sets:
months = set('January', 'February', ..., 'December')
Now you can test whether a value matches one of the values in the set like this:
if m in months:
like dF, though, I usually just use string constants in place of enums.
Alexandru's suggestion of using class constants for enums works quite well.
I also like to add a dictionary for each set of constants to lookup a human-readable string representation.
This serves two purposes: a) it provides a simple way to pretty-print your enum and b) the dictionary logically groups the constants so that you can test for membership.
class Animal:
TYPE_DOG = 1
TYPE_CAT = 2
type2str = {
TYPE_DOG: "dog",
TYPE_CAT: "cat"
}
def __init__(self, type_):
assert type_ in self.type2str.keys()
self._type = type_
def __repr__(self):
return "<%s type=%s>" % (
self.__class__.__name__, self.type2str[self._type].upper())
def M_add_class_attribs(attribs):
def foo(name, bases, dict_):
for v, k in attribs:
dict_[k] = v
return type(name, bases, dict_)
return foo
def enum(names):
class Foo(object):
__metaclass__ = M_add_class_attribs(enumerate(names))
def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError
return Foo()
Use it like this:
Animal = enum(('DOG', 'CAT'))
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError
if you just want unique symbols and don't care about the values, replace this line:
__metaclass__ = M_add_class_attribs(enumerate(names))
with this:
__metaclass__ = M_add_class_attribs((object(), name) for name in names)
What I use:
class Enum(object):
def __init__(self, names, separator=None):
self.names = names.split(separator)
for value, name in enumerate(self.names):
setattr(self, name.upper(), value)
def tuples(self):
return tuple(enumerate(self.names))
How to use:
>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))
So this gives you integer constants like state.PUBLISHED and the two-tuples to use as choices in Django models.
The best solution for you would depend on what you require from your fake enum
.
Simple enum:
If you need the enum
as only a list of names identifying different items, the solution by Mark Harrison (above) is great:
(Pen, Pencil, Eraser) = range(0, 3)
Using a range
also allows you to set any starting value:
(Pen, Pencil, Eraser) = range(9, 12)
In addition to the above, if you also require that the items belong to a container of some sort, then embed them in a class:
class Stationary:
(Pen, Pencil, Eraser) = range(0, 3)
To use the enum item, you would now need to use the container name and the item name:
stype = Stationary.Pen
Complex enum:
For long lists of enum or more complicated uses of enum, these solutions will not suffice. You could look to the recipe by Will Ware for Simulating Enumerations in Python published in the Python Cookbook. An online version of that is available here.
More info:
PEP 354: Enumerations in Python has the interesting details of a proposal for enum in Python and why it was rejected.
What about :
TYPE = {'EAN13': u'EAN-13',
'CODE39': u'Code 39',
'CODE128': u'Code 128',
'i25': u'Interleaved 2 of 5',}
>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']
I used that for Django model choices, it looks very pythonic. It is not really a Enum, but do the job.
Here's yet another way:
def enum(**enums):
return type('Enum', (), enums)
Used like so:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
You can also easily support automatic enumeration with something like this:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
Used like so:
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
I had need of some symbolic constants in pyparsing to represent left and right associativity of binary operators. I used class constants like this:
# an internal class, not intended to be seen by client code
class _Constants(object):
pass
# an enumeration of constants for operator associativity
opAssoc = _Constants()
opAssoc.LEFT = object()
opAssoc.RIGHT = object()
Now when client code wants to use these constants, they can import the entire enum using:
import opAssoc from pyparsing
The enumerations are unique, they can be tested with 'is' instead of '==', they don't take up a big footprint in my code for a minor concept, and they are easily imported into the client code. They don't support any fancy str() behavior, but so far that is in the YAGNI category.
Here is another one. It seems somewhat similar to the general approach used by @Cipher. The author called it yapenum, "yet another Python enum".
This is the best one I have seen: "First Class Enums in Python"
http://code.activestate.com/recipes/413486/
It gives you a class, and the class contains all the enums. The enums can be compared to each other, but don't have any particular value; you can't use them as an integer value. (I resisted this at first because I am used to C enums, which are integer values. But if you can't use it as an integer, you can't use it as an integer by mistake so overall I think it is a win.) Each enum is a unique value. You can print enums, you can iterate over them, you can test that an enum value is "in" the enum. It's pretty complete and slick.
Here is what I use....
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
Here is its Implementation...
Animals = Enum(["DOG", "CAT", "Horse"])
print Animals.DOG
Following the Java like enum implementation proposed by Aaron Maenpaa, i came out with this, the idea was to make it generic and parseable.
class Enum:
#'''
#Java like implementation for enums.
#
#Usage:
#class Tool(Enum): name = 'Tool'
#Tool.DRILL = Tool.register('drill')
#Tool.HAMMER = Tool.register('hammer')
#Tool.WRENCH = Tool.register('wrench')
#'''
name = 'Enum' # Enum name
_reg = dict([]) # Enum registered values
@classmethod
def register(cls, value):
#'''
#Registers a new value in this enum.
#
#@param value: New enum value.
#
#@return: New value wrapper instance.
#'''
inst = cls(value)
cls._reg[value] = inst
return inst
@classmethod
def parse(cls, value):
#'''
#Parses a value, returning the enum instance.
#
#@param value: Enum value.
#
#@return: Value corresp instance.
#'''
return cls._reg.get(value)
def __init__(self, value):
#'''
#Constructor (only for internal use).
#'''
self.value = value
def __str__(self):
#'''
#str() overload.
#'''
return self.value
def __repr__(self):
#'''
#repr() overload.
#'''
return "<" + self.name + ": " + self.value + ">"
Considering this same question several years later, the enum package from PyPi provides a robust implementation of enums. An earlier answer mentioned PEP 354; this was rejected but the proposal was implemented http://pypi.python.org/pypi/enum.
Why must enumerations be ints? Unfortunately, I can't think of any good looking construct to produce this without chaning the Python language, so I'll use strings:
class Enumerator(object):
def __init__(self, name):
self.name = name
def __eq__(self, other):
if self.name == other:
return True
return self is other
def __ne__(self, other):
if self.name != other:
return False
return self is other
def __repr__(self):
return 'Enumerator({0})'.format(self.name)
def __str__(self):
return self.name
class Enum(object):
def __init__(self, *enumerators):
for e in enumerators:
setattr(self, e, Enumerator(e))
def __getitem__(self, key):
return getattr(self, key)
Then again maybe it's even better now that we can naturally test against strings, for the sake of config files or other remote input.
Example:
class Cow(object):
State = Enum(
'standing',
'walking',
'eating',
'mooing',
'sleeping',
'dead',
'dying'
)
state = State.standing
In [1]: from enum import Enum
In [2]: c = Cow()
In [3]: c2 = Cow()
In [4]: c.state, c2.state
Out[4]: (Enumerator(standing), Enumerator(standing))
In [5]: c.state == c2.state
Out[5]: True
In [6]: c.State.mooing
Out[6]: Enumerator(mooing)
In [7]: c.State['mooing']
Out[7]: Enumerator(mooing)
In [8]: c.state = Cow.State.dead
In [9]: c.state == c2.state
Out[9]: False
In [10]: c.state == Cow.State.dead
Out[10]: True
In [11]: c.state == 'dead'
Out[11]: True
In [12]: c.state == Cow.State['dead']
Out[11]: True
def enum( *names ):
'''
Makes enum.
Usage:
E = enum( 'YOUR', 'KEYS', 'HERE' )
print( E.HERE )
'''
class Enum():
pass
for index, name in enumerate( names ):
setattr( Enum, name, index )
return Enum
I like the java enum, that's how I do it in python:
def enum(clsdef):
class Enum(object):
__slots__=tuple([var for var in clsdef.__dict__ if isinstance((getattr(clsdef, var)), tuple) and not var.startswith('__')])
def __new__(cls, *args, **kwargs):
if not '_the_instance' in cls.__dict__:
cls._the_instance = object.__new__(cls, *args, **kwargs)
return cls._the_instance
def __init__(self):
clsdef.values=lambda cls, e=Enum: e.values()
clsdef.valueOf=lambda cls, n, e=self: e.valueOf(n)
for ordinal, key in enumerate(self.__class__.__slots__):
args=getattr(clsdef, key)
instance=clsdef(*args)
instance._name=key
instance._ordinal=ordinal
setattr(self, key, instance)
@classmethod
def values(cls):
if not hasattr(cls, '_values'):
cls._values=[getattr(cls, name) for name in cls.__slots__]
return cls._values
def valueOf(self, name):
return getattr(self, name)
def __repr__(self):
return ''.join(['<class Enum (', clsdef.__name__, ') at ', str(hex(id(self))), '>'])
return Enum()
Sample use:
i=2
@enum
class Test(object):
A=("a",1)
B=("b",)
C=("c",2)
D=tuple()
E=("e",3)
while True:
try:
F, G, H, I, J, K, L, M, N, O=[tuple() for _ in range(i)]
break;
except ValueError:
i+=1
def __init__(self, name="default", aparam=0):
self.name=name
self.avalue=aparam
all class variables are defined as a tuple, just like the constructor. so far, you can't use named arguments.
How about the enumerate built-in function?
>>> for i, season in enumerate(['Spring', 'Summer', 'Fall', 'Winter']):
... print i, season
0 Spring
1 Summer
2 Fall
3 Winter