namedtuple vs dataclass
namedtuple tworzy niezmienną, lekką krotkę z nazwanymi polami, a @dataclass tworzy zwykłą, mutowalną klasę z wygenerowanym __init__, __repr__ i __eq__.
Porównanie w tabeli
#Dziedziczy po
- @dataclass
- Zwykła klasa (
object)
Można zmieniać pola po utworzeniu
- @dataclass
- Tak (domyślnie),
frozen=Trueblokuje
Indeksowanie i rozpakowywanie jak krotka
- @dataclass
- Nie
Klucz słownika albo element zbioru
- @dataclass
- Tylko z
frozen=True
Wartość domyślna będąca listą
- collections.namedtuple
- Przez
defaults=[...]dla ostatnich pól - @dataclass
- Przez
field(default_factory=list)
Typowe użycie
- collections.namedtuple
- Lekkie, stałe dane: wynik funkcji, wiersz CSV
- @dataclass
- Dane, które się zmieniają, własne metody, walidacja
Przykład
#from collections import namedtuple
from dataclasses import dataclass
Point = namedtuple("Point", ["x", "y"])
@dataclass
class Student:
name: str
points: int = 0
point = Point(3, 4)
student = Student("Ania")
student.points += 40
print(point, point[0])
print(student)Definicja i zastosowanie
#Obie konstrukcje eliminują powtarzalny kod przy klasach, które głównie przechowują dane: pola deklarujesz raz, a __init__, czytelny __repr__ i porównywanie __eq__ powstają automatycznie.
Różnią się tym, czym naprawdę jest wynikowy obiekt. namedtuple tworzy klasę dziedziczącą po tuple: obiekt jest niezmienny, da się go indeksować, rozpakować i użyć jako klucz słownika, ale zmiana pola kończy się AttributeError. @dataclass tworzy zwykłą klasę: obiekt jest mutowalny, chyba że dodasz frozen=True, nie jest krotką, więc nie indeksujesz go liczbą, ale łatwo dodać mu metody, wartości domyślne przez field(default_factory=...) i walidację w __post_init__.
Wybierz namedtuple, gdy dane są proste, stałe i mają być lekkie, np. wynik funkcji zwracającej kilka wartości albo wiersz z pliku CSV. Wybierz @dataclass, gdy klasa ma się zmieniać w czasie działania programu, potrzebuje własnych metod albo wartości domyślnych takich jak lista czy słownik.
Składnia
#from collections import namedtuple
Nazwa = namedtuple("Nazwa", ["pole1", "pole2"])
from dataclasses import dataclass
@dataclass
class Nazwa:
pole1: typ
pole2: typWięcej przykładów
#from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
point = Point(3, 4)
try:
point.x = 10
except AttributeError:
print("Pola nie można zmienić")
moved = point._replace(x=10)
print(moved)from dataclasses import dataclass, field
@dataclass
class Task:
title: str
tags: list = field(default_factory=list)
def summary(self):
return f"{self.title}, tagi: {len(self.tags)}"
task = Task("Nauka Pythona")
task.tags.append("python")
print(task.summary())
print(task)Dobre praktyki
#- Namedtuple dobrze sprawdza się przy zwracaniu kilku wartości z funkcji: wynik rozpakujesz jak krotkę albo odczytasz po nazwach pól.
- Jeśli potrzebujesz niezmienności, ale też metod i nowszej składni z typami, rozważ
@dataclass(frozen=True)albotyping.NamedTuple. - Pola ze zmienną wartością domyślną w
@dataclass(lista, słownik) zawsze twórz przezfield(default_factory=...), inaczej dostaniesz błąd już przy definicji klasy.
Powiązane hasła
#- collections.namedtupleTworzy krotki z nazwanymi polami: dane odczytasz przez student.points zamiast student[2], a obiekt pozostaje niezmienny.
- @dataclassDekorator, który na podstawie pól z adnotacjami typów generuje __init__, __repr__ i __eq__ klasy przechowującej dane.
- tuple()Tworzy krotkę: niezmienną sekwencję elementów.
- classDefiniuje klasę: szablon obiektów łączący dane i funkcje, które na nich działają.
Widzisz błąd albo brakuje przykładu? Napisz do nas.