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)
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
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, 4Schemat: 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
from dataclasses import dataclass, field
@dataclass
class Articolo:
nome: str
prezzo: float = 0.0
tag: list[str] = field(default_factory=list)frozen=True: niezmienne
@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
@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.
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.0Niezmienne 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
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)`.
Pokaż wskazówkę
@dataclass generuje metodę __eq__ porównującą pole po polu.
Rozwiązanie dostępne po 3 próbach
Ćwiczenie powtórzeniowe
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.
Pokaż wskazówkę
field(default_factory=list) gwarantuje nową listę dla każdej instancji.
Rozwiązanie dostępne po 3 próbach
Dodatkowe wyzwanie
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`.
Pokaż wskazówkę
Użyj @dataclass nad klasą Point i zadeklaruj x: float oraz y: float.
Rozwiązanie dostępne po 3 próbach