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, ...).