datarekha
Python Easy Asked at GoogleAsked at AmazonAsked at Meta

What is the difference between `__str__` and `__repr__` in Python, and which should you implement first?

The short answer

`__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.

HookCalled byAudienceGoal
__repr__repr(), the REPL, !r in f-stringsdevelopersunambiguous; ideally eval(repr(obj)) == obj
__str__str(), print(), plain f-stringsend usersreadable

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__ → both str(obj) and repr(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.

Learn it properly Dunder Methods

Keep practising

All Python questions

Explore further

Skip to content