sofros
Para encapsular una lista de estados que estoy usando enum
módulo:
from enum import Enum
class MyEnum(Enum):
state1='state1'
state2 = 'state2'
state = MyEnum.state1
MyEnum['state1'] == state # here it works
'state1' == state # here it does not throw but returns False (fail!)
Sin embargo, el problema es que necesito usar sin problemas los valores como cadenas en muchos contextos en mi secuencia de comandos, como:
select_query1 = select(...).where(Process.status == str(MyEnum.state1)) # works but ugly
select_query2 = select(...).where(Process.status == MyEnum.state1) # throws exeption
Cómo hacerlo evitando llamar a conversión de tipo adicional (str(state)
arriba) o el valor subyacente (state.value
)?
sofros
Parece que basta con heredar de str
clase al mismo tiempo que Enum
:
from enum import Enum
class MyEnum(str, Enum):
state1 = 'state1'
state2 = 'state2'
La parte complicada es que el orden de clases en la cadena de herencia es importante como esto:
class MyEnum(Enum, str):
state1 = 'state1'
state2 = 'state2'
lanza:
TypeError: new enumerations should be created as `EnumName([mixin_type, ...] [data_type,] enum_type)`
Con la clase correcta las siguientes operaciones en MyEnum
estan bien:
print('This is the state value: ' + state)
Como nota al margen, parece que el truco de herencia especial no es necesario para cadenas formateadas que funcionan incluso para Enum
solo herencia:
msg = f'This is the state value: {state}' # works without inheriting from str
-
Documentos relevantes: docs.python.org/3/library/enum.html#restricted-enum-subclassing
– Aran Fey
29 oct 2019 a las 13:34
-
Tenga en cuenta que aa
str
mixin puede tener un efecto secundario no deseado, por ejemplo, en el comportamiento de (des) serialización; consulte, por ejemplo, stackoverflow.com/q/65339635/10009545– koks der drache
17 de diciembre de 2020 a las 12:55
elyasaf755
Al leer la documentación (es decir, no lo probé porque uso una versión anterior de Python, pero confío en los documentos), ya que Pitón 3.11 puedes hacer lo siguiente:
from enum import StrEnum
class Directions(StrEnum):
NORTH = 'north', # notice the trailing comma
SOUTH = 'south'
print(Directions.NORTH)
>>> north
por favor refiérase a documentos y el discusión de diseño para una mayor comprensión.
si estas corriendo pitón 3.6+ejecutar pip install StrEnum
y luego puedes hacer lo siguiente (confirmado por mí):
from strenum import StrEnum
class URLs(StrEnum):
GOOGLE = 'www.google.com'
STACKOVERFLOW = 'www.stackoverflow.com'
print(URLs.STACKOVERFLOW)
>>> www.stackoverflow.com
Puedes leer más al respecto aquí.
Además, esto se mencionó en los documentos: cómo crear sus propias enumeraciones basadas en otras clases:
Si bien IntEnum es parte del módulo enum, sería muy simple de implementar de forma independiente:
class IntEnum(int, Enum): pass Esto demuestra cómo se pueden definir enumeraciones derivadas similares; por ejemplo, un StrEnum que se mezcla en str en lugar de int.
Algunas reglas:
Al subclasificar Enum, los tipos de combinación deben aparecer antes que Enum en la secuencia de bases, como en el ejemplo anterior de IntEnum.
Si bien Enum puede tener miembros de cualquier tipo, una vez que mezcle un tipo adicional, todos los miembros deben tener valores de ese tipo, por ejemplo, int arriba. Esta restricción no se aplica a los complementos que solo agregan métodos y no especifican otro tipo.
Cuando se mezcla otro tipo de datos, el atributo de valor no es el mismo que el propio miembro de la enumeración, aunque es equivalente y se comparará igual.
Formato de estilo %: %s y %r llaman a la clase Enum calle() y
repetir() respectivamente; otros códigos (como %i o %h para IntEnum) tratan el miembro de enumeración como su tipo mixto.Los literales de cadena formateados, str.format() y format() utilizarán los tipos combinados formato() a no ser que calle() o formato() se anula en la subclase, en cuyo caso se utilizarán los métodos anulados o los métodos Enum. Use los códigos de formato !s y !r para forzar el uso de la clase Enum calle() y repetir() métodos.
-
es 3.11 para StrEnum
– Rohit
17 dic 2021 a las 16:41
-
Usemos valores de enumeración generados automáticamente en la serialización. Entonces podría romperse fácilmente actualizando a la versión con errores ‘strenum’. Que no vale la pena.
– Denis Barmenkov
8 de febrero a las 0:07
-
Do. No funciona en 3.7.3 🙁
– Mota Zart
22 abr a las 23:09
Mientras que una clase mixin entre str
y Enum
puede resolver este problema, siempre debe pensar también en obtener el herramienta adecuada para el trabajo.
Y, a veces, la herramienta adecuada podría ser simplemente un MODULE_CONSTANT con un valor de cadena. Por ejemplo, logging
tiene algunas constantes como DEBUG, INFO, etc. con valores significativos, incluso si son int
s en este caso.
Las enumeraciones son una buena herramienta y las uso a menudo. Sin embargo, están destinados a compararse principalmente con otros miembros del mismo Enum, razón por la cual compararlos, por ejemplo, con cadenas requiere que salte a través de un aro adicional.
-
Enum
se creó para que no se necesitaran constantes de módulos opacos.–Ethan Furman
29 oct 2019 a las 14:36
-
@EthanFurman Entonces, ¿por qué logging.DEBUG y sus amigos no están en desuso?
– ojo brillante
29 oct 2019 a las 14:38
-
No quedarían en desuso, sino que se reemplazarían por un correspondiente
IntEnum
. Es política estándar mantener la stdlib lo más estable posible, lo que significa no reescribirla al por mayor para aprovechar cada característica nueva. Hasta aquíhttp
,socket
yre
las constantes del módulo han sido reemplazadas (y tal vez un par más que no recuerdo en este momento).–Ethan Furman
29 de octubre de 2019 a las 14:48
-
-
¡Vaya, parece que ha pasado mucho tiempo desde que tuvimos esa conversación! Los puntos principales allí se relacionan con mi comentario anterior: no reescribir el stdlib sin una buena razón. El caso de las cuerdas mágicas es aún más difícil de hacer, porque las cuerdas generalmente se explican por sí mismas. Los módulos con más posibilidades de tener
Enum
las conversiones están orientadas al usuario con constantes enteras (comore
yhttp
). Además, una gran razón para no convertir partes específicas de stdlib es si se usó antesEnum
se puede importar Todas estas son razones que no afectan el código fuera de stdlib.–Ethan Furman
29 de octubre de 2019 a las 15:28
sha de mierda
Si los valores de cadena asociados son nombres de Python válidos, puede obtener los nombres de los miembros de la enumeración usando .name
propiedad como esta:
from enum import Enum
class MyEnum(Enum):
state1=0
state2=1
print (MyEnum.state1.name) # 'state1'
a = MyEnum.state1
print(a.name) # 'state1'
Si los valores de cadena asociados son cadenas arbitrarias, puede hacer esto:
class ModelNames(str, Enum):
gpt2 = 'gpt2'
distilgpt2 = 'distilgpt2'
gpt2_xl="gpt2-XL"
gpt2_large="gpt2-large"
print(ModelNames.gpt2) # 'ModelNames.gpt2'
print(ModelNames.gpt2 is str) # False
print(ModelNames.gpt2_xl.name) # 'gpt2_xl'
print(ModelNames.gpt2_xl.value) # 'gpt2-XL'
Prueba esto en línea: https://repl.it/@sytelus/enumstrtest
Si desea trabajar con cadenas directamente, podría considerar usar
MyEnum = collections.namedtuple(
"MyEnum", ["state1", "state2"]
)(
state1="state1",
state2="state2"
)
en lugar de enumerar en absoluto. Iterando sobre esto o haciendo MyEnum.state1
dará los valores de cadena directamente. Crear la tupla con nombre dentro de la misma declaración significa que solo puede haber una.
Obviamente, hay ventajas y desventajas por no usar Enum, por lo que depende de lo que valore más.
Gabriele Iannetti
¿Qué hay de malo en usar el valor?
En mi humilde opinión, a menos que use Python versión 3.11 con StrEnum, simplemente anulo el __str__(self)
método en la clase Enum adecuada:
class MyStrEnum(str, Enum):
OK = 'OK'
FAILED = 'FAILED'
def __str__(self) -> str:
return self.value
Mejor
pierre-vr
Simplemente use .value :
MyEnum.state1.value == 'state1'
# True
-
La pregunta establece explícitamente que llamar
.value
no es una solución aceptable.– Encantador de serpientes
14 de enero de 2021 a las 17:08
MyEnum.state1.value
?– Dan
29 oct 2019 a las 13:30
Lo siento pero esto es igual de feo que
str(state)
a mi…– sophros
29 oct 2019 a las 13:40
que tipo es
Testround.status
? ¿Podrías hacerlo de tipoMyEnum
?– Dan
29 oct 2019 a las 14:25
Su código de ejemplo
'state1' == state
está mal, esa comparación regresaFalse
.–Ethan Furman
29 oct 2019 a las 14:39
@EthanFurman: no arroja, pero de hecho el resultado está lejos de ser satisfactorio. ¡Gracias! De corrección.
– sophros
29 oct 2019 a las 15:03