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.

Have a go. Finish the code and press Run to see the result immediately, right on this page.

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.