¿Cómo puedo representar un 'Enum' en Python?
Soy principalmente 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 se ha realizado una copia de seguridad 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, pruebe 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, lo necesitará __order__
en python 2 ).
- Para usar
enum34
, haz$ pip install enum34
- Para usar
aenum
, haz$ pip install aenum
La instalación 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: 1>
Animal['ant'] # returns <Animal.ant: 1> (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 realizar enumeraciones es:
def enum(**enums):
return type('Enum', (), enums)
que se usa así:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
También puedes 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 usado así:
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
Se puede agregar soporte para convertir los valores nuevamente a nombres 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á un KeyError
si el mapeo inverso no existe. Con el primer ejemplo:
>>> Numbers.reverse_mapping['three']
'THREE'
Si está utilizando MyPy, otra forma de expresar "enumeraciones" es con typing.Literal
.
Por ejemplo:
from typing import Literal #python >=3.8
from typing_extensions import Literal #python 2.7, 3.4-3.7
Animal = Literal['ant', 'bee', 'cat', 'dog']
def hello_animal(animal: Animal):
print(f"hello {animal}")
hello_animal('rock') # error
hello_animal('bee') # passes
Antes de PEP 435, Python no tenía un equivalente pero podías implementar el tuyo propio.
A mí me gusta mantenerlo simple (he visto algunos ejemplos terriblemente complejos en la red), algo como esto...
class Animal:
DOG = 1
CAT = 2
x = Animal.DOG
En Python 3.4 ( PEP 435 ), puedes hacer de Enum la clase base. Esto le brinda un poco de funcionalidad adicional, descrita en el PEP. Por ejemplo, los miembros de enum son distintos de los números enteros y están compuestos por a name
y a value
.
from enum import Enum
class Animal(Enum):
DOG = 1
CAT = 2
print(Animal.DOG)
# <Animal.DOG: 1>
print(Animal.DOG.value)
# 1
print(Animal.DOG.name)
# "DOG"
Si no desea escribir los valores, utilice el siguiente acceso directo:
class Animal(Enum):
DOG, CAT = range(2)
Enum
las implementaciones se pueden convertir en listas y son iterables . El orden de sus miembros es el orden de declaración y no tiene nada que ver con sus valores. Por ejemplo:
class Animal(Enum):
DOG = 1
CAT = 2
COW = 0
list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]
[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)