Syllabus Lesson 50 of 239 · Object-Oriented Python
Object-Oriented Python

Dunder Methods

Python has special method names wrapped in double underscores, said out loud as dunder methods. You already met __init__. Others let your objects work naturally with built-in operations like printing, equality, and sorting.

Printing: __str__ and __repr__

__str__ controls what print() and str() show. __repr__ is the developer-facing version, used in the interactive shell and inside lists. A good habit is to define at least one.

class Money:
    def __init__(self, cents):
        self.cents = cents

    def __str__(self):
        return f"${self.cents / 100:.2f}"

    def __repr__(self):
        return f"Money({self.cents})"

m = Money(150)
print(m)        # $1.50
print(repr(m))  # Money(150)

Equality: __eq__

By default two objects are equal only if they are literally the same object. __eq__(self, other) lets you compare by value instead.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

print(Point(1, 2) == Point(1, 2))   # True

One catch rides along with __eq__: the moment you define it, Python sets __hash__ to None, so your objects become unhashable. If you then drop one into a set or use it as a dict key you get TypeError: unhashable type: 'Point'. The reason is a rule Python depends on, that equal objects must share the same hash, so once you redefine equality it makes you opt back in on purpose. If you need that, add a matching __hash__ built from the same fields you compare, or use a frozen dataclass (covered in the @dataclass lesson) which writes both for you.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __hash__(self):
        return hash((self.x, self.y))   # same fields as __eq__

print(len({Point(1, 2), Point(1, 2)}))   # 1, and no TypeError

Ordering: __lt__

__lt__(self, other) defines the less-than rule, which is enough to let sorted() sort your objects.

class Score:
    def __init__(self, points):
        self.points = points

    def __lt__(self, other):
        return self.points < other.points

    def __repr__(self):
        return f"Score({self.points})"

print(sorted([Score(3), Score(1), Score(2)]))
# [Score(1), Score(2), Score(3)]
Your turn

Define a class Temp that stores self.degrees from __init__. Add __str__ returning a string like "20C" (the number followed by C), __eq__ so two temps are equal when their degrees match, and __lt__ comparing by degrees so a list of temps can be sorted.

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output