Na dit hoofdstuk snap je:
- wat “dunder methods” zijn (double underscore),
- het verschil tussen
__str__en__repr__(en wanneer welke gebruikt wordt), - hoe je objecten kunt vergelijken met
__eq__, - wat er gebeurt met
==,!=,in, sorteren, etc. - en je kent de valkuilen (recursie, inconsistenties, hash-problemen).
1. Wat zijn dunder methods? #
Dunder = duble underscore, zoals:
__init____str____repr____eq__
Dit zijn “speciale hooks” waarmee je Python vertelt:
- hoe jouw object zich gedraagt als je het print,
- hoe het zich laat vergelijken,
- hoe het werkt in sets/dicts,
- hoe het iteraties doet, enz.
Je hoeft ze niet te kennen om OOP te starten, maar:
zodra je eigen classes “netjes” moeten aanvoelen, worden ze goud waard.
2. __str__ vs __repr__ (het verschil dat iedereen door elkaar haalt) #
__str__ = bedoeld voor mensen #
Wordt gebruikt door:
print(obj)str(obj)
__repr__ = bedoeld voor developers/debugging #
Wordt gebruikt door:
repr(obj)- in de Python shell / notebooks (weergave van objecten)
- logging/debug output
- containers:
print([obj])gebruikt vaakreprvan elementen
Vuistregel die je kunt onthouden:
__str__: “mooi”__repr__: “informatief / eenduidig”
Voorbeeld zonder dunders (onhandig)
class User:
def __init__(self, name):
self.name = name
u = User("Alice")
print(u) # <__main__.User object at 0x...>PythonMet __repr__ (debug-friendly)
class User:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"User(name={self.name!r})"
u = User("Alice")
print(u) # User(name='Alice')
print([u, u]) # [User(name='Alice'), User(name='Alice')]PythonWaarom die !r?
{self.name!r} gebruikt repr(self.name) in plaats van str(self.name).
Dat is superhandig omdat je quotes ziet en speciale tekens duidelijk zijn.
Met __str__ (user-friendly output)
class User:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"User(name={self.name!r})"
def __str__(self):
return self.name
u = User("Alice")
print(u) # Alice (str)
print(repr(u))# User(name='Alice') (repr)PythonTip: Als je maar één kiest: kies vaak __repr__ eerst.__str__ is nice-to-have.
3. __eq__: objecten vergelijken met == #
Zonder __eq__ #
Python vergelijkt objecten standaard op identiteit (zelfde object in memory).
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # False (standaard)PythonMet __eq__ (waarde-vergelijking) #
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # TruePythonWaarom NotImplemented?
Als je other een ander type is, zeg je eigenlijk:
“Ik weet niet hoe ik hiermee moet vergelijken.”
Dan krijgt Python de kans om:
other.__eq__(p1)te proberen,- of netjes
Falsete geven.
Dit is netter dan hard False retourneren.
4. Valkuil: __eq__ en __hash__ (sets/dicts) #
Dit is belangrijk:
- Als je
__eq__definieert, verandert het idee van “gelijkheid”. - Voor gebruik in
setof als key indictheb je ook een stabiele__hash__nodig. - Python zet soms automatisch
__hash__ = Noneals je__eq__definieert, om je te beschermen tegen bugs.
Waarom? #
Een set/dict gebruikt hashes om snel te zoeken.
Als twee objecten “gelijk” zijn (__eq__ True), moeten ze ook dezelfde hash hebben.
Als je object mutable is, is __hash__ gevaarlijk:
- je wijzigt velden,
- hash verandert,
- object “verdwijnt” uit dict/set lookup.
Praktische regels #
- Mutable value objects? Vaak: géén
__hash__, dus niet als dict-key. - Immutable value objects? Dan kun je
__hash__wél goed doen. - Of gebruik
@dataclass(frozen=True)later (hoofdstuk 6), die regelt dit netjes.
5. Voorbeeld: OrderLine (repr + eq) #
class OrderLine:
def __init__(self, sku, qty):
self.sku = sku
self.qty = qty
def __repr__(self):
return f"OrderLine(sku={self.sku!r}, qty={self.qty})"
def __eq__(self, other):
if not isinstance(other, OrderLine):
return NotImplemented
return (self.sku, self.qty) == (other.sku, other.qty)
a = OrderLine("ABC", 2)
b = OrderLine("ABC", 2)
print(a) # OrderLine(sku='ABC', qty=2)
print(a == b) # TruePython6. Extra (handig om te weten): __lt__ en sorteren #
Als je wil sorteren met sorted(...), kun je __lt__ (“less than”) implementeren.
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __repr__(self):
return f"Student(name={self.name!r}, grade={self.grade})"
def __lt__(self, other):
if not isinstance(other, Student):
return NotImplemented
return self.grade < other.grade
students = [Student("A", 7.5), Student("B", 6.0)]
print(sorted(students)) # sorteert op gradePython(Je kunt ook vaak gewoon sorted(students, key=lambda s: s.grade) gebruiken; dat is meestal simpeler.)
7. Veelgemaakte fouten bij __repr__/__str__ #
Fout 1: recursie door jezelf te printen #
def __repr__(self):
return f"{self}" # ❌ self -> __str__/__repr__ -> loopPythonGoed: bouw string uit velden (self.name, self._price, etc.).
Fout 2: te weinig informatie in __repr__ #
return "User()"PythonNiet handig bij debugging. Zet minimaal de belangrijkste velden erin.
Nu je ook deze methodes kan toepassen is het handig om door te krijgen hoe je nette classes schrijft. Zie daarvoor het volgende hoofdstuk.

