Dunder Methods (Special Methods)

Hook into print(), len(), and == with dunder methods

Intermediate 10 min

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.

Example
class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

b = Book("Python 101", 320)
print(b)   # the unhelpful default
Output
<__main__.Book object at 0x10a3f9d50>
Without __str__, the default display is just a type and a memory address (which varies each run).
Example
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)
Output
'Python 101' (320 pages)
__str__ defines the friendly form print() uses.

__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.

Example
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))
Output
Focus: 3 songs
3
__len__ makes len(playlist) return the song count.

Common mistake: Returning a non-string from __str__

Why it happens:

It’s easy to forget the requirement and return a number or a list.

How to fix it:

__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

Why it happens:

A throwaway __repr__ that returns just the class name gives no useful detail when you’re hunting a bug.

How to fix it:

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

Why it happens:

Dunder methods feel advanced, so they get added ‘just in case’.

How to fix it:

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?

What must __str__ return?

Why might __repr__ differ from __str__?

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))

What to learn next

You learned what dunder methods are and added the ones that matter most: __str__ for friendly output, __repr__ for debugging, __len__ for len(), and __eq__ for comparisons — adding each only when there’s a real need. You gave a Playlist class a readable display and a working length.

To round out the unit, we step back to functions and make them more flexible. Flexible Functions: args, kwargs, and Lambdas covers accepting any number of arguments, unpacking lists and dicts into calls, and tiny inline lambda functions. When you’re ready, continue to the next lesson.