¿Cómo debo emular y / o evitar enumerar en Python?

He estado usando una clase pequeña para emular Enums en algunos proyectos de Python. ¿Hay alguna forma mejor o esto tiene más sentido para algunas situaciones?

Código de clase aquí:

class Enum(object): '''Simple Enum Class Example Usage: >>> codes = Enum('FOO BAR BAZ') # codes.BAZ will be 2 and so on ...''' def __init__(self, names): for number, name in enumerate(names.split()): setattr(self, name, number) 

Hay mucha buena discusión aquí .

Las enumeraciones se han propuesto para su inclusión en el lenguaje anteriormente, pero se rechazaron (consulte http://www.python.org/dev/peps/pep-0354/ ), aunque hay paquetes existentes que puede usar en lugar de escribir su propia implementación :

El caso de enumeración más común es el de los valores enumerados que forman parte de un patrón de diseño de Estado o Estrategia. Las enumeraciones son estados específicos o estrategias opcionales específicas que se utilizarán. En este caso, casi siempre forman parte de alguna definición de clase.

 class DoTheNeedful( object ): ONE_CHOICE = 1 ANOTHER_CHOICE = 2 YET_ANOTHER = 99 def __init__( self, aSelection ): assert aSelection in ( self.ONE_CHOICE, self.ANOTHER_CHOICE, self.YET_ANOTHER ) self.selection= aSelection 

Luego, en un cliente de esta clase.

 dtn = DoTheNeeful( DoTheNeeful.ONE_CHOICE ) 

Lo que veo más a menudo es esto, en el contexto del módulo de nivel superior:

 FOO_BAR = 'FOO_BAR' FOO_BAZ = 'FOO_BAZ' FOO_QUX = 'FOO_QUX' 

…y después…

 if something is FOO_BAR: pass # do something here elif something is FOO_BAZ: pass # do something else elif something is FOO_QUX: pass # do something else else: raise Exception('Invalid value for something') 

Tenga en cuenta que el uso de is lugar de == está tomando un riesgo aquí: se supone que la gente está usando your_module.FOO_BAR lugar de la cadena 'FOO_BAR' (que normalmente se internará de manera que coincida, pero ciertamente puede ‘ Se puede contar con) y, por lo tanto, puede no ser apropiado dependiendo del contexto.

Una de las ventajas de hacerlo de esta manera es que al mirar en cualquier lugar donde se almacena una referencia a esa cadena, es inmediatamente obvio de dónde proviene; FOO_BAZ es mucho menos ambiguo que 2 .

Además de eso, la otra cosa que ofende mis sensibilidades de Pythonic con respecto a la clase que propones es el uso de split() . ¿Por qué no solo pasar una tupla, lista u otro enumerable para comenzar?

La forma incorporada de hacer enumeraciones es:

 (FOO, BAR, BAZ) = range(3) 

que funciona bien para conjuntos pequeños, pero tiene algunos inconvenientes:

  • Necesitas contar el número de elementos a mano.
  • no puedes saltarte valores
  • Si agrega un nombre, también necesita actualizar el número de rango.

Para una implementación de enumeración completa en python, consulte: http://code.activestate.com/recipes/67107/

Comencé con algo que se parece mucho a la respuesta de S.Lott, pero solo sobrecargué ‘str’ y ‘eq’ (en lugar de toda la clase de objeto) para poder imprimir y comparar el valor de la enumeración.

 class enumSeason(): Spring = 0 Summer = 1 Fall = 2 Winter = 3 def __init__(self, Type): self.value = Type def __str__(self): if self.value == enumSeason.Spring: return 'Spring' if self.value == enumSeason.Summer: return 'Summer' if self.value == enumSeason.Fall: return 'Fall' if self.value == enumSeason.Winter: return 'Winter' def __eq__(self,y): return self.value==y.value 

Imprimir (x) proporcionará el nombre en lugar del valor y dos valores que contengan Spring serán iguales entre sí.

  >>> x = enumSeason(enumSeason.Spring) >>> print(x) Spring >>> y = enumSeason(enumSeason.Spring) >>> x == y True