Przejdź do głównej treści
eLearner.app
Moduł 9 · Lekcja 2 z 434/36 w kursie~12 min
Lekcje modułu (2/4)

dataclasses: klasy danych bez boilerplate

Dekorator @dataclass (moduł dataclasses) automatycznie generuje metody __init__, __repr__ i __eq__ dla klas, których jedynym zadaniem jest przechowywanie danych. Mniej powtarzalnego kodu (boilerplate), bardziej przejrzysty kod.

Bez dataclass (boilerplate)

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)

Z 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

Schemat: deklarowanie pól jako anotacji klas (nazwa: typ), opcjonalnie z wartością domyślną. @dataclass wygeneruje całą resztę.

Wartości domyślne i 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: niezmienne

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

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

Klasy danych z ustawieniem frozen są również modyfikowalne za pomocą haszowania (hashable): możesz używać ich jako kluczy słownika lub elementów zbioru.

order=True: automatyczne porównania

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

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

Generuje metody __lt__, __le__, __gt__, __ge__ porównujące pola w kolejności ich zadeklarowania.

Zwykłe metody

Klasy danych pozostają zwykłymi klasami: możesz dodawać do nich metody.

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

Niezmienne i wydajne klasy danych (dataclasses)

Możesz uczynić klasę danych niezmienną, przekazując argument frozen=True do dekoratora: @dataclass(frozen=True). Spowoduje to zgłoszenie błędu przy jakiejkolwiek próbie modyfikacji atrybutów po utworzeniu obiektu, co czyni instancje bezpiecznymi w środowiskach wielowątkowych oraz gotowymi do użycia jako klucze słownika.

Spróbuj sam

Ćwiczenie#python.m9.l2.e1
Próby: 0Ładowanie...

Utwórz klasę danych `Book` z polami `title: str` i `pages: int`. Utwórz jej obiekt jako `l = Book('Moby Dick', 635)`. Oceń `l == Book('Moby Dick', 635)`.

Ładowanie edytora...
Pokaż wskazówkę

@dataclass generuje metodę __eq__ porównującą pole po polu.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie powtórzeniowe

Ćwiczenie#python.m9.l2.e2
Próby: 0Ładowanie...

Utwórz klasę danych `Basket` z polem `items: list[str]` i default_factory=list. Utwórz instancje `c1 = Basket()` oraz `c2 = Basket()`, a następnie dodaj 'mela' do c1.items. Oceń `(c1.items, c2.items)`, aby upewnić się, że NIE jest to ta sama lista.

Ładowanie edytora...
Pokaż wskazówkę

field(default_factory=list) gwarantuje nową listę dla każdej instancji.

Rozwiązanie dostępne po 3 próbach

Dodatkowe wyzwanie

Ćwiczenie#python.m9.l2.e3
Próby: 0Ładowanie...

Zaimportuj `dataclass` z modułu `dataclasses`. Utwórz klasę danych `Point` zawierającą dwie współrzędne typu float: `x` oraz `y`. Utwórz obiekt punktu z wartościami `x=1.5` i `y=2.5` przypisując go do `p`. Na koniec oceń `p`.

Ładowanie edytora...
Pokaż wskazówkę

Użyj @dataclass nad klasą Point i zadeklaruj x: float oraz y: float.

Rozwiązanie dostępne po 3 próbach