What is __repr__ and why should I write one?
Short answer
__repr__ defines how an object appears when printed in a console or inside a list, so a good one makes debugging far easier. Aim for a string that shows the object's key values, ideally one you could paste back in.
The short version
__repr__ controls how your object appears in a console, in a traceback and inside a list. Without it, you see an unhelpful memory address. A good __repr__ shows the values that identify the object, ideally in a form you could paste back into Python to recreate it.
The default is useless
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
print(Point(1, 2)) # <__main__.Point object at 0x7f8b1c0d5e50>
print([Point(1, 2)]) # [<__main__.Point object at 0x7f8b1c0d5e50>]
The memory address tells you nothing. Inside a list of fifty of these, debugging becomes guesswork.
Writing one
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point(x={self.x}, y={self.y})"
print(Point(1, 2)) # Point(x=1, y=2)
print([Point(1, 2)]) # [Point(x=1, y=2)]
Now the object describes itself wherever it appears, including inside containers, which is exactly where the default fails you.
The guiding rule
Aim for a string that would recreate the object if you pasted it back into Python. That is a useful discipline, because it forces you to include the values that actually define the object and nothing else. When that is impractical (for a large object, say), use angle brackets to signal that it is a description rather than valid code.
def __repr__(self):
return f"<DataSet rows={len(self.rows)} source={self.source!r}>"
The !r applies repr to the nested value, so strings appear with their quotes, which keeps the output unambiguous.
repr versus str
__repr__ is for developers: unambiguous and detailed. __str__ is for users: readable and friendly. If you define only __repr__, Python uses it for both, which is why writing __repr__ first gives the most benefit for one method.
str(obj) # uses __str__ if defined, otherwise falls back to __repr__
repr(obj) # always uses __repr__
For most internal classes, a good __repr__ alone is enough.
Where it pays off
Every debugging session, every log line, every failed test that prints an object. A meaningful repr turns "assertion failed: object at 0x7f8b" into "assertion failed: Point(x=1, y=2)", which is the difference between an immediate diagnosis and ten minutes of poking around.
You get it free with dataclasses
A dataclass generates a sensible __repr__ automatically, showing every field.
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
print(Point(1, 2)) # Point(x=1, y=2)
This is one of the strongest arguments for using dataclasses for simple record types: you get the useful repr, plus equality, without writing either.