Las enumeraciones (Enums) son una característica poderosa de Python que permite definir conjuntos fijos de valores con nombre. Desde su introducción en Python 3.4 mediante la PEP 435, el módulo enum se ha convertido en esencial para escribir código más expresivo, seguro y fácil de mantener.
En esta guía completa, aprenderás desde conceptos básicos hasta técnicas avanzadas de Enum en Python, con ejemplos prácticos que puedes aplicar inmediatamente en tus proyectos.
🔷 ¿Qué Son las Enumeraciones?
Una enumeración es un tipo de dato que consiste en un conjunto fijo de valores con nombre llamados miembros. En lugar de usar cadenas sueltas o números mágicos dispersos por el código, defines un Enum que centraliza y da significado a esos valores.
Por ejemplo, en vez de usar cadenas como "lunes", "martes" propensas a errores ortográficos, puedes crear:
from enum import Enum
class DiaSemana(Enum):
LUNES = 1
MARTES = 2
MIERCOLES = 3
JUEVES = 4
VIERNES = 5
SABADO = 6
DOMINGO = 7
Ahora, en lugar de comparar cadenas propensas a errores, usas DiaSemana.LUNES, garantizando que solo se utilicen valores válidos.
📖 ¿Por Qué Usar Enum?
Las enumeraciones aportan varios beneficios a tu código:
- Legibilidad: nombres significativos reemplazan números mágicos o cadenas sueltas
- Seguridad: previene errores tipográficos y valores inválidos
- Mantenibilidad: cambios centralizados en un solo lugar
- Autodocumentación: el código expresa claramente sus intenciones
- Iteración: puedes recorrer todos los miembros fácilmente
- Comparación: los miembros se pueden comparar por identidad
Según la documentación oficial de Python, los Enum son ideales para representar valores fijos como estados, categorías, direcciones, colores y cualquier conjunto finito de opciones.
🚀 Creando Tu Primer Enum
Vamos a crear un Enum para representar el estado de un pedido en un sistema de e-commerce:
from enum import Enum
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 3
ENTREGADO = 4
CANCELADO = 5
Accediendo a miembros
print(EstadoPedido.PENDIENTE) # EstadoPedido.PENDIENTE
print(EstadoPedido.PENDIENTE.name) # 'PENDIENTE'
print(EstadoPedido.PENDIENTE.value) # 1
Cada miembro del Enum tiene dos atributos principales:
.name: devuelve el nombre del miembro como cadena.value: devuelve el valor asignado al miembro
También puedes crear Enum usando la API Funcional, útil para Enum simples:
from enum import Enum
Color = Enum('Color', ['ROJO', 'AZUL', 'VERDE'])
print(Color.ROJO) # Color.ROJO
print(list(Color)) # [Color.ROJO, Color.AZUL, Color.VERDE]
🎯 Accediendo a Miembros por Nombre y Valor
Python permite acceder a los miembros tanto por nombre como por valor:
from enum import Enum
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
Acceso por nombre
estado = EstadoPedido['PENDIENTE']
print(estado) # EstadoPedido.PENDIENTE
Acceso por valor
estado = EstadoPedido(2)
print(estado) # EstadoPedido.CONFIRMADO
Esta flexibilidad es extremadamente útil al integrar con bases de datos o APIs que devuelven valores numéricos.
🔄 Iterando Sobre Enums
Los Enum son iterables, permitiendo recorrer todos sus miembros fácilmente:
for estado in EstadoPedido:
print(f"{estado.name} = {estado.value}")
Salida:
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 3
ENTREGADO = 4
CANCELADO = 5
Esto es particularmente útil para poblar listas desplegables, generar informes o validar entradas.
🔧 Enum con auto()
La función auto() genera valores automáticamente, evitando la necesidad de numerar manualmente cada miembro:
from enum import Enum, auto
class EstadoPedido(Enum):
PENDIENTE = auto()
CONFIRMADO = auto()
ENVIADO = auto()
ENTREGADO = auto()
CANCELADO = auto()
print(list(EstadoPedido))
[EstadoPedido.PENDIENTE, EstadoPedido.CONFIRMADO, ...]
Verificando los valores generados
for estado in EstadoPedido:
print(f"{estado.name} = {estado.value}")
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 3
ENTREGADO = 4
CANCELADO = 5
Por defecto, auto() comienza en 1 e incrementa de 1 en 1, pero puedes personalizar este comportamiento sobrescribiendo _generate_next_value_.
🏷️ IntEnum: Enum con Comportamiento de Entero
IntEnum es una subclase de Enum que también hereda de int, permitiendo que los miembros se comporten como enteros:
from enum import IntEnum
class Prioridad(IntEnum):
BAJA = 1
MEDIA = 2
ALTA = 3
Se puede usar como entero
print(Prioridad.ALTA > Prioridad.MEDIA) # True
print(Prioridad.ALTA + Prioridad.BAJA) # 4
Puede sustituir enteros directamente
def procesar_ticket(prioridad: Prioridad):
if prioridad >= Prioridad.ALTA:
print("Prioridad alta! Procesar inmediatamente.")
procesar_ticket(3) # Funciona! IntEnum acepta comparación con int
IntEnum es perfecto cuando necesitas que los miembros funcionen en contextos que esperan enteros, como índices de lista o parámetros de funciones que requieren números.
🚩 Flag: Enum para Combinar Valores
Flag es ideal para representar combinaciones de opciones usando operaciones bit a bit:
from enum import Flag, auto
class Permiso(Flag):
LEER = auto()
ESCRIBIR = auto()
EJECUTAR = auto()
ELIMINAR = auto()
Combinando permisos
permiso_admin = Permiso.LEER | Permiso.ESCRIBIR | Permiso.EJECUTAR | Permiso.ELIMINAR
permiso_usuario = Permiso.LEER | Permiso.ESCRIBIR
Verificando permisos
if Permiso.LEER in permiso_usuario:
print("Usuario puede leer")
if Permiso.ELIMINAR not in permiso_usuario:
print("Usuario NO puede eliminar")
Inspeccionando miembros
print(Permiso.LEER) # Permiso.LEER
print(Permiso.LEER.value) # 1 (bits: 0001)
Flag es extremadamente útil para sistemas de permisos, configuraciones de funciones, filtros y cualquier escenario donde múltiples opciones pueden estar activas simultáneamente.
🧩 StrEnum: Enum con Comportamiento de Cadena
StrEnum (disponible desde Python 3.11) combina Enum con str:
from enum import StrEnum
class Color(StrEnum):
ROJO = 'rojo'
AZUL = 'azul'
VERDE = 'verde'
Se puede usar como cadena
print(Color.ROJO.upper()) # 'ROJO'
print(f"El color es {Color.AZUL}") # 'El color es azul'
print(Color.ROJO in 'rojo oscuro') # True
StrEnum es perfecto para integrar con bases de datos, APIs REST y archivos de configuración donde las cadenas son el formato estándar de intercambio de datos.
🧠 Métodos y Propiedades en Enums
Los Enum pueden tener métodos y propiedades personalizados, lo que los hace aún más potentes:
from enum import Enum
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 3
ENTREGADO = 4
CANCELADO = 5
@property
def descripcion(self):
descripciones = {
1: 'Esperando confirmación',
2: 'Pago confirmado',
3: 'En camino',
4: 'Entregado al cliente',
5: 'Pedido cancelado',
}
return descripciones[self.value]
def es_final(self):
return self in (EstadoPedido.ENTREGADO, EstadoPedido.CANCELADO)
Usando métodos y propiedades
estado = EstadoPedido.ENVIADO
print(estado.descripcion) # 'En camino'
print(estado.es_final()) # False
estado = EstadoPedido.ENTREGADO
print(estado.es_final()) # True
print(estado.descripcion) # 'Entregado al cliente'
Este enfoque mantiene la lógica relacionada centralizada dentro del Enum, siguiendo el principio de cohesión y facilitando el mantenimiento.
🔍 Comparación e Identidad
Los miembros de Enum son singleton, lo que significa que solo hay una instancia de cada miembro en memoria:
from enum import Enum
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
Comparación por identidad (recomendada)
print(EstadoPedido.PENDIENTE is EstadoPedido.PENDIENTE) # True
Comparación por igualdad
print(EstadoPedido.PENDIENTE == EstadoPedido.PENDIENTE) # True
print(EstadoPedido.PENDIENTE == EstadoPedido.CONFIRMADO) # False
Comparación con valor (NO recomendada)
print(EstadoPedido.PENDIENTE == 1) # False! (Excepto para IntEnum)
Siempre usa is o == para comparar miembros del mismo Enum. Evita comparar directamente con valores a menos que estés usando IntEnum.
🛡️ Valores Únicos y @unique
Por defecto, Python permite valores duplicados en Enum (los nombres duplicados se convierten en alias). Usa el decorador @unique para garantizar que todos los valores sean únicos:
from enum import Enum, unique
@unique
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 2 # Esto lanzaría ValueError!
ENVIADO = 3
Valores duplicados se permiten sin @unique
class ColorSinUnique(Enum):
ROJO = 1
ROJO_OSCURO = 1 # Alias de ROJO
print(ColorSinUnique(1)) # ColorSinUnique.ROJO (el primero es el principal)
Usa @unique siempre que la semántica de tu Enum exija que cada valor sea único. Esto previene errores sutiles causados por alias accidentales.
📋 Alias en Enums
Los alias son nombres diferentes que apuntan al mismo valor. Pueden ser útiles para proporcionar nombres alternativos:
from enum import Enum
class Color(Enum):
ROJO = 1
RED = 1 # Alias en inglés
AZUL = 2
BLUE = 2
print(Color.ROJO) # Color.ROJO
print(Color.RED) # Color.ROJO (alias)
print(Color(1)) # Color.ROJO (el primer nombre es el principal)
Listar solo miembros únicos (sin alias)
print(list(Color))
[Color.ROJO, Color.AZUL]
Los alias son especialmente útiles para mantener compatibilidad con sistemas heredados o proporcionar nombres en múltiples idiomas.
💡 Buenas Prácticas con Enums
- Usa siempre nombres en MAYÚSCULAS: por convención, los miembros de Enum se nombran en mayúsculas
- Usa auto() cuando sea posible: evita errores de numeración manual
- Prefiere @unique: garantiza que no haya valores duplicados accidentalmente
- Usa Enum en lugar de cadenas o enteros sueltos: para cualquier conjunto fijo de opciones
- Combina con Type Hints: tipado estático + Enum = código extremadamente seguro
- Maneja valores desconocidos: al recibir datos externos, usa
try/except ValueErrorpara capturar valores inválidos
⚠️ Manejo de Errores con Enums
Al recibir valores externos (de APIs, bases de datos o formularios), es crucial manejar entradas inválidas:
from enum import Enum
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 3
ENTREGADO = 4
CANCELADO = 5
def obtener_estado(codigo: int):
try:
return EstadoPedido(codigo)
except ValueError:
return None # O lanzar una excepción personalizada
Probando
print(obtener_estado(3)) # EstadoPedido.ENVIADO
print(obtener_estado(99)) # None
Esta técnica se integra perfectamente con el sistema de manejo de errores en Python, garantizando que tu código maneje elegantemente las entradas inesperadas.
🌐 Enums y Bases de Datos
Los Enum son excelentes para representar columnas con valores fijos en bases de datos. Así se integra con SQLAlchemy:
from enum import Enum
import sqlalchemy as sa
from sqlalchemy.orm import declarative_base, Mapped, mapped_column
class EstadoPedido(Enum):
PENDIENTE = 1
CONFIRMADO = 2
ENVIADO = 3
ENTREGADO = 4
CANCELADO = 5
Base = declarative_base()
class Pedido(Base):
tablename = 'pedidos'
id: Mapped[int] = mapped_column(primary_key=True)
estado: Mapped[EstadoPedido] = mapped_column(sa.Enum(EstadoPedido))
Esto garantiza integridad referencial tanto a nivel de aplicación como de base de datos simultáneamente.
📦 Enums en el Mundo Real
Los Enum se utilizan ampliamente en proyectos Python reales. Grandes frameworks como Django usan Enum extensivamente en sus modelos y configuraciones. El HOWTO oficial de Enum muestra ejemplos avanzados con OrderedEnum y otros patrones.
Aquí tienes un ejemplo más complejo de un sistema de logs con niveles:
from enum import IntEnum
from datetime import datetime
class NivelLog(IntEnum):
DEBUG = 10
INFO = 20
WARNING = 30
ERROR = 40
CRITICAL = 50
def formatear(self, mensaje: str) -> str:
timestamp = datetime.now().isoformat()
return f"[{timestamp}] [{self.name}] {mensaje}"
Uso
log = NivelLog.INFO.formatear("Sistema iniciado correctamente")
print(log)
[2026-05-22T09:00:00] [INFO] Sistema iniciado correctamente
📖 Lectura Recomendada
- Documentación oficial del módulo enum - La referencia definitiva
- Enum HOWTO - Guía práctica oficial
- PEP 435 - La especificación original de Enum en Python
- Real Python: Python Enum - Tutorial completo
- Enum.auto() - Documentación sobre valores automáticos
- IntEnum - Enum con comportamiento de entero
- Flag - Enum para combinaciones bit a bit
- Glosario Python: Enum - Definición en el glosario oficial
✅ Conclusión
Las enumeraciones en Python son mucho más que simples listas de constantes. Proporcionan una manera elegante y segura de representar conjuntos fijos de valores, haciendo tu código más legible, fácil de mantener y menos propenso a errores.
Desde lo básico con Enum y auto() hasta variantes potentes como IntEnum, Flag y StrEnum, el módulo enum ofrece herramientas para prácticamente cualquier escenario.
Ahora que dominas los Enum en Python, comienza a aplicarlos en tus proyectos. Reemplaza esas cadenas sueltas y números mágicos por enumeraciones bien definidas. ¡Tu código (y tus compañeros de equipo) te lo agradecerán! 🐍