Aprende leyendo en orden

enum y dataclasses — Constantes con nombre y clases de datos

Aprende a sustituir cadenas literales por Enum, numerar y ordenar con IntEnum + auto(), generar __init__ / __eq__ / __repr__ con @dataclass y usar field(default_factory=list).

Dos módulos para hacer explícito el significado de los valores. enum te permite reemplazar literales de cadena dispersos por todo tu código con constantes con nombre, así que los errores de tipeo aparecen en tiempo de ejecución en lugar de pasar silenciosamente. dataclasses proporciona una manera de definir una "clase que solo guarda datos" en una línea, eliminando el boilerplate de __init__ / __eq__ / __repr__.

Enum — reemplaza literales de cadena con constantes con nombre

Cuando literales de cadena como "paid" / "shipped" están dispersos por tu código, errores de tipeo como "shippped" se cuelan hasta tiempo de ejecución, el autocompletado no ayuda y los refactors son frágiles. Enum soluciona esto definiendo el conjunto de constantes como una clase — los nombres que defines se autocompletan en tu editor, los errores de tipeo lanzan AttributeError inmediatamente, la iteración sobre todos los miembros funciona desde el principio, y obtienes un valor seguro para usar como clave de dict o etiqueta de switch.

# Cadenas de estado dispersas por el código
if status == "paid":
    ship_order(order_id)
elif status == "shippped":   # Typo — debería ser "shipped", pero no lo notarás hasta tiempo de ejecución
    notify_shipped(order_id)
elif status == "cancelled":
    refund(order_id)
Literales de cadena vs. Enum
if status == "paid":(literal de cadena)Typo "paied"no lanza excepción→ El bug queda ocultoif status == OrderStatus.PAID:(Enum)OrderStatus.PAEID→ AttributeError→ Detectado al instante
Las cadenas planas no se autocompletan y los errores de tipeo se cuelan hasta tiempo de ejecución. Enum solo permite los nombres que has definido, así que obtienes autocompletado, detección de errores de tipeo e iteración sobre todos los miembros de una sola vez.
from enum import Enum

class OrderStatus(Enum):
    PENDING = "pending"
    PAID = "paid"
    SHIPPED = "shipped"
    CANCELLED = "cancelled"

# Acceso al miembro: cada miembro lleva un name y un value
print(OrderStatus.PAID)            # OrderStatus.PAID
print(OrderStatus.PAID.name)       # 'PAID'
print(OrderStatus.PAID.value)      # 'paid'

# Compara con ==
status = OrderStatus.PAID
if status == OrderStatus.PAID:
    print("Pago recibido")

Define un estado de pedido como un Enum y lee la información de los miembros.

① Importa Enum desde el módulo enum

② Define una clase Enum OrderStatus con los cuatro miembros PENDING / PAID / SHIPPED / CANCELLED (los valores son las cadenas en minúsculas "pending" / "paid" / "shipped" / "cancelled")

③ Imprime OrderStatus.PAID.name como PAID name: ◯◯

④ Imprime OrderStatus.PAID.value como PAID value: ◯◯

⑤ Imprime todos los nombres de miembros como All members: ◯ (puedes iterar con for s in OrderStatus)

(Si tu código se ejecuta correctamente, aparecerá la explicación.)

Editor Python

Ejecutar el código para ver el resultado

Construye una función que compare miembros de Enum en if/elif y devuelva un mensaje por estado. Ramificar sobre igualdad miembro a miembro en lugar de literales de cadena es la forma más segura de escribirlo.

① Define OrderStatus de la Práctica 1

② Define una función get_message(status) que use if/elif sobre cada miembro para devolver: PENDING → "Esperando el pago", PAID → "Preparando el envío", SHIPPED → "Enviado", CANCELLED → "Cancelado"

③ Imprime get_message(OrderStatus.PAID) como PAID: ◯

④ Imprime get_message(OrderStatus.SHIPPED) como SHIPPED: ◯

Editor Python

Ejecutar el código para ver el resultado

IntEnum y auto — numeración automática y comparación numérica

IntEnum es un Enum que hereda de int, así que los miembros se comparan y operan aritméticamente como enteros. Es la elección correcta para constantes donde el orden numérico importa — prioridades, niveles, rangos. auto() es una función que asigna valores automáticamente en lugar de escribirlos a mano: alinea las llamadas auto() y se convierten en 1, 2, 3, 4... en orden de definición.

Cuando los números específicos no importan (solo importa el orden), auto() es más seguro porque añadir o eliminar miembros no requiere reescribir números a mano.

IntEnum + auto
class Priority(IntEnum): LOW = auto() MEDIUM = auto()auto() numera auto1, 2, 3Priority.LOW < Priority.HIGH→ True (compara int)
auto() numera automáticamente 1, 2, 3, .... IntEnum se compara como int, así que Priority.LOW < Priority.HIGH funciona — el orden numérico codifica la prioridad.

Define un IntEnum Priority con auto() y ordena varias tareas de mayor a menor prioridad. Confirma en un escenario realista que auto() numera automáticamente y que IntEnum puede comparar y ordenar como int.

① Importa IntEnum y auto desde enum

② Define una clase IntEnum Priority con cuatro miembros LOW / MEDIUM / HIGH / URGENT (todos los valores auto())

③ Construye una lista de todos los valores de miembros e imprime como Numeración: ◯ (confirmando que auto() asignó 1, 2, 3, 4)

④ Ordena la lista de tareas [("Limpieza", Priority.LOW), ("Responder emails", Priority.HIGH), ("Preparar documentos", Priority.URGENT), ("Revisar", Priority.MEDIUM)] de mayor a menor prioridad, luego bajo el encabezado Orden de ejecución: imprime cada tarea con la forma - nombre (prioridad)

Editor Python

Ejecutar el código para ver el resultado

@dataclass — define una clase que guarda datos en una línea

Escribir una "clase plana solo con campos" a mano significa definir __init__ para fijar atributos, __eq__ (el método dunder llamado para las comparaciones ==) para igualdad por contenido, __repr__ (el método dunder llamado por print y el REPL) para una visualización legible, etc. — el mismo patrón boilerplate alineándose cada vez. Un solo decorador @dataclass genera todo eso automáticamente a partir de tus declaraciones de campo con type hints.

En otras palabras, solo alinear atributos con type hints construye toda la clase — obtienes las mismas funcionalidades con mucho menos código que las clases escritas a mano.

Qué autogenera @dataclass
@dataclassclass Order: id: int customer: str→ Autogenerado__init__Inicializa atributos__eq__== si todos los campos coinciden__repr__Order(id=..., customer=...)
Solo declara campos con type hints y obtienes __init__ / __eq__ / __repr__ gratis. field(default_factory=list) te permite fijar de forma segura valores por defecto mutables como una lista vacía, y frozen=True vuelve la clase inmutable (cambios de atributo bloqueados).

Usa default_factory para valores por defecto mutables

Intentar dar como valor por defecto una lista con @dataclass escribiendo items: list = [] lanza un SyntaxError (o un aviso de deprecación). Es una protección contra el bug clásico de "todas las instancias terminan compartiendo la misma lista". Usa field(default_factory=list) en su lugar, que crea una lista vacía nueva por instancia, haciéndolo seguro. La misma idea para dict / set: field(default_factory=dict).

Define un Order con @dataclass y confirma el __eq__ y __repr__ autogenerados.

① Importa dataclass y field desde dataclasses

② Define una clase Order decorada con @dataclass — cuatro campos: id: int, customer: str, items: list = field(default_factory=list), is_paid: bool = False

③ Construye una instancia con Order(id=1234, customer="Ana", items=["apple", "banana"]) e imprímela directamente (se activa el __repr__ autogenerado)

④ Construye una segunda instancia con el mismo contenido e imprime el resultado de la comparación con == como Iguales: True / False

⑤ Cambia el is_paid de la primera instancia a True, luego compara con == de nuevo e imprime como Iguales tras cambio: True / False

Editor Python

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1¿Cuál es la ventaja de reemplazar la comparación con literal de cadena "paid" por un Enum?

Pregunta 2¿Cuál es la ventaja de auto() en IntEnum?

Pregunta 3¿Cuál es la forma correcta de dar por defecto una lista a un campo en @dataclass?