skip to content

Why does printing a list of your objects show `__repr__` output instead of `__str__`?

level: middleimportance: should knowfreq 58%

answer

  1. The container is asked, not the item
  2. Nested text must stay unambiguous
  3. Quotes around a nested string matter
  4. str() of a list equals repr() of it
  5. Elements are rendered with repr()

basics

~20 s

print(items) calls str() on the list, and a list builds its own text by calling repr() on every element. Containers never call __str__ on their items, so an element's __str__ is skipped one level down.

solid answer

~40 s

`print(items)` calls `str(items)`. A list has no `__str__` of its own, so it inherits the one that delegates to `list.__repr__`, and that implementation formats each element with `repr()` rather than `str()`. Tuples, sets and dicts behave the same way, and a dict does it for keys as well as values. It is deliberate: a container's text has to be unambiguous and nestable — quoting strings so `1` is visibly different from `'1'` — which is precisely the `__repr__` contract, not the `__str__` one. So a class with a pretty `__str__` and no `__repr__` prints as `[<Track object at 0x...>, ...]`. Two fixes: define `__repr__` on the element class, which is the real one, or format the elements yourself with something like `", ".join(str(t) for t in items)`.

code

python · 16 lines
python
class Track:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"<Track {self.name}>"

    def __str__(self):
        return self.name


t = Track("audio-0")
print(t)                    # audio-0
print([t])                  # [<Track audio-0>]
print({"stream": t})        # {'stream': <Track audio-0>}
print(", ".join(str(x) for x in [t, t]))   # audio-0, audio-0

go deeper

for a junior

Recall the outcome and the fix: collections show item reprs, so a class needs __repr__ before its instances look sensible inside a list. Adding only __str__ will not change what a printed list shows.

for a middle

Explain the chain: print calls str on the container, the container inherits __str__ from object, that delegates to the container's __repr__, and that renders each element with repr(). Give the quoting argument for why.

for a senior

Connect it to debuggability. Every collection in a log line, exception message, debugger pane or failed assertion shows item reprs, so a missing __repr__ quietly degrades diagnosis across the whole system, not just at the print statement.

for a principal

Frame it as a codebase policy question: which domain types must carry a __repr__, what a review should reject, and how much of your production log quality depends on that one dunder being present on the objects you log.

This trips people up because the fallback they just learned appears to stop working. It does not — the object never gets asked for its `__str__` in the first place. ## What actually happens 1. `print(items)` evaluates `str(items)`, where `items` is the list. 2. `list` defines no `__str__`, so it inherits `object.__str__`, which delegates to `list.__repr__`. You can check this directly: `list.__str__ is object.__str__` is `True`, and so is the same expression for `tuple` and `dict`. 3. `list.__repr__` then walks the elements and calls `repr()` on each one, joining the results with `, ` inside square brackets. Your element's `__str__` is never consulted. `str(items)` and `repr(items)` therefore produce identical text for a list — a small fact that surprises people and is worth checking in a REPL once. ## Why the containers chose repr A container's text is a nested structure, and nested text has to be readable without ambiguity. Consider `['a', 1]`. - With `repr()` on the elements you get `['a', 1]`, and you can see that the first item is a string and the second an integer. - With `str()` you would get `[a, 1]`, where `a` might be the string `'a'`, a name, or an object whose `__str__` happened to return `a`. The moment elements can be strings, the `str()` rendering becomes lossy. The same reasoning applies to a dict, which reprs both keys and values, which is why `{'a': 'b'}` shows quotes on both sides. ## The knock-on effects Because reprs are what containers show, a missing `__repr__` degrades every place a collection is displayed: - a printed list, - a dict of objects in a log line, - an argument tuple inside an exception message, - a debugger's variables pane, - the value shown when a test assertion over a list fails. All of them collapse into indistinguishable `<Track object at 0x10a2f1c30>` entries. This is the single most common practical reason to write `__repr__` first: the payoff shows up in tooling you did not write. ## The fixes, in the right order 1. The real fix is to give the element class a `__repr__` that identifies the instance — `<Track 'audio-0'>` or `Track(name='audio-0')`. Adding a `__str__` to the element class does nothing for the list, because nothing on that path calls it. 2. If you genuinely need the readable form for each element in output, build the string explicitly: `", ".join(str(t) for t in items)`, or a comprehension of f-strings. Do not try to subclass `list` to change how elements are rendered; the element type is the one carrying the wrong information, and a container subclass that reprs its items differently from every other container is a trap for the next reader. ## Related surfaces worth knowing - The `pprint` module builds its output from `repr()` too, so pretty-printing a nested structure inherits the same rule. - `%`-formatting a container with `%s` gives the same text as `str()` on it, which — as above — is the repr rendering of the items. - And an f-string with no conversion, `f"{items}"`, routes through `__format__`, whose inherited implementation returns `str(items)`: again the item reprs. ## How to answer this in an interview 1. State the mechanism first: `print` calls `str` on the container, the container's text is built from `repr()` of each element, so element `__str__` is bypassed. 2. Then give the reason: unambiguous, nestable output. 3. Then give the fix: define `__repr__` on the element class, or format the elements explicitly. Candidates who say "lists call `__repr__` because Python is weird" have memorised the symptom; candidates who mention the quoting of nested strings have understood why the rule exists. ## Why `str` itself is the exception that proves the rule Of the built-in types, `str` is the one where `str()` and `repr()` genuinely differ: `str('a')` is `a`, while `repr('a')` is `'a'` with quotes. That single difference is the whole reason containers chose repr. Rendering `['a', 1]` with element `str()` would give `[a, 1]`, in which the string and the integer are indistinguishable, and a value containing a comma or a bracket would silently corrupt the shape of the output. Rendering with `repr()` keeps the text parseable by eye and, for simple structures, by `eval` too. Sets, tuples, nested lists and dicts all follow the same rule, all the way down, so a deeply nested structure prints as one consistent, unambiguous expression rather than a mixture of two conventions.

  • How do you print the `__str__` form of every element of a list?
    Build the text yourself: `", ".join(str(x) for x in items)`, or a comprehension of f-strings joined the same way. There is no switch that makes a container render its items with `str()`, and subclassing the container to change it would surprise every reader who expects standard behaviour.
  • Does a dict use `repr()` on its keys as well as its values?
    Yes, both. `{'a': 'b'}` shows quotes on the key and the value because each side is rendered with `repr()`. That is what lets you distinguish the string key `'1'` from the integer key `1` in a printed dict — a distinction that would vanish if keys were rendered with `str()`.
  • Why does `str(items)` equal `repr(items)` for a list?
    Because `list` never defines `__str__`. It inherits `object.__str__`, which simply calls `__repr__`, so both functions end up in `list.__repr__`. The same holds for tuples, sets and dicts. Only types that define their own `__str__` — such as `str` itself — make the two differ.

saying these in an interview costs you the question

  • Saying a printed list calls each item's __str__
  • Trying to fix it by subclassing list
  • Thinking str() and repr() of a list differ
  • Assuming nested strings lose their quotes
  • Adding __str__ to the item class as the fix

context