Dataclasses in Python: Weniger Boilerplate für saubere Datenobjekte

Wer Klassen nur zum Speichern von Daten schreibt, tippt schnell viel überflüssigen Code. Der Decorator @dataclass nimmt dir __init__, __repr__ und den Vergleich ab – und macht deinen Code lesbarer.

Teilen

Fast jedes Python-Projekt kennt sie: kleine Klassen, die eigentlich nur ein paar Werte zusammenhalten. Ein Point mit x und y, ein User mit Name und E-Mail, eine Config mit einer Handvoll Einstellungen. Klassisch schreibt man dafür einen __init__, dann ein __repr__, damit die Ausgabe beim Debuggen etwas taugt, und wenn man Objekte vergleichen will, kommt noch __eq__ dazu. Das ist viel wiederkehrender Code für erstaunlich wenig Logik – und genau hier setzt das Modul dataclasses an.

Der Unterschied auf einen Blick

Ohne Dataclass sieht eine simple Datenklasse so aus:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)

Mit dem Decorator @dataclass schrumpft das auf das Wesentliche zusammen:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

Python erzeugt __init__, __repr__ und __eq__ jetzt automatisch aus den Typannotationen. Point(1, 2) funktioniert, print(Point(1, 2)) gibt etwas Lesbares aus, und Point(1, 2) == Point(1, 2) ist True. Der eigentliche Zweck der Klasse steht nicht mehr unter einem Berg an Boilerplate begraben.

Standardwerte und der Fallstrick mit Listen

Felder dürfen Standardwerte haben, ganz wie normale Funktionsargumente. Wichtig ist nur die Reihenfolge: Felder ohne Default müssen vor Feldern mit Default stehen. Bei veränderlichen Standardwerten wie Listen gibt es außerdem einen klassischen Stolperstein. Python verbietet tags: list = [], weil sich sonst alle Instanzen dieselbe Liste teilen würden. Die Lösung ist field mit einer Factory:

from dataclasses import dataclass, field

@dataclass
class Article:
    title: str
    tags: list = field(default_factory=list)

So bekommt jede Instanz ihre eigene, frische Liste. Wer schon einmal mit veränderlichen Default-Argumenten in normalen Funktionen zu kämpfen hatte, erkennt das Muster sofort wieder.

Unveränderlich und aufgeräumt

Mit @dataclass(frozen=True) werden die Objekte unveränderlich – Zuweisungen nach der Erstellung lösen dann einen Fehler aus. Das ist praktisch für Konfigurationen oder Werte, die als Dictionary-Schlüssel dienen sollen, denn frozen Dataclasses sind hashbar. Für Nachbearbeitung gibt es __post_init__, eine Methode, die direkt nach dem generierten __init__ läuft und sich gut für Validierung eignet.

Für alle, die noch tiefer einsteigen wollen

Dataclasses sind ein kleines Feature mit großer Wirkung, und es lohnt sich, die Details wirklich zu verstehen – von field-Optionen über slots bis hin zum Zusammenspiel mit Vererbung. Unser englischsprachiges Schwesterblog PyKit hat dazu eine ausführliche, praxisnahe Vertiefung geschrieben, die genau diese Feinheiten Schritt für Schritt durchgeht: Less Boilerplate, More Data: A Practical Deep Dive into Python's dataclasses. Wenn dich hier ein Aspekt neugierig gemacht hat, findest du dort die passende Fortsetzung mit weiteren Beispielen.

Der beste Weg, ein Gefühl für Dataclasses zu bekommen, ist der direkte Vergleich: Nimm eine bestehende Klasse aus deinem Projekt, die nur Daten hält, und schreibe sie in eine Dataclass um. In den meisten Fällen bleibt der halbe Code übrig – und das, was übrig bleibt, sagt endlich klar, worum es der Klasse eigentlich geht.