Chapters
Python114 chapters

Classes and objectsChapter 61 of 114

The __str__ Method

Decide what your object looks like when it is printed.

The default is useless

Python
class Dog:
    def __init__(self, name):
        self.name = name

dog = Dog("Rex")
print(str(dog).startswith("<"))
print("Dog object" in str(dog))

Output

True
True

The address changes every run, and nothing tells you which dog it is.

__str__ is for people

Python
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name}, aged {self.age}"

rex = Dog("Rex", 3)
print(rex)
print(str(rex))
print(f"The dog is {rex}")

Output

Rex, aged 3
Rex, aged 3
The dog is Rex, aged 3

print(), str() and f-strings all go through __str__. It must return a string.

__repr__ is for programmers

__repr__ should be unambiguous, and ideally look like the code that would rebuild the object. It is what you see at the prompt, in a traceback, and inside a container:

Python
class Dog:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"Dog({self.name!r})"

rex = Dog("Rex")
print(repr(rex))
print([rex, rex])

Output

Dog('Rex')
[Dog('Rex'), Dog('Rex')]

Note the second line: printing a list of objects uses __repr__, not __str__. That is why a class with only __str__ still shows addresses inside a list.

The !r in the f-string means "use repr for this value", which is what puts the quotes around the name.

Which to write

If you only write one, write __repr__. str() falls back to it, so you get both:

Python
class Dog:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"Dog({self.name!r})"

rex = Dog("Rex")
print(str(rex))
print([rex])

Output

Dog('Rex')
[Dog('Rex')]

The reverse is not true — a class with only __str__ keeps the default, useless __repr__.

Both, when they differ

Write both when the friendly form and the precise form genuinely differ:

Python
class Money:
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency

    def __str__(self):
        return f"{self.amount:.2f} {self.currency}"

    def __repr__(self):
        return f"Money({self.amount!r}, {self.currency!r})"

price = Money(4.5, "EUR")
print(price)
print(repr(price))

Output

4.50 EUR
Money(4.5, 'EUR')

Other useful dunder methods

The same idea extends to operators and builtins:

Python
class Basket:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

    def __eq__(self, other):
        return self.items == other.items

    def __contains__(self, item):
        return item in self.items

a = Basket(["apple", "pear"])
b = Basket(["apple", "pear"])

print(len(a))
print(a == b)
print("pear" in a)
print(bool(Basket([])))

Output

2
True
True
False

bool() falls back to __len__ when there is no __bool__, which is why the empty basket is falsy for free.

Test yourself

2 questions

Which method does printing a list of your objects use?

Show the answer

__repr__ — That is why a class with only __str__ still shows memory addresses inside a container.

If you write only one of them, which should it be?

Show the answer

__repr__, because str() falls back to it — The fallback only goes one way: __repr__ covers both, __str__ does not.

Next chapter

Methods

Functions that belong to a class, and the three kinds of them.