PorównaniePorównania

namedtuple vs dataclass

namedtuple tworzy niezmienną, lekką krotkę z nazwanymi polami, a @dataclass tworzy zwykłą, mutowalną klasę z wygenerowanym __init__, __repr__ i __eq__.

Na tej stronie

Porównanie w tabeli

#

Przykład

#
Python
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)
Wynikzapisany wynik, możesz go sprawdzić
Point(x=3, y=4) 3
Student(name='Ania', points=40)

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

#
Składnia
from collections import namedtuple
Nazwa = namedtuple("Nazwa", ["pole1", "pole2"])

from dataclasses import dataclass
@dataclass
class Nazwa:
    pole1: typ
    pole2: typ

Więcej przykładów

#
namedtuple jest niezmienny
Python
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)
Wynikzapisany wynik, możesz go sprawdzić
Pola nie można zmienić
Point(x=10, y=4)
dataclass łatwo dodaje metody i wartości domyślne
Python
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)
Wynikzapisany wynik, możesz go sprawdzić
Nauka Pythona, tagi: 1
Task(title='Nauka Pythona', tags=['python'])

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) albo typing.NamedTuple.
  • Pola ze zmienną wartością domyślną w @dataclass (lista, słownik) zawsze twórz przez field(default_factory=...), inaczej dostaniesz błąd już przy definicji klasy.

Powiązane hasła

#

Widzisz błąd albo brakuje przykładu? Napisz do nas.