Skip to main content

Enumeraciones en Python

⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.


En Python, las enumeraciones se manejan con la clase Enum del módulo enum. Aunque ambas plataformas ofrecen una forma estructurada de trabajar con valores predefinidos, hay diferencias clave en las funcionalidades y en cómo se utilizan las enumeraciones en cada lenguaje.

📌 Definición y Uso Básico

En Python, se pueden definir enumeraciones de la siguiente manera:

from enum import Enum

class DeliveryState(Enum):
PENDING = 0
PAID = 1
SHIPPED = 2
DELIVERED = 3
CANCELLED = 4

Aquí, DeliveryState es una enumeración que contiene los mismos valores que definimos en Kotlin. Sin embargo, hay algunas diferencias importantes:

  • Tipado Estático vs. Dinámico: A diferencia de Kotlin, Python es un lenguaje de tipado dinámico, lo que significa que no se tiene la misma garantía de seguridad de tipos en tiempo de compilación. En Kotlin, el compilador asegura que solo se usen valores válidos de la enumeración, mientras que en Python, los errores relacionados con valores no válidos se detectan en tiempo de ejecución.
  • No Exhaustividad: En Python, no hay un mecanismo directo que garantice que se manejan todos los casos de una enumeración. En Kotlin, when asegura la exhaustividad, pero en Python, el uso de una sentencia if-else no tiene esta garantía automática.

🚀 Uso de auto() en Enumeraciones

Python proporciona enum.auto(), una forma conveniente de asignar valores automáticamente a los miembros de una enumeración sin necesidad de definirlos manualmente.

¿Cuándo usar auto()?

✔️ Si no necesitas valores específicos, pero sí una enumeración con valores únicos.
✔️ Si quieres evitar asignar valores manualmente para reducir errores y mejorar la legibilidad.
❌ No es útil si cada miembro debe tener un valor específico (por ejemplo, códigos HTTP o estados con valores predefinidos).

from enum import Enum, auto

class DeliveryState(Enum):
PENDING = auto()
PAID = auto()
SHIPPED = auto()
DELIVERED = auto()
CANCELLED = auto()

print(list(DeliveryState))
# Output: [<DeliveryState.PENDING: 1>, <DeliveryState.PAID: 2>, <DeliveryState.SHIPPED: 3>, <DeliveryState.DELIVERED: 4>, <DeliveryState.CANCELLED: 5>]

Detalles clave sobre auto()

  • Los valores asignados comienzan en 1 y aumentan secuencialmente.
  • No se puede combinar auto() con valores manuales dentro de la misma enumeración.
  • Es útil cuando solo interesa identificar cada estado sin importar su valor numérico.

🔧 Personalización de Métodos

En Python, se pueden agregar métodos a las enumeraciones, similar a Kotlin, pero la forma de hacerlo es un poco diferente:

from enum import Enum

class DeliveryState(Enum):
PENDING = 0
PAID = 1
SHIPPED = 2
DELIVERED = 3
CANCELLED = 4

def signal(self):
return {
DeliveryState.PENDING: "Order is pending",
DeliveryState.PAID: "Order is paid",
DeliveryState.SHIPPED: "Order is shipped",
DeliveryState.DELIVERED: "Order is delivered",
DeliveryState.CANCELLED: "Order is cancelled"
}[self]

🏗️ Implementación de Comportamientos Comunes en Python

Tanto en Kotlin como en Python, es posible definir métodos en las enumeraciones para devolver valores específicos o ejecutar acciones según el estado de la enumeración. Sin embargo, hay diferencias clave entre ambos lenguajes:

  • En Kotlin, se pueden definir métodos abstractos dentro de las enumeraciones, lo que obliga a cada valor a proporcionar una implementación específica.
  • En Python, esto no es posible de manera nativa. No se puede hacer que cada valor de la enumeración implemente un método de manera obligatoria.

En lugar de interfaces, en Python se puede usar herencia múltiple y mixins para compartir comportamientos comunes entre las enumeraciones.

📌 Ejemplo de herencia múltiple y mixins en enumeraciones

from enum import Enum

class Notifier:
def notify(self, subscriber, message):
print(f"Notifying {subscriber.__class__.__name__}: {message}")

class Storable:
def store(self):
print("Storing data")

class DeliveryState(Enum, Notifier, Storable):
PENDING = ("Order is pending", 0)
PAID = ("Order is paid", 1)
SHIPPED = ("Order is shipped", 2)
DELIVERED = ("Order is delivered", 3)
CANCELLED = ("Order is cancelled", 4)

def __init__(self, description, code):
self.description = description
self.code = code

def signal(self):
return self.description

def is_final_state(self):
return self in (self.DELIVERED, self.CANCELLED)

# Uso
state = DeliveryState.PAID
print(state.signal()) # Output: "Order is paid"
state.notify(object(), "Order is paid") # Output: "Notifying object: Order is paid"
state.store() # Output: "Storing data"
print(state.is_final_state()) # Output: False

🧐 ¿Por qué init() en una enumeración?

Python permite que cada miembro de una enumeración tenga valores adicionales asociados, lo que amplía su funcionalidad más allá de ser simples valores constantes.

En el ejemplo anterior, cada estado de DeliveryState almacena una descripción (description) y un código (code) dentro de la enumeración. Sin embargo, en la mayoría de los casos, las enumeraciones no requieren __init__() si solo representan valores constantes.

✅ Ejemplo sin __init__() (solo valores constantes)

from enum import Enum

class SimpleDeliveryState(Enum):
PENDING = 0
PAID = 1
SHIPPED = 2
DELIVERED = 3
CANCELLED = 4

📌 ¿Cuándo usar __init__()?

  • ✔️ Si necesitas almacenar información adicional en cada miembro de la enumeración (como en el caso de description y code).
  • Si solo necesitas valores constantes, __init__() es innecesario y puede hacer el código más complejo de lo necesario.

🔍 Acceso y Validación de Valores

En Kotlin, se puede validar y acceder a los valores de la enumeración con métodos como valueOf. Python ofrece una forma similar:

try:
state = DeliveryState["PAID"]
print(state)
except KeyError:
print("Invalid state")

Python utiliza KeyError si un valor no existe, mientras que en Kotlin se lanza una IllegalArgumentException. Ambos lenguajes requieren el manejo explícito de errores si se busca un valor no válido, pero Python no ofrece una función equivalente a entries para iterar directamente sobre todos los valores de la enumeración, como en Kotlin.

También es posible iterar sobre los valores de la enumeración en Python:

for state in DeliveryState:
print(state)

⚖️ Comparación Final

CaracterísticaKotlinPython
Tipado EstáticoGarantiza la seguridad de tipos en tiempo de compilación, asegurando que solo se utilicen valores válidos de la enumeración.Tipado dinámico, los errores relacionados con valores no válidos se detectan en tiempo de ejecución.
ExhaustividadEl uso de when asegura que se manejen todos los casos posibles, eliminando la necesidad de un bloque else y garantizando cobertura completa.No hay mecanismo directo para asegurar exhaustividad en sentencias if-else, lo que puede resultar en casos no manejados sin advertencia.
Métodos AbstractosPermite definir métodos abstractos en las enumeraciones, obligando a que cada valor proporcione una implementación específica.No hay soporte directo para métodos abstractos en las enumeraciones, por lo que no se puede imponer la implementación específica por valor.
Interfaces vs MixinsUtiliza interfaces para compartir comportamientos comunes, proporcionando una estructura clara y segura en tiempo de compilación.Utiliza herencia múltiple y mixins, lo que da flexibilidad pero puede introducir complejidad y conflictos si no se maneja con cuidado.
Acceso a ValoresOfrece valueOf para buscar valores de la enumeración de manera segura y entries para iterar sobre todos los valores disponibles.Ofrece acceso mediante [], pero no incluye una función nativa como entries para listar todos los valores de la enumeración.

✅ Ventajas y Desventajas de Enumeraciones en Python

Beneficios

  • Flexibilidad: La herencia múltiple y los mixins permiten una combinación flexible de comportamientos para las enumeraciones, dando más opciones de personalización.
  • Simplicidad: Definir y utilizar enumeraciones en Python es directo, lo que facilita su uso para casos simples y rápidos sin mucha configuración adicional.
  • Compatibilidad con Tipos Anidados: Python permite asociar fácilmente datos adicionales a cada valor de la enumeración a través de constructores, similar a Kotlin.

Limitaciones

  • Falta de Control en Tiempo de Compilación: El tipado dinámico y la falta de exhaustividad en Python pueden resultar en errores que solo se detectan en tiempo de ejecución, aumentando el riesgo de errores no manejados.
  • Ausencia de Métodos Abstractos en Enumeraciones: Python no obliga a que cada valor de la enumeración implemente métodos específicos, lo que puede llevar a comportamientos inconsistentes si no se manejan con cuidado.
  • Complejidad con Herencia Múltiple: Aunque la herencia múltiple y los mixins ofrecen flexibilidad, también introducen la posibilidad de conflictos y complicaciones en el código si se utilizan en exceso o sin control.

🎯 Conclusiones

📌 Puntos clave

  • 🔒 Seguridad de tipos y exhaustividad
    • Kotlin garantiza seguridad de tipos en tiempo de compilación y exige que todas las ramas de un when sean manejadas.
    • Python, al ser de tipado dinámico, permite más flexibilidad, pero los errores se detectan solo en tiempo de ejecución.
  • ⚡ Uso de enum.auto()
    • Permite asignar valores automáticamente en Python, similar a cómo Kotlin define enum class sin valores explícitos.
    • Evita errores humanos al asignar valores manualmente.
  • 🛠 Métodos y comportamientos en enumeraciones
    • Kotlin permite definir métodos abstractos en enumeraciones, asegurando que cada valor proporcione una implementación específica.
    • Python no soporta métodos abstractos en enums, pero se puede lograr un comportamiento similar con herencia múltiple y mixins.
  • 🏗 __init__() en Python
    • No es obligatorio, pero útil si cada valor de la enumeración necesita almacenar datos adicionales, como un código numérico o descripción.
  • 🔗 Acceso y validación de valores
    • Kotlin usa valueOf() para convertir strings en valores de la enumeración, lanzando una IllegalArgumentException si el valor no es válido.
    • Python usa DeliveryState["PAID"], pero lanza un KeyError si el valor no existe.

🚀 Reflexión final

Ambos lenguajes ofrecen soluciones potentes para definir y trabajar con enumeraciones, pero su enfoque difiere:

  • Kotlin prioriza seguridad y estructura, asegurando que las enumeraciones sean exhaustivas y tipadas en tiempo de compilación.
  • Python enfatiza flexibilidad y dinamismo, permitiendo personalización avanzada con herencia múltiple y mixins, aunque con menos garantías en tiempo de compilación.

La elección entre Kotlin y Python para manejar enumeraciones dependerá del contexto del proyecto:
✔️ Si se necesita seguridad y exhaustividad → Kotlin es la mejor opción.
✔️ Si se busca flexibilidad y personalización avanzada → Python ofrece más herramientas para adaptar las enumeraciones a diferentes necesidades.