The difference between __str__ and __repr__ in Python
Both turn an object into a string. __str__ is for people, __repr__ is for
you when something has gone wrong — and if you only ever define one of them,
define __repr__.
The difference on built-in types
str : a string repr : 'a string'
str : 3.0 repr : 3.0
str : 2026-08-29 repr : datetime.date(2026, 8, 29)
str : 0.1 repr : Decimal('0.1')
str : None repr : None
The date row shows the intent. str gives you something to put on a page;
repr gives you something that says exactly what the object is, including its
type.
The string row is the one you meet daily: repr keeps the quotes, so you can
tell 1 from "1".
Which one you get, where
print(s) : line
break
print([s]) : ['line\nbreak']
print() uses str. A container uses repr for its items — always, even
when you print the container. That is why a list of your objects looks
different from printing one of them.
The interactive interpreter echoes repr, which is why typing a value at the
prompt shows quotes and print() does not.
In an f-string you choose: !s for str, !r for repr.
Define repr first
This is the practical rule, and it follows from a fallback:
class OnlyRepr:
def __repr__(self): return "OnlyRepr(x=1)"
str(o) : OnlyRepr(x=1)
repr(o) : OnlyRepr(x=1)
str() falls back to __repr__ when there is no __str__. So defining
__repr__ alone gives you sensible output everywhere.
The reverse does not hold:
with only __str__, repr(p) : <__main__.OnlyStr object at 0x...
[p] in a list : [<__main__.OnlyStr object at 0x7ede07e3738...
Define only __str__ and your objects are unreadable in a list, a dict, a
debugger, or a logged data structure — which is exactly when you need to read
them.
Both, when they differ
class Money:
def __init__(self, c): self.cents = c
def __repr__(self): return f"Money(cents={self.cents})"
def __str__(self): return f"{self.cents / 100:.2f} EUR"
print(m) : 4.99 EUR
repr(m) : Money(cents=499)
[m] : [Money(cents=499)]
The user sees 4.99 EUR. You see Money(cents=499), which tells you it is
cents and not euros — the distinction you would want at three in the morning.
The rule for repr
The documentation asks for something unambiguous, and ideally something you could paste back in:
repr(a date) : datetime.date(2026, 8, 29)
eval works? : True
str(a date) : 2026-08-29 <- not valid Python
eval(repr(x)) == x is a target rather than a requirement — plenty of objects
cannot manage it. When you cannot, the convention is angle brackets to signal
“this is a description, not an expression”:
def __repr__(self):
return f"<Connection to {self.host}, {'open' if self.open else 'closed'}>"
The float case
str : 0.30000000000000004
repr : 0.30000000000000004
float(repr(f)) == f : True
Since Python 3.1 these agree for floats, and both give the shortest string
that round-trips exactly. Before that, str() rounded to 12 significant
digits and repr() gave 17, which is where the old advice about the two
differing comes from. It is out of date.
Log repr, not str
str(exc) : division by zero
repr(exc) : ZeroDivisionError('division by zero')
str() of an exception gives the message and nothing else. repr() gives the
message and the type. In a log file, “division by zero” without the class
name is a good deal less useful, and the difference costs nothing.
The same reasoning applies to logging: %r rather than %s for anything you
might have to identify later.
One caveat about dataclasses
@dataclass generates a
__repr__ for you, in exactly the form described above. So for a plain data
holder you get this for free and should not write one by hand. It does not
generate a __str__, which is the right default — most objects do not need
one.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).