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.