This comes third, before properties or inheritance, because a class you cannot read in a traceback costs you time every single day you have it.
The default is useless
class Order:
def __init__(self, id_, total):
self.id, self.total = id_, total
o = Order(17, 249.5)
print(o)
print([o, o])
It prints something like:
<__main__.Order object at 0x7daca41360f0>
[<__main__.Order object at 0x7daca41360f0>, <__main__.Order object at 0x7daca41360f0>]
The address will differ on your machine, and that is the whole problem: the only thing Python can tell you is where the object lives in memory, which is never what you wanted to know.
__repr__ fixes it everywhere at once
class Order2:
def __init__(self, id_, total):
self.id, self.total = id_, total
def __repr__(self):
return f"Order2(id={self.id!r}, total={self.total!r})"
o2 = Order2(17, 249.5)
print(o2)
print([o2, o2])
It prints:
Order2(id=17, total=249.5)
[Order2(id=17, total=249.5), Order2(id=17, total=249.5)]
Note the second line. Containers use repr on their contents, which is why a list of objects with no __repr__ is a wall of addresses.
The convention is to return something you could paste back into Python to rebuild the object. Order2(id=17, total=249.5) meets that. Use !r on the values inside it, or a string field will lose its quotes and stop being valid.
str is for the reader, repr is for you
class Order3:
def __init__(self, id_, total):
self.id, self.total = id_, total
def __repr__(self):
return f"Order3(id={self.id!r}, total={self.total!r})"
def __str__(self):
return f"Order #{self.id} — {self.total:,.2f}"
o3 = Order3(17, 249.5)
print(str(o3))
print(repr(o3))
print(f"{o3}")
print(f"{o3!r}")
It prints:
Order #17 — 249.50
Order3(id=17, total=249.5)
Order #17 — 249.50
Order3(id=17, total=249.5)
print() and f-strings use str. The REPL, containers, and !r use repr.
Define __repr__ first, __str__ only if you need it
The fallback goes one way only. Define __repr__ alone and str() uses it:
class Minimal:
def __init__(self, v): self.v = v
def __repr__(self): return f"Minimal({self.v!r})"
m = Minimal(1)
print(str(m), '|', repr(m))
It prints:
Minimal(1) | Minimal(1)
Define only __str__ and repr() still gives you the address. So __repr__ is the one that always earns its place; __str__ is worth adding when the object is shown to a person.
Where it actually pays off
Error messages and tracebacks:
def charge(order):
raise ValueError(f"cannot charge {order!r}")
try:
charge(o3)
except ValueError as err:
print(err)
try:
charge(o)
except ValueError as err:
print(str(err)[:46] + '...')
It prints:
cannot charge Order3(id=17, total=249.5)
cannot charge <__main__.Order object at 0x7dac...
The first tells you which order failed. The second tells you an object failed and sends you back to reproduce it. Over a year that difference is hours.
What to remember
-
Always write
__repr__. It is the cheapest debugging improvement available. -
Make it look like the call that would rebuild the object, and use
!ron the fields. -
__str__is for output a person reads. It is optional, and falls back to__repr__. -
Containers and
!ruserepr;printand f-strings usestr.
Dataclasses generate __repr__ for you, which is one of the better reasons to reach for one. Part 8 covers that choice.