¿Cómo puedo representar un ‘Enum’ en Python?

Soy principalmente un desarrollador de C #, pero actualmente estoy trabajando en un proyecto en Python.

¿Cómo puedo representar el equivalente de un Enum en Python?

Se han agregado enumeraciones a Python 3.4 como se describe en PEP 435 . También ha sido backported a 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 y 2.4 en pypi.

Para técnicas Enum más avanzadas, intente con la biblioteca aenum (2.7, 3.3+, el mismo autor que enum34 . El código no es perfectamente compatible entre py2 y py3, por ejemplo, necesitará __order__ en python 2 ).

  • Para usar enum34 , haga $ pip install enum34
  • Para usar aenum , haz $ pip install aenum

La instalación de enum (sin números) instalará una versión completamente diferente e incompatible.


 from enum import Enum # for enum34, or the stdlib version # from aenum import Enum # for the aenum version Animal = Enum('Animal', 'ant bee cat dog') Animal.ant # returns  Animal['ant'] # returns  (string lookup) Animal.ant.name # returns 'ant' (inverse lookup) 

o equivalente:

 class Animal(Enum): ant = 1 bee = 2 cat = 3 dog = 4 

En versiones anteriores, una forma de lograr enumeraciones es:

 def enum(**enums): return type('Enum', (), enums) 

que se usa como tal:

 >>> Numbers = enum(ONE=1, TWO=2, THREE='three') >>> Numbers.ONE 1 >>> Numbers.TWO 2 >>> Numbers.THREE 'three' 

También puede admitir fácilmente la enumeración automática con algo como esto:

 def enum(*sequential, **named): enums = dict(zip(sequential, range(len(sequential))), **named) return type('Enum', (), enums) 

y utilizado como tal:

 >>> Numbers = enum('ZERO', 'ONE', 'TWO') >>> Numbers.ZERO 0 >>> Numbers.ONE 1 

El soporte para convertir los valores de nuevo a nombres se puede agregar de esta manera:

 def enum(*sequential, **named): enums = dict(zip(sequential, range(len(sequential))), **named) reverse = dict((value, key) for key, value in enums.iteritems()) enums['reverse_mapping'] = reverse return type('Enum', (), enums) 

Esto sobrescribe cualquier cosa con ese nombre, pero es útil para representar sus enumeraciones en la salida. Lanzará KeyError si la asignación inversa no existe. Con el primer ejemplo:

 >>> Numbers.reverse_mapping['three'] 'THREE' 

Antes de PEP 435, Python no tenía un equivalente, pero puedes implementar el tuyo.

A mí mismo, me gusta mantenerlo simple (he visto algunos ejemplos horriblemente complejos en la red), algo como esto …

 class Animal: DOG = 1 CAT = 2 x = Animal.DOG 

En Python 3.4 ( PEP 435 ), puede hacer que Enum sea la clase base. Esto le proporciona un poco de funcionalidad adicional, descrita en el PEP. Por ejemplo, los miembros de la enumeración son distintos de los enteros y están compuestos de un name y un value .

 class Animal(Enum): DOG = 1 CAT = 2 print(Animal.DOG) #  print(Animal.DOG.value) # 1 print(Animal.DOG.name) # "DOG" 

Si no desea escribir los valores, use el siguiente método abreviado:

 class Animal(Enum): DOG, CAT = range(2) 

Enum implementaciones de enumeración se pueden convertir a listas y son iterables . El orden de sus miembros es el orden de statement y no tiene nada que ver con sus valores. Por ejemplo:

 class Animal(Enum): DOG = 1 CAT = 2 COW = 0 list(Animal) # [, , ] [animal.value for animal in Animal] # [1, 2, 0] Animal.CAT in Animal # True 

Aquí hay una implementación:

 class Enum(set): def __getattr__(self, name): if name in self: return name raise AttributeError 

Aquí está su uso:

 Animals = Enum(["DOG", "CAT", "HORSE"]) print(Animals.DOG) 

Si necesita los valores numéricos, aquí está la manera más rápida:

 dog, cat, rabbit = range(3) 

En Python 3.x también puedes agregar un marcador de posición destacado al final, que absorberá todos los valores restantes del rango en caso de que no te importe perder memoria y no puedas contar:

 dog, cat, rabbit, horse, *_ = range(100) 

La mejor solución para usted dependería de lo que necesite de su enum falsa .

Enumeración simple:

Si necesita la enum como solo una lista de nombres que identifican diferentes elementos , la solución de Mark Harrison (arriba) es excelente:

 Pen, Pencil, Eraser = range(0, 3) 

El uso de un range también le permite establecer cualquier valor de inicio :

 Pen, Pencil, Eraser = range(9, 12) 

Además de lo anterior, si también requiere que los elementos pertenezcan a un contenedor de algún tipo, entonces insértelos en una clase:

 class Stationery: Pen, Pencil, Eraser = range(0, 3) 

Para usar el elemento de enumeración, ahora necesitaría usar el nombre del contenedor y el nombre del elemento:

 stype = Stationery.Pen 

Enumeración compleja:

Para listas largas de enumeración o usos más complicados de la enumeración, estas soluciones no serán suficientes. Puede consultar la receta de Will Ware para simular enumeraciones en Python publicada en el Libro de cocina de Python . Una versión en línea de eso está disponible aquí .

Más información:

PEP 354: Enumerations in Python tiene los detalles interesantes de una propuesta de enumeración en Python y por qué se rechazó.

El patrón de enumeración de tipos seguros que se usó en Java pre-JDK 5 tiene varias ventajas. Al igual que en la respuesta de Alexandru, usted crea una clase y los campos de nivel de clase son los valores de enumeración; sin embargo, los valores de enumeración son instancias de la clase en lugar de números enteros pequeños. Esto tiene la ventaja de que sus valores de enumeración no se comparan inadvertidamente con enteros pequeños, puede controlar cómo se imprimen, agregar métodos arbitrarios si eso es útil y hacer afirmaciones utilizando isinstance:

 class Animal: def __init__(self, name): self.name = name def __str__(self): return self.name def __repr__(self): return "" % self Animal.DOG = Animal("dog") Animal.CAT = Animal("cat") >>> x = Animal.DOG >>> x  >>> x == 1 False 

Un hilo reciente en python-dev señaló que hay un par de bibliotecas de enumeración en la naturaleza, que incluyen:

  • flufl.enum
  • lazr.enum
  • … y la enumeración imaginativamente nombrada

Una clase Enum puede ser de una sola línea.

 class Enum(tuple): __getattr__ = tuple.index 

Cómo usarlo (búsqueda directa e inversa, claves, valores, elementos, etc.)

 >>> State = Enum(['Unclaimed', 'Claimed']) >>> State.Claimed 1 >>> State[1] 'Claimed' >>> State ('Unclaimed', 'Claimed') >>> range(len(State)) [0, 1] >>> [(k, State[k]) for k in range(len(State))] [(0, 'Unclaimed'), (1, 'Claimed')] >>> [(k, getattr(State, k)) for k in State] [('Unclaimed', 0), ('Claimed', 1)] 

Por lo tanto, estoy de acuerdo. No apliquemos la seguridad de tipos en Python, pero me gustaría protegerme de los errores tontos. Entonces, ¿qué pensamos acerca de esto?

 class Animal(object): values = ['Horse','Dog','Cat'] class __metaclass__(type): def __getattr__(self, name): return self.values.index(name) 

Me impide la colisión de valores en la definición de mis enumeraciones.

 >>> Animal.Cat 2 

Hay otra ventaja útil: búsquedas inversas realmente rápidas:

 def name_of(self, i): return self.values[i] 

Python no tiene un equivalente integrado a enum , y otras respuestas tienen ideas para implementar la suya (también puede estar interesado en la versión superior en el libro de recetas de Python).

Sin embargo, en situaciones en las que se enum una enum en C, generalmente termino usando cadenas simples : debido a la forma en que se implementan los objetos / atributos, (C) Python está optimizado para funcionar muy rápido con cadenas cortas de todos modos, así que Realmente no sería ningún beneficio de rendimiento al usar enteros. Para protegerse contra errores tipográficos / valores no válidos, puede insertar cheques en los lugares seleccionados.

 ANIMALS = ['cat', 'dog', 'python'] def take_for_a_walk(animal): assert animal in ANIMALS ... 

(Una desventaja en comparación con el uso de una clase es que pierde el beneficio de autocompletar)

El 2013-05-10, Guido aceptó aceptar PEP 435 en la biblioteca estándar de Python 3.4. ¡Esto significa que Python finalmente ha incorporado soporte para las enumeraciones!

Hay un backport disponible para Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 y 2.4. Está en Pypi como enum34 .

Declaración:

 >>> from enum import Enum >>> class Color(Enum): ... red = 1 ... green = 2 ... blue = 3 

Representación:

 >>> print(Color.red) Color.red >>> print(repr(Color.red))  

Iteración:

 >>> for color in Color: ... print(color) ... Color.red Color.green Color.blue 

Acceso programático:

 >>> Color(1) Color.red >>> Color['blue'] Color.blue 

Para más información, consultar la propuesta . La documentación oficial probablemente seguirá pronto.

Prefiero definir enums en Python así:

 class Animal: class Dog: pass class Cat: pass x = Animal.Dog 

Es más a prueba de errores que usar números enteros, ya que no tiene que preocuparse por asegurarse de que los números enteros sean únicos (por ejemplo, si dijo que Dog = 1 y Cat = 1 estarían jodidos).

Es más a prueba de errores que usar cadenas, ya que no tiene que preocuparse por los errores tipográficos (por ejemplo, x == “catt” falla silenciosamente, pero x == Animal.Catt es una excepción de tiempo de ejecución).

 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() 

Úsalo así:

 Animal = enum('DOG', 'CAT') Animal.DOG # returns 0 Animal.CAT # returns 1 Animal.DOG = 2 # raises NotImplementedError 

Si solo desea símbolos únicos y no le importan los valores, reemplace esta línea:

 __metaclass__ = M_add_class_attribs(enumerate(names)) 

con este:

 __metaclass__ = M_add_class_attribs((object(), name) for name in names) 

Hmmm … Supongo que lo más parecido a una enumeración sería un diccionario, definido como este:

 months = { 'January': 1, 'February': 2, ... } 

o

 months = dict( January=1, February=2, ... ) 

Luego, puedes usar el nombre simbólico para las constantes como esta:

 mymonth = months['January'] 

Hay otras opciones, como una lista de tuplas o una tupla de tuplas, pero el diccionario es el único que le proporciona una forma “simbólica” (cadena constante) para acceder al valor.

Edit: Me gusta la respuesta de Alexandru también!

Otra implementación muy simple de una enumeración en Python, utilizando namedtuple :

 from collections import namedtuple def enum(*keys): return namedtuple('Enum', keys)(*keys) MyEnum = enum('FOO', 'BAR', 'BAZ') 

o alternativamente,

 # With sequential number values def enum(*keys): return namedtuple('Enum', keys)(*range(len(keys))) # From a dict / keyword args def enum(**kwargs): return namedtuple('Enum', kwargs.keys())(*kwargs.values()) 

Al igual que el método por encima de ese set subclases, esto permite:

 'FOO' in MyEnum other = MyEnum.FOO assert other == MyEnum.FOO 

Pero tiene más flexibilidad ya que puede tener diferentes claves y valores. Esto permite

 MyEnum.FOO < MyEnum.BAR 

para actuar como se espera si usa la versión que llena los valores numéricos secuenciales.

Lo que yo uso:

 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)) 

Cómo utilizar:

 >>> state = Enum('draft published retracted') >>> state.DRAFT 0 >>> state.RETRACTED 2 >>> state.FOO Traceback (most recent call last): File "", line 1, in  AttributeError: 'Enum' object has no attribute 'FOO' >>> state.tuples() ((0, 'draft'), (1, 'published'), (2, 'retracted')) 

Así que esto te da constantes enteras como state.PUBLISHED y las dos tuplas para usar como opciones en los modelos de Django.

Desde Python 3.4 habrá soporte oficial para enums. Puede encontrar documentación y ejemplos aquí en la página de documentación de Python 3.4 .

Las enumeraciones se crean utilizando la syntax de clase, lo que las hace fáciles de leer y escribir. Un método de creación alternativo se describe en la API funcional. Para definir una enumeración, subclase Enum como sigue:

 from enum import Enum class Color(Enum): red = 1 green = 2 blue = 3 

davidg recomienda el uso de dictados. Iría un paso más allá y usaría sets:

 months = set('January', 'February', ..., 'December') 

Ahora puede probar si un valor coincide con uno de los valores en el conjunto como este:

 if m in months: 

Sin embargo, como dF, normalmente solo uso constantes de cadena en lugar de enumeraciones.

Este es el mejor que he visto: “First Class Enums in Python”

http://code.activestate.com/recipes/413486/

Te da una clase, y la clase contiene todas las enumeraciones. Las enumeraciones pueden compararse entre sí, pero no tienen ningún valor en particular; no puedes usarlos como un valor entero (Al principio me resistí a esto porque estoy acostumbrado a las Cums, que son valores enteros. Pero si no puedes usarlo como un entero, no puedes usarlo como un entero por error, así que en general creo que es una victoria .) Cada enumeración es un valor único. Puede imprimir enumeraciones, puede iterar sobre ellas, puede probar que un valor de enumeración está “en” la enumeración. Es bastante completo y resbaladizo.

Editar (cfi): el enlace anterior no es compatible con Python 3. Aquí está mi puerto de enum.py a Python 3:

 def cmp(a,b): if a < b: return -1 if b < a: return 1 return 0 def Enum(*names): ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment! class EnumClass(object): __slots__ = names def __iter__(self): return iter(constants) def __len__(self): return len(constants) def __getitem__(self, i): return constants[i] def __repr__(self): return 'Enum' + str(names) def __str__(self): return 'enum ' + str(constants) class EnumValue(object): __slots__ = ('__value') def __init__(self, value): self.__value = value Value = property(lambda self: self.__value) EnumType = property(lambda self: EnumType) def __hash__(self): return hash(self.__value) def __cmp__(self, other): # C fans might want to remove the following assertion # to make all enums comparable by ordinal value {;)) assert self.EnumType is other.EnumType, "Only values from the same enum are comparable" return cmp(self.__value, other.__value) def __lt__(self, other): return self.__cmp__(other) < 0 def __eq__(self, other): return self.__cmp__(other) == 0 def __invert__(self): return constants[maximum - self.__value] def __nonzero__(self): return bool(self.__value) def __repr__(self): return str(names[self.__value]) maximum = len(names) - 1 constants = [None] * len(names) for i, each in enumerate(names): val = EnumValue(i) setattr(EnumClass, each, val) constants[i] = val constants = tuple(constants) EnumType = EnumClass() return EnumType if __name__ == '__main__': print( '\n*** Enum Demo ***') print( '--- Days of week ---') Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su') print( Days) print( Days.Mo) print( Days.Fr) print( Days.Mo < Days.Fr) print( list(Days)) for each in Days: print( 'Day:', each) print( '--- Yes/No ---') Confirmation = Enum('No', 'Yes') answer = Confirmation.No print( 'Your answer is not', ~answer) 

Mantenlo simple:

 class Enum(object): def __init__(self, tupleList): self.tupleList = tupleList def __getattr__(self, name): return self.tupleList.index(name) 

Entonces:

 DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT')) DIRECTION.DOWN 1 

He tenido ocasión de necesitar una clase Enum, con el propósito de decodificar un formato de archivo binario. Las características que deseaba son una definición concisa de enumeración, la capacidad de crear libremente instancias de la enumeración por valor entero o cadena, y una representación útil. Esto es lo que terminé con:

 >>> class Enum(int): ... def __new__(cls, value): ... if isinstance(value, str): ... return getattr(cls, value) ... elif isinstance(value, int): ... return cls.__index[value] ... def __str__(self): return self.__name ... def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name) ... class __metaclass__(type): ... def __new__(mcls, name, bases, attrs): ... attrs['__slots__'] = ['_Enum__name'] ... cls = type.__new__(mcls, name, bases, attrs) ... cls._Enum__index = _index = {} ... for base in reversed(bases): ... if hasattr(base, '_Enum__index'): ... _index.update(base._Enum__index) ... # create all of the instances of the new class ... for attr in attrs.keys(): ... value = attrs[attr] ... if isinstance(value, int): ... evalue = int.__new__(cls, value) ... evalue._Enum__name = attr ... _index[value] = evalue ... setattr(cls, attr, evalue) ... return cls ... 

Un ejemplo caprichoso de usarlo:

 >>> class Citrus(Enum): ... Lemon = 1 ... Lime = 2 ... >>> Citrus.Lemon Citrus.Lemon >>> >>> Citrus(1) Citrus.Lemon >>> Citrus(5) Traceback (most recent call last): File "", line 1, in  File "", line 6, in __new__ KeyError: 5 >>> class Fruit(Citrus): ... Apple = 3 ... Banana = 4 ... >>> Fruit.Apple Fruit.Apple >>> Fruit.Lemon Citrus.Lemon >>> Fruit(1) Citrus.Lemon >>> Fruit(3) Fruit.Apple >>> "%d %s %r" % ((Fruit.Apple,)*3) '3 Apple Fruit.Apple' >>> Fruit(1) is Citrus.Lemon True 

Características clave:

  • str() , int() y repr() producen la salida más útil posible, respectivamente, el nombre de la enumeración, su valor entero y una expresión de Python que se evalúa de nuevo a la enumeración.
  • Los valores enumerados devueltos por el constructor están limitados estrictamente a los valores predefinidos, no a valores de enumeración accidentales.
  • Los valores enumerados son singletons; Se pueden comparar estrictamente con

Realmente me gusta la solución de Alec Thomas (http://stackoverflow.com/a/1695250):

 def enum(**enums): '''simple constant "enums"''' return type('Enum', (object,), enums) 

Es elegante y de apariencia limpia, pero es solo una función que crea una clase con los atributos especificados.

Con una pequeña modificación en la función, podemos hacer que actúe un poco más ‘enumeración’:

NOTA: Creé los siguientes ejemplos al tratar de reproducir el comportamiento del nuevo estilo de pygtk ‘enums’ (como Gtk.MessageType.WARNING)

 def enum_base(t, **enums): '''enums with a base class''' T = type('Enum', (t,), {}) for key,val in enums.items(): setattr(T, key, T(val)) return T 

Esto crea una enumeración basada en un tipo especificado. Además de otorgar acceso a atributos como la función anterior, se comporta como se esperaría de un Enum con respecto a los tipos. También hereda la clase base.

Por ejemplo, enumeraciones enteras:

 >>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3) >>> Numbers.ONE 1 >>> x = Numbers.TWO >>> 10 + x 12 >>> type(Numbers)  >>> type(Numbers.ONE)  >>> isinstance(x, Numbers) True 

Otra cosa interesante que se puede hacer con este método es personalizar el comportamiento específico al reemplazar los métodos integrados:

 def enum_repr(t, **enums): '''enums with a base class and repr() output''' class Enum(t): def __repr__(self): return ''.format(self._name, t.__name__) for key,val in enums.items(): i = Enum(val) i._name = key setattr(Enum, key, i) return Enum >>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3) >>> repr(Numbers.ONE) '' >>> str(Numbers.ONE) '1' 

El nuevo estándar en Python es PEP 435 , por lo que una clase Enum estará disponible en futuras versiones de Python:

 >>> from enum import Enum 

Sin embargo, para comenzar a usarlo ahora, puede instalar la biblioteca original que motivó el PEP:

 #sudo pip install flufl.enum //or #sudo easy_install flufl.enum 

Entonces puedes usarlo según su guía en línea :

 >>> from flufl.enum import Enum >>> class Colors(Enum): ... red = 1 ... green = 2 ... blue = 3 >>> for color in Colors: print color Colors.red Colors.green Colors.blue 
 def enum(*sequential, **named): enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named) return type('Enum', (), enums) 

Si lo nombra, es su problema, pero si no crea objetos en lugar de valores le permite hacer esto:

 >>> DOG = enum('BARK', 'WALK', 'SIT') >>> CAT = enum('MEOW', 'WALK', 'SIT') >>> DOG.WALK == CAT.WALK False 

Cuando use otras implementaciones ubicadas aquí (también cuando use instancias nombradas en mi ejemplo), debe asegurarse de nunca intentar comparar objetos de diferentes enumeraciones. Para aquí hay un posible escollo:

 >>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3) >>> CAT = enum('WALK'=1, 'SIT'=2) >>> pet1_state = DOG.BARK >>> pet2_state = CAT.WALK >>> pet1_state == pet2_state True 

¡Ay!

El paquete de enumeración de PyPI proporciona una implementación robusta de enums. Una respuesta anterior mencionó PEP 354; esto fue rechazado pero la propuesta fue implementada http://pypi.python.org/pypi/enum .

El uso es fácil y elegante:

 >>> from enum import Enum >>> Colors = Enum('red', 'blue', 'green') >>> shirt_color = Colors.green >>> shirt_color = Colors[2] >>> shirt_color > Colors.red True >>> shirt_color.index 2 >>> str(shirt_color) 'green' 

La sugerencia de Alexandru de usar constantes de clase para enums funciona bastante bien.

También me gusta agregar un diccionario para cada conjunto de constantes para buscar una representación de cadena legible por humanos.

Esto sirve para dos propósitos: a) proporciona una forma sencilla de imprimir en forma bonita su enumeración yb) el diccionario agrupa lógicamente las constantes para que pueda probar la membresía.

 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()) 

Aquí hay un enfoque con algunas características diferentes que me parecen valiosas:

  • permite> y
  • puede direccionar el elemento por nombre, propiedad o índice: xa, x [‘a’] o x [0]
  • admite operaciones de segmentación como [:] o [-1]

and most importantly prevents comparisons between enums of different types !

Based closely on http://code.activestate.com/recipes/413486-first-class-enums-in-python .

Many doctests included here to illustrate what’s different about this approach.

 def enum(*names): """ SYNOPSIS Well-behaved enumerated type, easier than creating custom classes DESCRIPTION Create a custom type that implements an enumeration. Similar in concept to a C enum but with some additional capabilities and protections. See http://code.activestate.com/recipes/413486-first-class-enums-in-python/. PARAMETERS names Ordered list of names. The order in which names are given will be the sort order in the enum type. Duplicate names are not allowed. Unicode names are mapped to ASCII. RETURNS Object of type enum, with the input names and the enumerated values. EXAMPLES >>> letters = enum('a','e','i','o','u','b','c','y','z') >>> letters.a < letters.e True ## index by property >>> letters.a a ## index by position >>> letters[0] a ## index by name, helpful for bridging string inputs to enum >>> letters['a'] a ## sorting by order in the enum() create, not character value >>> letters.u < letters.b True ## normal slicing operations available >>> letters[-1] z ## error since there are not 100 items in enum >>> letters[99] Traceback (most recent call last): ... IndexError: tuple index out of range ## error since name does not exist in enum >>> letters['ggg'] Traceback (most recent call last): ... ValueError: tuple.index(x): x not in tuple ## enums must be named using valid Python identifiers >>> numbers = enum(1,2,3,4) Traceback (most recent call last): ... AssertionError: Enum values must be string or unicode >>> a = enum('-a','-b') Traceback (most recent call last): ... TypeError: Error when calling the metaclass bases __slots__ must be identifiers ## create another enum >>> tags = enum('a','b','c') >>> tags.a a >>> letters.a a ## can't compare values from different enums >>> letters.a == tags.a Traceback (most recent call last): ... AssertionError: Only values from the same enum are comparable >>> letters.a < tags.a Traceback (most recent call last): ... AssertionError: Only values from the same enum are comparable ## can't update enum after create >>> letters.a = 'x' Traceback (most recent call last): ... AttributeError: 'EnumClass' object attribute 'a' is read-only ## can't update enum after create >>> del letters.u Traceback (most recent call last): ... AttributeError: 'EnumClass' object attribute 'u' is read-only ## can't have non-unique enum values >>> x = enum('a','b','c','a') Traceback (most recent call last): ... AssertionError: Enums must not repeat values ## can't have zero enum values >>> x = enum() Traceback (most recent call last): ... AssertionError: Empty enums are not supported ## can't have enum values that look like special function names ## since these could collide and lead to non-obvious errors >>> x = enum('a','b','c','__cmp__') Traceback (most recent call last): ... AssertionError: Enum values beginning with __ are not supported LIMITATIONS Enum values of unicode type are not preserved, mapped to ASCII instead. """ ## must have at least one enum value assert names, 'Empty enums are not supported' ## enum values must be strings assert len([i for i in names if not isinstance(i, types.StringTypes) and not \ isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode' ## enum values must not collide with special function names assert len([i for i in names if i.startswith("__")]) == 0,\ 'Enum values beginning with __ are not supported' ## each enum value must be unique from all others assert names == uniquify(names), 'Enums must not repeat values' class EnumClass(object): """ See parent function for explanation """ __slots__ = names def __iter__(self): return iter(constants) def __len__(self): return len(constants) def __getitem__(self, i): ## this makes xx['name'] possible if isinstance(i, types.StringTypes): i = names.index(i) ## handles the more normal xx[0] return constants[i] def __repr__(self): return 'enum' + str(names) def __str__(self): return 'enum ' + str(constants) def index(self, i): return names.index(i) class EnumValue(object): """ See parent function for explanation """ __slots__ = ('__value') def __init__(self, value): self.__value = value value = property(lambda self: self.__value) enumtype = property(lambda self: enumtype) def __hash__(self): return hash(self.__value) def __cmp__(self, other): assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable' return cmp(self.value, other.value) def __invert__(self): return constants[maximum - self.value] def __nonzero__(self): ## return bool(self.value) ## Original code led to bool(x[0])==False, not correct return True def __repr__(self): return str(names[self.value]) maximum = len(names) - 1 constants = [None] * len(names) for i, each in enumerate(names): val = EnumValue(i) setattr(EnumClass, each, val) constants[i] = val constants = tuple(constants) enumtype = EnumClass() return enumtype 

Here is a variant on Alec Thomas’s solution :

 def enum(*args, **kwargs): return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL') assert x.POOH == 0 assert x.TIGGER == 1 

This solution is a simple way of getting a class for the enumeration defined as a list (no more annoying integer assignments):

enumeration.py:

 import new def create(class_name, names): return new.classobj( class_name, (object,), dict((y, x) for x, y in enumerate(names)) ) 

example.py:

 import enumeration Colors = enumeration.create('Colors', ( 'red', 'orange', 'yellow', 'green', 'blue', 'violet', )) 

While the original enum proposal, PEP 354 , was rejected years ago, it keeps coming back up. Some kind of enum was intended to be added to 3.2, but it got pushed back to 3.3 and then forgotten. And now there’s a PEP 435 intended for inclusion in Python 3.4. The reference implementation of PEP 435 is flufl.enum .

As of April 2013, there seems to be a general consensus that something should be added to the standard library in 3.4—as long as people can agree on what that “something” should be. That’s the hard part. See the threads starting here and here , and a half dozen other threads in the early months of 2013.

Meanwhile, every time this comes up, a slew of new designs and implementations appear on PyPI, ActiveState, etc., so if you don’t like the FLUFL design, try a PyPI search .

A variant (with support to get an enum value’s name) to Alec Thomas’s neat answer :

 class EnumBase(type): def __init__(self, name, base, fields): super(EnumBase, self).__init__(name, base, fields) self.__mapping = dict((v, k) for k, v in fields.iteritems()) def __getitem__(self, val): return self.__mapping[val] def enum(*seq, **named): enums = dict(zip(seq, range(len(seq))), **named) return EnumBase('Enum', (), enums) Numbers = enum(ONE=1, TWO=2, THREE='three') print Numbers.TWO print Numbers[Numbers.ONE] print Numbers[2] print Numbers['three']