20% off with code ERSTIS26
5 min read 06 Aug 2026

Python Dunder Methoden: __str__, __eq__ & __hash__ einfach erklärt

Die wichtigsten Python Dunder Methoden wie __str__, __eq__ und __hash__ einfach erklärt – inklusive Beispielen für Azubis, Studenten & Einsteiger.

Marcel Schmidtpeter By Marcel Schmidtpeter 06 Aug 2026
Python Dunder Methoden: __str__, __eq__ & __hash__ einfach erklärt

Du gibst dein erstes Python-Objekt mit `print()` aus und siehst nur so etwas wie `<__main__.Auto object at 0x7f4a2b3c4d10>`. Frustrierend, oder? Genau hier kommen Python Dunder Methoden ins Spiel. Egal ob du als Fachinformatiker-Azubi deine erste Klasse schreibst, im Informatikstudium Objektorientierung übst oder im ersten Job Legacy-Code wartest - diese speziellen Methoden begegnen dir ständig. Sie sind der Schlüssel, damit deine eigenen Objekte sich "natürlich" verhalten.

Was sind Python Dunder Methoden?

Python Dunder Methoden (von "double underscore") sind spezielle Methoden, deren Name mit zwei Unterstrichen beginnt und endet - zum Beispiel `__str__` oder `__init__`. Sie werden auch magische Methoden genannt, weil Python sie automatisch bei bestimmten Operationen aufruft. Du rufst sie fast nie direkt auf.

Stell dir vor, du baust einen Roboter. Die Dunder Methoden sind die vorgefertigten Schnittstellen, über die der Roboter mit der Außenwelt kommuniziert. Du musst nur noch programmieren, *was* er sagen soll - *wann* er etwas sagt, entscheidet Python.

Merke: Dunder Methoden definieren, wie deine Objekte auf eingebaute Python-Funktionen wie `print()`, `len()` oder Operatoren wie `==` reagieren.

Die wichtigsten Dunder Methoden für den Start sind `__init__` (Konstruktor), `__str__` (Textdarstellung), `__eq__` (Gleichheit) und `__hash__` (Hashwert für Sets und Dictionaries). Die offizielle Python-Dokumentation listet alle - es sind über 80 Stück.

Wie funktioniert `__init__`? Der Konstruktor

Der Konstruktor wird aufgerufen, wenn du ein neues Objekt einer Klasse erstellst. Er initialisiert die Attribute deines Objekts mit den Werten, die du übergibst.

python
class Auto:
    def __init__(self, marke, modell, baujahr):
        self.marke = marke
        self.modell = modell
        self.baujahr = baujahr


mein_auto = Auto("Volkswagen", "Golf", 2020)

Hier übergibst du die Werte direkt beim Erstellen. Python ruft `__init__` automatisch auf. Du musst dafür nichts weiter tun.

Für dich als Student im ersten Semester oder Azubi im ersten Lehrjahr ist das oft die allererste Dunder Methode, die du lernst. Auch wenn du aus der Java-Welt kommst (wie in unserem Beitrag Java Konstruktor einfach erklärt beschrieben), ist das Konzept vertraut - nur die Syntax ist anders.

`__str__` vs `__repr__`: Wie stellst du Objekte lesbar dar?

`__str__` definiert die "benutzerfreundliche" Textdarstellung eines Objekts. Sie wird aufgerufen, wenn du `print()` oder `str()` verwendest. `__repr__` ist die "entwicklerfreundliche" Darstellung für Debugging und REPL-Ausgaben. Sie greift bei `repr()` oder wenn `__str__` nicht definiert ist.

Methode

Wann aufgerufen

Zielgruppe

Stil

`__str__`

`print()`, `str()`, f-Strings

Endbenutzer

Lesbar, freundlich

`__repr__`

`repr()`, Debugging, REPL

Entwickler

Eindeutig, reproduzierbar

So sieht das in der Praxis aus:

python
class Auto:
    def __init__(self, marke, modell, baujahr):
        self.marke = marke
        self.modell = modell
        self.baujahr = baujahr

    def __str__(self):
        return f"{self.marke} {self.modell} ({self.baujahr})"

    def __repr__(self):
        return f"Auto(marke='{self.marke}', modell='{self.modell}', baujahr={self.baujahr})"


mein_auto = Auto("Volkswagen", "Golf", 2020)
print(mein_auto)  # Volkswagen Golf (2020)
print(repr(mein_auto))  # Auto(marke='Volkswagen', modell='Golf', baujahr=2020)

Eine gute `__repr__` sollte idealerweise so formatiert sein, dass du den String kopieren und als Code ausführen kannst, um das gleiche Objekt zu erzeugen. Das ist besonders nützlich beim Debuggen in der Praxis - egal ob im ersten Berufsjahr oder bei komplexen Uni-Projekten.

Merke: Definiere immer `__repr__`. Wenn du `__str__` nicht definierst, nutzt Python automatisch `__repr__` als Fallback. Andersherum gilt das nicht!

Wie vergleichst du Objekte mit `__eq__`?

`__eq__` überschreibt den Gleichheitsoperator (`==`) für deine Objekte. Standardmäßig vergleicht Python Objekte nach ihrer Speicheradresse - zwei verschiedene Objekte mit identischen Attributen sind daher "ungleich".

python
class Auto:
    def __init__(self, marke, modell, baujahr):
        self.marke = marke
        self.modell = modell
        self.baujahr = baujahr


auto1 = Auto("BMW", "X5", 2021)
auto2 = Auto("BMW", "X5", 2021)

print(auto1 == auto2)  # False! Verschiedene Objekte im Speicher

Mit `__eq__` änderst du dieses Verhalten auf einen inhaltlichen Vergleich:

python
class Auto:
    def __init__(self, marke, modell, baujahr):
        self.marke = marke
        self.modell = modell
        self.baujahr = baujahr

    def __eq__(self, other):
        if not isinstance(other, Auto):
            return NotImplemented
        return (
            self.marke == other.marke
            and self.modell == other.modell
            and self.baujahr == other.baujahr
        )


auto1 = Auto("BMW", "X5", 2021)
auto2 = Auto("BMW", "X5", 2021)

print(auto1 == auto2)  # True!

Der Parameter `other` ist das Objekt, mit dem verglichen wird. `isinstance()` prüft den Typ. `NotImplemented` signalisiert Python, dass dieser Vergleichstyp nicht unterstützt wird - Python kann dann den anderen Operanden fragen. Warum so umständlich? Weil dein Auto sonst mit einer Zahl oder einem String verglichen werden könnte, was keinen Sinn ergibt.

Best Practice: Gib `NotImplemented` zurück, nicht `False`, wenn der Typ nicht passt. So kann Python versuchen, die Reflexion beim anderen Objekt anzufragen.

Wann brauchst du `__hash__`?

`__hash__` gibt einen ganzzahligen Wert zurück, der ein Objekt identifiziert. Python nutzt diesen Hashwert, um Objekte in Sets (Mengen ohne Duplikate) und als Schlüssel in Dictionaries (Schlüssel-Wert-Paare) effizient zu speichern.

Stell dir vor, `__hash__` ist wie ein Parkplatznummern-System. Anstatt alle Autos zu durchsuchen, berechnet das System aus dem Kennzeichen direkt die Parkplatznummer.

Der kritische Zusammenhang: Wenn du `__eq__` überschreibst, setzt Python `__hash__` automatisch auf `None`. Dein Objekt wird unhashable und kann nicht mehr in Sets oder als Dictionary-Schlüssel verwendet werden. Das ist kein Bug, sondern Absicht - Python verhindert damit inkonsistente Hash-Vergleiche.

python
class Auto:
    def __init__(self, marke, modell, baujahr):
        self.marke = marke
        self.modell = modell
        self.baujahr = baujahr

    def __eq__(self, other):
        if not isinstance(other, Auto):
            return NotImplemented
        return (
            self.marke == other.marke
            and self.modell == other.modell
            and self.baujahr == other.baujahr
        )

    def __hash__(self):
        return hash((self.marke, self.modell, self.baujahr))


auto1 = Auto("BMW", "X5", 2021)
auto2 = Auto("BMW", "X5", 2021)

auto_set = {auto1, auto2}
print(len(auto_set))  # 1, weil auto1 == auto2

Hier kombinieren wir die eingebaute `hash()`-Funktion mit einem Tupel der Attribute. Zwei gleiche Objekte haben denselben Hashwert - das ist die Grundregel.

Merke: Wenn `a == b` gilt, dann MUSS `hash(a) == hash(b)` gelten. Sonst brechen Sets und Dictionaries. Andersherum muss das nicht stimmen: Gleiche Hashwerte bedeuten nicht zwingend Gleichheit (sogenannte Hash-Kollisionen).

Welche Fehler solltest du bei Dunder Methoden vermeiden?

Fehler 1: `__hash__` vergessen nach `__eq__`

Der Klassiker. Du überschreibst `__eq__` für einen logischen Vergleich, vergisst aber `__hash__`. Danach wirft Python einen `TypeError`, sobald du das Objekt in ein Set steckst.

python
# FALSCH: __eq__ ohne __hash__
class Auto:
    def __init__(self, marke):
        self.marke = marke

    def __eq__(self, other):
        return self.marke == other.marke


a = Auto("BMW")
mein_set = {a}  # TypeError: unhashable type: 'Auto'

Fehler 2: `__eq__` ohne Typprüfung

Wenn du in `__eq__` nicht prüfst, ob `other` überhaupt den richtigen Typ hat, crasht dein Code bei Vergleichen mit fremden Typen.

python
# FALSCH
def __eq__(self, other):
    return self.marke == other.marke  # Crasht bei other = 42


# RICHTIG
def __eq__(self, other):
    if not isinstance(other, Auto):
        return NotImplemented
    return self.marke == other.marke

Fehler 3: Mutable Attribute in `__hash__` verwenden

Wenn Attribute veränderbar sind und sich ändern, verändert sich auch der Hashwert. Das Objekt ist dann in Sets nicht mehr auffindbar - es existiert praktisch "unsichtbar" weiter. Das ist ein subtiler Bug, der dich im Beruf stundenlang aufhalten kann. Nutze für hashbare Objekte nur unveränderliche (immutable) Attribute wie Strings, Zahlen und Tupel.

Übungstipp: Baue eine eigene Klasse

Nimm dir 15 Minuten Zeit und implementiere eine Klasse `Buch` mit den Attributen `titel`, `autor` und `isbn`. Implementiere `__str__`, `__repr__`, `__eq__` und `__hash__`.

Teste dann:

  • `print()` und `repr()` zeigen verschiedene Formate

  • Zwei Bücher mit gleicher ISBN sind gleich

  • Ein Set aus Duplikaten enthält nur ein Buch

Diese Übung festigt dein Wissen besser als jedes Tutorial-Lesen. Werde kreativ und erweitere die Klasse nach Belieben.

FAQ: Häufige Fragen zu Python Dunder Methoden

Wie viele Dunder Methoden gibt es in Python?

Python definiert über 80 Dunder Methoden. Die wichtigsten für den Alltag sind `__init__`, `__str__`, `__repr__`, `__eq__`, `__hash__`, `__len__` und `__lt__`. Die vollständige Liste findest du in der offiziellen Python-Dokumentation.

Was bedeutet `__main__` bei der Objektausgabe?

`__main__` ist der Name des Moduls, in dem die Klasse definiert wurde. Führst du ein Skript direkt aus, heißt das Modul `__main__`. Importierst du es aus einer anderen Datei, steht dort der Dateiname. Das erklärt die Ausgabe `<__main__.Auto object at 0x...>`.

Kann ich `__init__` weglassen?

Ja, aber nur wenn deine Klasse keine Attribute bei der Erstellung benötigt. Ohne `__init__` nutzt Python den Standardkonstruktor, der keine Parameter außer `self` akzeptiert.

Warum sollte ich `__repr__` implementieren, wenn es `__str__` gibt?

Weil `__repr__` als Fallback dient und beim Debuggen, in Listen und bei Fehlermeldungen verwendet wird. Ohne `__repr__` siehst du dort nur die kryptische Speicheradresse. Mehr dazu auch bei den Python Docs zum Objektmodell.

Gibt es Dunder Methoden für arithmetische Operationen?

Ja. `__add__` für `+`, `__sub__` für `-`, `__mul__` für `*` und viele mehr. So kannst du eigene Datentypen bauen, die sich wie Zahlen verhalten. Das ist besonders relevant für Mathematik-Bibliotheken oder komplexe Datentypen im Studium.

Fazit

Python Dunder Methoden machen deine Objekte zu echten "Bürgern" der Sprache. Mit `__str__` machst du sie lesbar, mit `__eq__` vergleichbar und mit `__hash__` in Sets und Dictionaries nutzbar. Der Zusammenhang zwischen `__eq__` und `__hash__` ist besonders wichtig - vergisst du ihn, suchst du stundenlang nach seltsamen Fehlern. Wenn du dich weiter in Python einarbeiten willst, helfen dir unsere Beiträge zu self in Python oder Python super() weiter.

Brauchst du Hilfe bei Python, Java oder C#? Egal ob Prüfungsvorbereitung im Studium, IHK-Abschlussprüfung als Fachinformatiker oder Onboarding im ersten Job - bei study-it.education findest du erfahrene Nachhilfelehrer, die sich genau diese Themen mit dir erarbeiten.


First chat · €0 · 30 min

Need someone to walk through this with you?

Instead of re-reading: 30 min with a senior dev.

Book a free first chat →
About the author
Marcel Schmidtpeter
Marcel Schmidtpeter

Die wichtigsten Python Dunder Methoden wie __str__, __eq__ und __hash__ einfach erklärt – inklusive Beispielen für Azubis, Studenten & Einsteiger.

marcel.schmidtpeter@study-it.education →
Keep reading

More reading