Classes and objectsChapter 61 of 114
The __str__ Method
Decide what your object looks like when it is printed.
The default is useless
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
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:
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:
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:
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:
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 questionsWhich 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.
Methods
Functions that belong to a class, and the three kinds of them.