intflag - python enumerate
Python, ¿para qué sirve el tipo Enum? (1)
¿Cuál es el propósito de enums? ¿Qué valor crean para el idioma? ¿Cuándo debo usarlos y cuándo debo evitarlos?
El tipo Enum entró en Python a través de PEP 435 . El razonamiento dado es:
Las propiedades de una enumeración son útiles para definir un conjunto de valores constantes inmutables y relacionados que pueden tener o no un significado semántico.
Cuando se usan números y cadenas para este propósito, podrían caracterizarse como "números mágicos" o "cadenas mágicas". Los números rara vez llevan consigo la semántica, y las cadenas se confunden fácilmente (¿mayúsculas? ¿Ortografía? ¿Serpiente o caja de camello?)
Los días de la semana y los grados de las cartas escolares son ejemplos de este tipo de colecciones de valores.
Aquí hay un ejemplo de la docs :
from enum import Enum
class Color(Enum):
red = 1
green = 2
blue = 3
Al igual que la clase básica, esto es mucho más fácil de leer y elegante que el ejemplo del ejemplo, también es inmutable y tiene otros beneficios como veremos a continuación.
Estrictamente dominante: el tipo de miembro de la enumeración es la enumeración
>>> type(Color.red)
<enum ''Color''>
>>> isinstance(Color.green, Color)
True
Esto le permite definir la funcionalidad de los miembros en la definición Enum. La definición de la funcionalidad en los valores se podría lograr con los otros métodos anteriores, pero sería muy poco elegante.
Mejora: coerción de cuerdas
La representación de la cadena es legible por humanos, mientras que la repr tiene más información:
>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>
Me parece que esto es una mejora con respecto a los números mágicos e incluso posiblemente mejor que las cadenas del tupo nombrado.
Iteración (paridad):
La enumeración admite la iteración (como la tupla nombrada, pero no tanto la clase básica) también:
>>> for color in Color:
print(color)
Color.red
Color.green
Color.blue
El atributo __members__
es un OrderedDict
los nombres de las enumeraciones a sus respectivos objetos de enumeración (similar a la función _asdict()
de _asdict()
).
Soportado por pickle (paridad)
Puede serializar y deserializar la enumeración (en caso de que alguien esté preocupado por esto):
>>> import pickle
>>> color.red is pickle.loads(pickle.dumps(color.red))
True
Mejora: Alias
Esta es una buena característica que la clase básica no tiene, y sería difícil decir que el alias estaba allí en el grupo con namedtuple
.
class Color(Enum):
red = 1
green = 2
blue = 3
really_blue = 3
El alias viene después del nombre canónico, pero ambos son iguales:
>>> Color.blue is Color.really_blue
True
Si se deben prohibir los alias para evitar colisiones de valor, use el decorador enum.unique
(una característica estrictamente dominante).
Estrictamente dominante: las comparaciones hechas con is
La enumeración que se pretende probar con is
, que es una comprobación rápida de la identidad de un solo objeto en el proceso.
>>> Color.red is Color.red
True
>>> Color.red is Color.blue
False
>>> Color.red is not Color.blue
True
Las pruebas de igualdad también funcionan, pero las pruebas de identidad son óptimas.
Semántica diferente de otras clases de Python.
Las clases de enumeración tienen diferentes semánticas de los tipos regulares de Python Los valores de Enum son instancias de Enum, y son singletons en memoria para esos valores; no hay otro propósito para crear una instancia de ellos.
>>> Color.red is Color(''red'')
Es importante tener esto en cuenta, quizás sea un inconveniente, pero comparar en esta dimensión es comparar manzanas con naranjas.
Enums no se supone que sean ordenados
Mientras que la clase Enum sabe en qué orden se crean los miembros, no se asume que los enums estén ordenados. Esta es una característica porque muchas cosas que se pueden enumerar no tienen un orden natural y, por lo tanto, el orden sería arbitrario.
Sin embargo, puede ordenar su enumeración (vea la siguiente sección).
Subclases
No puede subclasificar un Enum con miembros declarados, pero puede subclasificar un Enum que no declara que los miembros compartan comportamientos (consulte la receta OrderedEnum en los docs ).
Esta es una característica: tiene poco sentido subclasificar un Enum con miembros, pero nuevamente, la comparación es manzanas y naranjas.
¿Cuándo debo usar enum.Enum
?
Esta es la nueva enumeración canónica en Python. Los colaboradores esperarán que sus enums se comporten como estos enums.
Úselo en cualquier lugar donde tenga una fuente canónica de datos enumerados en su código donde desee que se especifique explícitamente el uso del nombre canónico, en lugar de datos arbitrarios.
Por ejemplo, si en su código desea que los usuarios Color.green
que no es "Green"
, "green"
, 2 o "Greene"
, sino Color.green
, use el objeto enum.Enum. Es tanto explícito como específico.
Hay muchos ejemplos y recetas en la documentation .
¿Cuándo debo evitarlos?
Deja de enrollarte o deja que la gente adivine sobre números mágicos y cadenas. No los evites. Abrázalos
Sin embargo, si se requiere que los miembros de su enumeración sean enteros por razones históricas, existe el IntEnum
del mismo módulo, que tiene el mismo comportamiento, pero también es un entero porque IntEnum
una subclase del int
interno antes de subclasificar Enum
. De la ayuda de IntEnum
:
class IntEnum(builtins.int, Enum)
podemos ver que los valores de IntEnum se probarían como una instancia de un int
.
Esta pregunta ya tiene una respuesta aquí:
- Enumeración de Python, ¿cuándo y dónde usar? 2 respuestas
En Python 3.4, obtuvimos un Enum lib en la biblioteca estándar: enum
. Podemos obtener un backport para enum
que funcione con Python 2.4 a 2.7 (e incluso 3.1 a 3.3), enum34 en pypi.
Pero nos las hemos arreglado durante bastante tiempo sin este nuevo módulo. ¿Por qué ahora lo tenemos?
Tengo una idea general sobre el propósito de las enumeraciones de otros idiomas. En Python, ha sido común usar una clase simple de la siguiente manera y referirse a esto como una enumeración:
class Colors:
blue = 1
green = 2
red = 3
Esto se puede usar en una API para crear una representación canónica del valor, por ejemplo:
function_of_color(Colors.green)
Si esto tiene alguna crítica, es mutable, no se puede iterar (fácilmente) y ¿cómo podemos saber la semántica del entero, 2
?
Entonces supongo que podría usar algo como un tupla con nombre, ¿cuál sería inmutable?
>>> Colors = namedtuple(''Colors'', ''blue green red'')
>>> colors = Colors(''blue'', ''green'', ''red'')
>>> colors
Colors(blue=''blue'', green=''green'', red=''red'')
>>> list(colors)
[''blue'', ''green'', ''red'']
>>> len(colors)
3
>>> colors.blue
''blue''
>>> colors.index(colors.blue)
0
La creación de la tupla nombrada es un poco redundante (tenemos que escribir cada nombre dos veces) y, por lo tanto, poco elegante. Obtener el "número" del color también es poco elegante (tenemos que escribir los colors
dos veces). La comprobación del valor deberá realizarse con cadenas, que serán un poco menos eficientes.
Así que de vuelta a las enumeraciones.
¿Cuál es el propósito de enums? ¿Qué valor crean para el idioma? ¿Cuándo debo usarlos y cuándo debo evitarlos?