Dunder Methods (Special Methods)
Hook into print(), len(), and == with dunder methods
In this lesson
Python lets your objects behave like its built-in types by defining special methods — the ones with double underscores, nicknamed ‘dunder’ methods. Define the right ones and print(obj), len(obj), and obj1 == obj2 all just work. This lesson covers the few you’ll actually use.
Explain it like I’m 5
Dunder methods are special hooks Python looks for when it needs your object to act like a normal Python thing — to be printed, measured, or compared.
__str__ and __repr__: readable output
By default, printing an object shows something unhelpful like <__main__.Book object at 0x...>. Define __str__ to control what print() and str() show — a friendly, human-readable form. __repr__ is the developer-facing version, shown in the REPL and in lists, and ideally unambiguous for debugging.
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
b = Book("Python 101", 320)
print(b) # the unhelpful default
<__main__.Book object at 0x10a3f9d50>
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __str__(self):
return f"'{self.title}' ({self.pages} pages)"
b = Book("Python 101", 320)
print(b)
'Python 101' (320 pages)
__len__ and __eq__: measure and compare
Define __len__ and Python’s built-in len() will work on your object. Define __eq__ and the == operator compares objects the way you choose (say, two books are equal if their titles match). Add these only when there’s a clear need — not every class wants a length or custom equality.
class Playlist:
def __init__(self, name, songs):
self.name = name
self.songs = songs
def __str__(self):
return f"{self.name}: {len(self.songs)} songs"
def __len__(self):
return len(self.songs)
p = Playlist("Focus", ["Song A", "Song B", "Song C"])
print(p)
print(len(p))
Focus: 3 songs 3
Common mistake: Returning a non-string from __str__
It’s easy to forget the requirement and return a number or a list.
__str__ must return a string. Wrap your value in an f-string or str(...) so the result is always text.
Common mistake: Making __repr__ too vague to help debugging
A throwaway __repr__ that returns just the class name gives no useful detail when you’re hunting a bug.
Aim for a __repr__ that shows the key attributes, ideally looking like the call that would recreate the object, e.g. Book('Python 101', 320).
Common mistake: Adding special methods before there's a use case
Dunder methods feel advanced, so they get added ‘just in case’.
Add a dunder method when something concrete needs it — when you want to print the object, measure it, or compare it. Otherwise leave it out.
Which method controls what print(object) shows?
print() and str() call __str__ for the human-readable form.
What must __str__ return?
__str__ must return a string; returning anything else raises a TypeError.
Why might __repr__ differ from __str__?
__str__ targets end users; __repr__ targets developers and aims to be unambiguous for debugging.
Mini exercise (medium)
Create a Playlist class that stores a name and a list of songs. Add __str__ so printing it shows something like "Road Trip (2 songs)", and __len__ so len(playlist) returns the number of songs.
Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.
class Playlist:
def __init__(self, name, songs):
self.name = name
self.songs = songs
def __str__(self):
# TODO: return e.g. "Road Trip (2 songs)"
return ""
def __len__(self):
# TODO: return how many songs are in the playlist
return 0
p = Playlist("Road Trip", ["Highway", "Sunset"])
print(p)
print(len(p))
__str__ should return f"{self.name} ({len(self.songs)} songs)"; __len__ should return len(self.songs).
class Playlist:
def __init__(self, name, songs):
self.name = name
self.songs = songs
def __str__(self):
return f"{self.name} ({len(self.songs)} songs)"
def __len__(self):
return len(self.songs)
p = Playlist("Road Trip", ["Highway", "Sunset"])
print(p)
print(len(p))
Road Trip (2 songs)
2
assert str(p) == "Road Trip (2 songs)", "__str__ should return 'Road Trip (2 songs)'"
assert len(p) == 2, "__len__ should return the number of songs"
assert isinstance(str(Playlist("X", [])), str), "__str__ must return a string"
print("✓ Feels like a built-in type!")