tags:

views:

14539

answers:

26

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?

+56  A: 

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
Alexandru Nedelcu
What's the point of defining numerical values (1 and 2)? They seem useless, and that's why I prefer zacherates' solution.
bortzmeyer
It is clear from the example that constants are being defined. There is an issue with type safety, however, which may cause problems.
strager
You should have first created an instance of Animal class..X = Animal()z = X.DOG
aatifh
No, it's a class variable.
Georg
-1, there are several reasons that the Type-Safe Enum pattern was created. See http://java.sun.com/developer/Books/shiftintojava/page1.html. Yes I know that this is a python question, but a good pattern is a good pattern.
WolfmanDragon
Python is dynamic by default. There's no valid reason to enforce compile-time safety in a language like Python, especially when there is none.And another thing ... a good pattern is only good in the context in which it was created. A good pattern can also be superseded or completely useless, depending on the tools you're using.
Alexandru Nedelcu
Using range is generally better
Casebash
what if we inherit set and use __getattr__ method to use Enum, seehttp://stackoverflow.com/questions/36932/whats-the-best-way-to-implement-an-enum-in-python/2182437#2182437
Tumbleweed
What if I have 100 values, I have to write the numbers for each of them? Yuk!
Longpoke
@Longpoke if you have 100 values, then you're definitely doing something wrong ;)I like numbers associated with my enums ... they are easy to write (vs strings), can be easily persisted in a database, and are compatible with the C/C++ enum, which makes for easier marshaling.
Alexandru Nedelcu
I use this, with the numbers replaced by `object()`.
Tobu
+9  A: 

What exactly do you want to use an enum for? Is there a more Pythonic way of doing it?

Chris Upchurch
Because Python doesn't catch string typos at runtime. Compare `date = WEDSENDAY` vs. `date = 'wedsenday'` — the former will raise a NameError.
a paid nerd
+2  A: 

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!

dguaraglia
+14  A: 

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)

dF
I prefer this solution. I like to use built-in types where possible.
Seun Osewa
That version isn't really over the top. It just has a lot of supplied testing code
Casebash
Actually, the "correct" version is in the comments and is much more complex - the main version has a minor bug.
Casebash
+36  A: 

If you need the numeric values, here's the quickest way:

dog, cat, rabbit = range(3)
Mark Harrison
You can make it even shorter: `dog, cat, rabbit = range(3)` You don't really need the parens around the tuple unpacking, and of course `range()` goes from 0 by default.
steveha
But then you have to count the number of enumerated things, which can be very annoying, it should be done automatically.
Longpoke
+40  A: 

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
Aaron Maenpaa
I think this is a very bad approach. Animal.DOG = Animal("dog")Animal.DOG2 = Animal("dog")assert Animal.DOG == Animal.DOG2 fails...
Confusion
@Confusion The user isn't supposed to call the constructor, the fact that there's even a constructor is an implementation detail and you have to communicate to who ever is using your code that making new enumeration values makes no sense and that exiting code will not "do the right thing". Of course that doesn't stop you from implementing Animal.from_name("dog") --> Animal.DOG.
Aaron Maenpaa
"the advantage that your enum values don't inadvertently compare equal to small integers" What's the advantage in this? What's wrong with comparing your enum to integers? Especially if you store the enum in the database, you usually want it to be stored as integers, so you'll have to compare it to integers at some point.
ionut bizau
@AaronMcSmooth Incorrect: dir(Animal) --> ['CAT', 'DOG', '__doc__', '__init__', '__module__', '__repr__', '__str__']
Aaron Maenpaa
@Aaaron Maenpaa. correct. It's still a broken and overly complicated way to do it.
aaronasterling
@AaronMcSmooth That really depends on whether you're coming in from the C perspective of "Enums are just names for a couple of ints" or the more object oriented approach where enum values are actual objects and have methods (which is how enums in Java 1.5 are, and which the type safe enum pattern was going for). Personally, I don't like switch statements so I lean towards enum values that are actual objects.
Aaron Maenpaa
+1  A: 

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.

tuxedo
yep!, much better if u inherit set and provide __getattr__ method !
Tumbleweed
+1  A: 

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())
Rick Harris
+1  A: 

You can take a look at the traits package. This gives you something like type safety and many other useful features.

But it really depends on what you want to use such an enum for.

nikow
+17  A: 
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)
IMHO it would be cleaner if you changed `enum(names)` to `enum(*names)` - then you could drop the extra parenthesis when calling it.
Chris Lutz
I like this approach. I actually changed it to set the attribute value to the same string as the name, which has the nice property that Animal.DOG == 'DOG', so they stringify themselves for you automatically. (Helps immensely for printing out debug output.)
Ted Mielczarek
A: 
Cipher
+9  A: 

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.

+16  A: 

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.

Ashwin
I like this solution; short, simple; just like the C enum...
Malkocoglu
I like the class approach, makes it easy to import the enum into other modules
Brandon Thomson
A: 

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.

Natim
+38  A: 

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
Alec Thomas
+1, this isn't a horrible way to do something that i've never wanted to do.
TokenMacGuy
Dude! This solution is awesome. My favorite I think!
Mark
+1  A: 

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.

Paul McGuire
Downvote, quoi?
Paul McGuire
+1  A: 

Here is another one. It seems somewhat similar to the general approach used by @Cipher. The author called it yapenum, "yet another Python enum".

http://blog.bstpierre.org/yet-another-python-enum-module

steveha
+1  A: 

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.

steveha
+23  A: 

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
Tumbleweed
I'd say this is the most Pythonic, and correct, way. Implementing it now!
Beau Martínez
A: 

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 + ">"
iobaixas
I can not understand how this repr could be used to reproduce the object.
Tony Veijalainen
+1  A: 

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.

pythonic metaphor
A: 

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
Longpoke
A: 
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
Denis Ryzhkov
A: 

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.

daegga
python3k btw, don't know if it runs on 2.x
daegga
A: 

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
clay
This is useful, but not really the same thing as the `enum` abstraction provided in other languages (and, more importantly, not useful for the same purposes).
Charles Duffy