Saltar al contenido principal
eLearner.app
Módulo 9 · Lección 2 de 434/36 en el curso~12 min
Lecciones del módulo (2/4)

dataclasses: clases de datos sin boilerplate

El decorador @dataclass (módulo dataclasses) genera automáticamente __init__, __repr__ y __eq__ para clases cuya única función es almacenar datos. Menos código repetitivo, código más claro.

Sin dataclass (código repetitivo)

Python
class Punto:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y
    def __repr__(self) -> str:
        return f"Punto(x={self.x}, y={self.y})"
    def __eq__(self, other) -> bool:
        if not isinstance(other, Punto):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

Con dataclass

Python
from dataclasses import dataclass

@dataclass
class Punto:
    x: float
    y: float

p = Punto(3, 4)
p             # Punto(x=3, y=4)   ← __repr__ gratis
Punto(3, 4) == Punto(3, 4)        # True ← __eq__ gratis
p.x, p.y      # 3, 4

El patrón: declarar campos como anotaciones de clase (nombre: tipo), opcionalmente con un valor por defecto. @dataclass genera el resto.

Valores por defecto y default_factory

Python
from dataclasses import dataclass, field

@dataclass
class Articolo:
    nome: str
    prezzo: float = 0.0
    tag: list[str] = field(default_factory=list)

frozen=True: inmutable

Python
@dataclass(frozen=True)
class Coordinata:
    lat: float
    lon: float

c = Coordinata(45.4, 9.2)
c.lat = 99   # FrozenInstanceError!

Las dataclasses con frozen también son hashables: puedes usarlas como claves de diccionario o elementos de conjunto.

order=True: comparaciones automáticas

Python
@dataclass(order=True)
class Voto:
    valore: int

Voto(10) < Voto(20)   # True
sorted([Voto(30), Voto(10), Voto(20)])

Genera __lt__, __le__, __gt__, __ge__ comparando campos en el orden en que fueron declarados.

Métodos normales

Las dataclasses siguen siendo clases normales: puedes añadir métodos.

Python
import math
from dataclasses import dataclass

@dataclass
class Punto:
    x: float
    y: float
    def distanza_dall_origine(self) -> float:
        return math.hypot(self.x, self.y)

Punto(3, 4).distanza_dall_origine()   # 5.0

Dataclasses inmutables y eficientes

Puedes hacer que una dataclass sea inmutable pasando el argumento frozen=True al decorador: @dataclass(frozen=True). Esto genera un error ante cualquier intento de modificar los atributos después de la instanciación, haciendo que las instancias sean seguras para entornos concurrentes o para su uso como claves de diccionario.

Pruébalo tú

Ejercicio#python.m9.l2.e1
Intentos: 0Cargando...

Crea una dataclass `Book` con los campos `title: str` y `pages: int`. Instánciala como `l = Book('Moby Dick', 635)`. Evalúa `l == Book('Moby Dick', 635)`.

Cargando editor...
Mostrar pista

@dataclass genera __eq__ comparando campo por campo.

Solución disponible después de 3 intentos

Ejercicio de repaso

Ejercicio#python.m9.l2.e2
Intentos: 0Cargando...

Crea una dataclass `Basket` con el campo `items: list[str]` con default_factory=list. Instancia `c1 = Basket()` y `c2 = Basket()`, añade 'mela' a c1.items. Evalúa `(c1.items, c2.items)` para verificar que NO son la misma lista.

Cargando editor...
Mostrar pista

field(default_factory=list) garantiza una lista nueva por instancia.

Solución disponible después de 3 intentos

Desafío adicional

Ejercicio#python.m9.l2.e3
Intentos: 0Cargando...

Importa `dataclass` de `dataclasses`. Crea una dataclass `Point` que contenga dos coordenadas float `x` e `y`. Instancia un punto con `x=1.5` e `y=2.5` guardándolo en `p`. Finalmente, evalúa `p`.

Cargando editor...
Mostrar pista

Usa @dataclass encima de la clase Point, y declara x: float e y: float.

Solución disponible después de 3 intentos