StrEnum es una clase de la biblioteca estándar de Python diseñada para representar conjuntos cerrados de valores textuales. Combina el comportamiento de str con Enum, por lo que cada miembro funciona como una cadena real y, al mismo tiempo, conserva la organización, validación y claridad de una enumeración.
Este recurso es útil en APIs, configuraciones, serialización JSON, herramientas de línea de comandos, estados de aplicaciones e integraciones con bases de datos. En lugar de repetir cadenas mágicas por todo el proyecto, puedes definir los valores válidos una sola vez.
Por qué usar StrEnum
Imagina una aplicación que acepta los estados pending, running y done. Las cadenas simples funcionan al principio, pero cualquier error tipográfico puede generar comportamientos inconsistentes. Con StrEnum, el contrato queda explícito.
from enum import StrEnum
class Estado(StrEnum):
PENDING = "pending"
RUNNING = "running"
DONE = "done"
Estado.RUNNING es ahora un miembro de enumeración y también un valor compatible con cadenas.
Diferencia entre Enum y StrEnum
Un miembro de Enum tradicional no es automáticamente igual a su cadena subyacente. Un miembro de StrEnum sí lo es.
from enum import Enum, StrEnum
class ColorEnum(Enum):
RED = "red"
class ColorStr(StrEnum):
RED = "red"
print(ColorStr.RED == "red") # True
print(ColorEnum.RED == "red") # False
Esto reduce conversiones repetitivas con .value, aunque el valor bruto sigue disponible cuando deseas ser explícito.
Valores automáticos
StrEnum funciona con auto(). De forma predeterminada, el nombre del miembro se convierte en minúsculas.
from enum import StrEnum, auto
class Entorno(StrEnum):
DEVELOPMENT = auto()
STAGING = auto()
PRODUCTION = auto()
print(Entorno.PRODUCTION.value) # production
Este patrón es práctico cuando el valor público debe seguir directamente el nombre del miembro.
Uso en APIs
Las APIs REST intercambian texto en parámetros, solicitudes y respuestas. Una enumeración de strings mantiene esos valores estables y fáciles de documentar.
class Formato(StrEnum):
JSON = "json"
CSV = "csv"
XML = "xml"
def exportar(formato: Formato):
if formato is Formato.JSON:
return {"items": []}
if formato is Formato.CSV:
return "items\n"
return " "
Para ampliar este tema, consulta la guía sobre API REST con Python.
Conversión desde cadenas externas
Puedes construir un miembro utilizando su valor textual.
entrada = "running"
estado = Estado(entrada)
print(estado is Estado.RUNNING)
Si el valor no existe, Python lanza ValueError. En los límites de la aplicación conviene capturar el error y devolver un mensaje útil.
def leer_estado(texto: str) -> Estado:
try:
return Estado(texto)
except ValueError as error:
opciones = ", ".join(item.value for item in Estado)
raise ValueError(f"estado inválido; usa: {opciones}") from error
Serialización JSON
Como los miembros se comportan como strings, muchos codificadores JSON los serializan de forma natural.
import json
payload = {"estado": Estado.DONE}
print(json.dumps(payload))
Sin embargo, algunos frameworks hacen verificaciones estrictas y pueden exigir .value. Añade pruebas de integración para confirmar el comportamiento real.
Configuraciones y variables de entorno
Entornos, niveles de log, estrategias de caché y modos de ejecución son buenos candidatos para StrEnum.
class NivelLog(StrEnum):
DEBUG = "debug"
INFO = "info"
WARNING = "warning"
ERROR = "error"
Este enfoque combina bien con archivos de configuración y variables de entorno. Lee también el artículo sobre variables de entorno en Python.
Identidad e igualdad
Comparar un miembro con una cadena puede ser cómodo, pero dentro de la lógica de dominio suele ser más claro comparar miembros de la propia enumeración.
if estado is Estado.DONE:
print("proceso finalizado")
Convierte las cadenas externas al entrar en la aplicación y conserva el tipo fuerte internamente.
Iteración y opciones válidas
Las enumeraciones son iterables, lo que facilita generar documentación, opciones de CLI y mensajes de validación.
for estado in Estado:
print(estado.name, estado.value)
Así evitas mantener una segunda lista separada de valores permitidos.
Pattern matching
El pattern matching de Python funciona de forma clara con miembros de enum.
def describir(estado: Estado) -> str:
match estado:
case Estado.PENDING:
return "en espera"
case Estado.RUNNING:
return "en ejecución"
case Estado.DONE:
return "finalizado"
Consulta el tutorial de match case en Python para más ejemplos.
Type hints
Anotar parámetros con StrEnum ayuda a los analizadores estáticos y documenta mejor las interfaces.
def iniciar(entorno: Entorno) -> None:
print(f"iniciando en {entorno}")
La guía sobre type hints en Python explica cómo aprovechar estas anotaciones.
Aliases y valores duplicados
Si dos nombres reciben el mismo valor, el segundo suele convertirse en alias.
class Metodo(StrEnum):
GET = "get"
READ = "get"
Los aliases pueden ayudar en migraciones, pero también pueden ocultar errores. Úsalos de forma intencional y documenta cuál es el nombre principal.
Personalizar valores ausentes
Puedes implementar _missing_ para normalizar variantes controladas.
class Respuesta(StrEnum):
YES = "yes"
NO = "no"
@classmethod
def _missing_(cls, value):
if isinstance(value, str):
normalizado = value.strip().lower()
for item in cls:
if item.value == normalizado:
return item
return None
De esta manera, valores como " YES " pueden aceptarse sin repetir la lógica de normalización en muchos lugares.
Integración con bases de datos
Las enumeraciones de strings son cómodas para columnas de base de datos porque los valores almacenados siguen siendo legibles. Sin embargo, debes decidir si la restricción pertenece a la aplicación o a la base de datos.
Si los valores cambian con frecuencia o son administrados por usuarios, una tabla de referencia puede ser mejor que una enumeración definida en código.
Compatibilidad con versiones anteriores
StrEnum forma parte de versiones modernas de Python. Si el proyecto soporta intérpretes antiguos, puedes combinar str y Enum.
from enum import Enum
class EstadoCompat(str, Enum):
PENDING = "pending"
RUNNING = "running"
El comportamiento es parecido, pero la representación y la integración con frameworks pueden variar. Prueba las versiones que realmente soportas.
Pruebas recomendadas
Prueba conversiones válidas, valores inválidos, serialización, aliases, normalización y compatibilidad con frameworks web. La guía de pytest en Python muestra patrones útiles para pruebas parametrizadas.
Cuándo no usarlo
StrEnum no es ideal para valores dinámicos que cambian con frecuencia, dependen de usuarios o llegan desde una tabla administrable. Las enumeraciones requieren cambios de código y despliegues. Úsalas para vocabularios estables y cerrados.
Buenas prácticas
Convierte el texto externo en la frontera, conserva miembros de enum dentro del dominio, utiliza valores estables en minúsculas, evita aliases accidentales, documenta deprecaciones y prueba la serialización. Prefiere varias enumeraciones pequeñas y específicas antes que una clase gigante con conceptos no relacionados.
Referencias
Consulta la documentación oficial de StrEnum y la PEP 663 para comprender las decisiones de representación y comportamiento.
Conclusión
StrEnum es una herramienta práctica para modelar valores textuales controlados. Reduce strings mágicas, mejora la validación, funciona bien con JSON y APIs y ofrece contratos más claros para herramientas estáticas. Usado en dominios estables, hace que las aplicaciones Python sean más predecibles sin añadir complejidad innecesaria.







