What is the difference between __str__ and __repr__ in Python, and which should you implement first?
repr is the developer-facing representation — unambiguous, ideally eval-able back to the object. str is the user-facing string — readable and concise. When only repr is defined, Python falls back to it for str() as well, so implement repr first.
How to think about it
The interviewer wants to know whether you grasp that Python keeps two different representations of an object — show me this for debugging and show me this for a human — and whether you know the one-way fallback between them: str() falls back to repr(), but never the reverse.
| Hook | Called by | Audience | Goal |
|---|---|---|---|
__repr__ | repr(), the REPL, !r in f-strings | developers | unambiguous; ideally eval(repr(obj)) == obj |
__str__ | str(), print(), plain f-strings | end users | readable |
Put plainly: __repr__ is what you want in a debugger or an error log; __str__ is what you’d put in a UI or a report.
A worked example
A Transaction makes the split concrete — a developer debugging a payment pipeline wants to reconstruct the exact object; a customer reading a receipt just wants the summary:
from datetime import date
class Transaction:
def __init__(self, amount, currency, on):
self.amount = amount
self.currency = currency
self.on = on
def __repr__(self):
return (f"Transaction(amount={self.amount!r}, "
f"currency={self.currency!r}, on={self.on!r})")
def __str__(self):
return f"{self.currency} {self.amount:.2f} on {self.on}"
t = Transaction(49.99, "USD", date(2026, 6, 6))
print("repr:", repr(t)) # developer-facing
print("str: ", str(t)) # user-facing
print("f-string default:", f"{t}") # plain {t} -> __str__
print("f-string with !r:", f"{t!r}") # {t!r} -> __repr__
# Inside a collection, Python always uses repr() on each element
print("inside list:", [t, Transaction(20.00, "EUR", date(2026, 6, 7))])
repr: Transaction(amount=49.99, currency='USD', on=datetime.date(2026, 6, 6))
str: USD 49.99 on 2026-06-06
f-string default: USD 49.99 on 2026-06-06
f-string with !r: Transaction(amount=49.99, currency='USD', on=datetime.date(2026, 6, 6))
inside list: [Transaction(amount=49.99, currency='USD', on=datetime.date(2026, 6, 6)), Transaction(amount=20.0, currency='EUR', on=datetime.date(2026, 6, 7))]
Notice the last line: the list printed each transaction with its __repr__, never its __str__, even though we never asked for repr explicitly. Collections always show their elements’ developer representation.
The fallback chain
When __str__ is missing, Python falls back to __repr__. There is no fallback the other way:
- Define only
__repr__→ bothstr(obj)andrepr(obj)work, with the same output. - Define only
__str__→repr(obj)shows the useless<ClassName object at 0x…>.
That asymmetry is the whole reason for the rule: write __repr__ first, and add __str__ only when a distinct user-facing form is actually worth it.