Dekorator @dataclass to jedno z narzędzi wprowadzonych w Pythonie 3.7, mające na celu skrócenie kodu szablonowego przy tworzeniu prostych klas do przechowywania danych. Dzięki adnotacjom typów Python automatycznie generuje metody __init__, __repr__, __eq__ i inne.
Historia problemu:
Przed pojawieniem się dataclass programiści ręcznie pisali klasy szablonowe, implementując konstruktory, metody porównawcze, repr, a następnie często przechodzili do nazwanych krotek lub bibliotek typu attrs. Wprowadzenie @dataclass znormalizowało i uprościło ten proces.
Problem:
Kod szablonowy, duplikowanie kodu konstruktorów i metod porównawczych często prowadzi do błędów i komplikowało utrzymanie dużych aplikacji.
Rozwiązanie:
Użycie adnotacji typów i specjalnego dekoratora @dataclass umożliwia automatyczne wygenerowanie wszystkich potrzebnych metod w klasie.
Przykład kodu:
from dataclasses import dataclass @dataclass class Point: x: int y: int p1 = Point(10, 20) p2 = Point(10, 20) print(p1 == p2) # True, automatycznie wygenerowany __eq__ print(p1) # Point(x=10, y=20), automatycznie wygenerowany __repr__
Kluczowe cechy:
Czy @dataclass zmienia zachowanie dziedziczenia (specyfika przy dziedziczeniu)?
Tak. Przy dziedziczeniu klas dataclass należy zwrócić szczególną uwagę: pola klasy bazowej są przetwarzane przed polami klasy dziedziczącej, co może prowadzić do konfliktów konstruktorów/ kolejności argumentów. Jeśli pola w klasach bazowej i dziedziczącej mają te same nazwy, ostatnie zastąpi poprzednie.
Czy w dataclass można używać zmiennych wartości domyślnych dla pól?
Nie, nie można bezpośrednio używać takich obiektów (na przykład, lista) jako domyślnych — należy użyć field(default_factory=list). W przeciwnym razie wszystkie instancje klasy będą dzielić tę samą kolekcję.
Przykład:
from dataclasses import dataclass, field @dataclass class User: values: list = field(default_factory=list)
Czy @dataclass jest szybki w każdych scenariuszach? Czy nadaje się do optymalnego przechowywania dużych zbiorów danych?
Nie. dataclass nie jest najbardziej efektywnym rozwiązaniem do optymalizacji pamięci. Do przechowywania milionów obiektów lepiej wykorzystać __slots__, namedtuple lub specjalne struktury — dataclass dodaje pola pomocnicze i nie oszczędza pamięci jak slots. Można je łączyć, przekazując parametr slots=True (Python 3.10+), albo ręcznie używać slots.
@dataclass class Cart: items: list = [] # błąd! c1 = Cart() c2 = Cart() c1.items.append("a") print(c2.items) # ['a'] — wszystkie Cart dzielą tę samą listę
Zalety:
Wady:
from dataclasses import dataclass, field @dataclass class Cart: items: list = field(default_factory=list) c1 = Cart() c2 = Cart() c1.items.append("a") print(c2.items) # []
Zalety:
Wady:
field(default_factory=...) (co wymaga osobnego zgłębiania).