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.
By Marcel Schmidtpeter 06 Aug 2026
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.
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:
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".
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 SpeicherMit `__eq__` änderst du dieses Verhalten auf einen inhaltlichen Vergleich:
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.
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 == auto2Hier 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.
# 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.
# 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.markeFehler 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.
Want 1:1 help with this topic?
Need someone to walk through this with you?
Instead of re-reading: 30 min with a senior dev.
Book a free first chat →
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 →More reading
Python None Optional: None-Werte sicher behandeln – einfach erklärt
None-Werte sicher behandeln: Erfahre, wie Optional und Typannotationen in Python funktionieren und wie du typische None-Fehler vermeidest.
Python List Comprehension map filter: Einfach erklärt mit Beispielen
List Comprehensions, map und filter im Vergleich: So filterst und transformierst du Daten in Python – mit anschaulichen Beispielen für den Einstieg.
Python Typannotationen und Generics einfach erklärt
Typannotationen und Generics in Python erklärt: So nutzt du Type Hints für lesbaren, wartbaren Code – mit Beispielen für Einsteiger.