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 ValueError para 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

✅ 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! 🐍