+ BASH HEAD GET BYTES +

← Back to Blog

__repr__ vs __str__

41 days ago · 61 views

I thought I understood __repr__ and __str__. I was wrong.

Every time I needed a string representation, I'd write __str__ first. It felt natural: str() converts things to strings, so obviously that's the one you implement.

Then I'd run into a situation where my object printed weirdly in a list or the debugger, and I'd get annoyed and add __repr__ as an afterthought.

This is backwards.

The Thing that was not clear

__repr__ is not the "technical" version of __str__. It's the foundation. If you only implement one, implement __repr__.

Here's why: if __str__ is missing, Python falls back to __repr__. But the reverse isn't true. If you only implement __str__, your objects will look broken everywhere except inside print() statements.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"

p = Point(3, 4)
print(p)        # (3, 4) - looks fine
print([p])      # [<__main__.Point object at 0x...>] - broken

That second line? That's what happens when you put your object in a container. Containers don't call __str__. They call __repr__. And if you didn't implement it, you get garbage.

The Actual Rule

__repr__ is for you, the developer. It should be unambiguous. Ideally, you could paste it into the REPL and recreate the object. Point(x=3, y=4) is a good __repr__.

__str__ is for people who don't care about the internal structure. It can be vague, pretty, formatted however you want. (3, 4) is fine for __str__.

Most of the time, you don't need __str__ at all. Just write a good __repr__ and call it done.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

Now it works everywhere. Debugger, REPL, lists, logs. One method.

What Changed

I stopped thinking of __repr__ as the "optional debugging thing" and started treating it as the default. If I want something prettier for end users later, I can add __str__. But __repr__ comes first now.

I still mess this up sometimes. Old habits. But at least now I know why my objects keep looking broken in the debugger.