vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns str() in Python 2026: String Conversion + Modern Formatting & Best Practices type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns super() in Python 2026: Method Resolution & Modern Inheritance Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices round() in Python 2026: Rounding Numbers + Modern Precision & Use Cases reversed() in Python 2026: Reverse Iteration + Modern Patterns & Best Practices repr() in Python 2026: Official String Representation + Modern Debugging & Serialization Use Cases pow() in Python 2026: Power & Modular Exponentiation + Modern Use Cases & Best Practices property() in Python 2026: Properties, Getters/Setters & Modern Patterns ord() in Python 2026: Unicode Code Point from Character + Modern Use Cases & Best Practices open() in Python 2026: File Handling + Modern I/O Patterns & Best Practices range() in Python 2026: Efficient Sequence Generation + Modern Patterns & Best Practices print() in Python 2026: Output Formatting + Modern CLI & Debugging Patterns View all in this category →

repr() in Python 2026: Official String Representation + Modern Debugging & Serialization Use Cases

Built In Function Mar 24, 2026

repr() in Python 2026: Official String Representation + Modern Debugging & Serialization Use Cases

The built-in repr(obj) returns the official string representation of an object, meant for developers rather than end users. When you inspect a value in the REPL, print a list, read a log line, or look at a failing test, you are usually looking at repr() output. This guide explains how it differs from str(), how to write a good __repr__, the f-string shortcuts, how to keep huge or recursive objects readable, and why repr() is not a serialization format.

All examples run unchanged on Python 3.13.

TL;DR: Key Takeaways

  • repr(obj) calls type(obj).__repr__(obj) and must return a str
  • str() is for readers; repr() is for developers, and it should be unambiguous
  • Where practical, make eval(repr(obj)) == obj true; otherwise use the <ClassName ...> form
  • Containers such as lists and dicts always show their items with repr()
  • In f-strings, {x!r} applies repr() and {x=} prints the name together with the repr
  • Use reprlib for very large or self-referencing objects
  • Use json or ast.literal_eval to store and load data, never eval() on untrusted text

1. repr() vs str(): Same Object, Two Audiences

str() gives a friendly, readable form. repr() shows exactly what the value is: quotes, escape sequences and all. That precision is why debuggers and the interactive prompt use it.


text = "Hello\tPyInns\n"
print(str(text))
print(repr(text))
print(repr(3.0), repr([1, "two", None]))

Output:


Hello	PyInns

'Hello\tPyInns\n'
3.0 [1, 'two', None]

The str() version hides the tab and the trailing newline. The repr() version shows them, which is usually what you need when you are chasing a whitespace bug.

2. Writing a Good __repr__ for Your Own Classes

Without __repr__, your objects look like <__main__.Point object at 0x7f...>, which tells you almost nothing. A good repr looks like the code that would rebuild the object:


class Point:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return f"{type(self).__name__}(x={self.x!r}, y={self.y!r})"

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


p = Point(1.5, -2)
print(repr(p))       # Point(x=1.5, y=-2)
print(str(p))        # (1.5, -2)
print([p])           # containers use repr: [Point(x=1.5, y=-2)]
print(eval(repr(p)).x)  # 1.5 -- round-trips because the repr is valid Python

Two habits are worth copying from this example:

  • Use !r on each field so strings keep their quotes. Point(x='1') and Point(x=1) are different objects.
  • Use type(self).__name__ rather than a hard-coded class name, so subclasses show their own name.

If the object cannot be rebuilt from a short expression (an open file, a database connection, a thread), follow the convention of the standard library and return something like <Connection host='db1' open=True>.

3. f-string Shortcuts: !r and the = Specifier


user = "ada"
retries = 3
path = "C:\\temp\\new"

print(f"{user!r} has {retries} retries")   # 'ada' has 3 retries
print(f"{user=}, {retries=}")               # user='ada', retries=3
print(f"{path=}")                           # path='C:\\temp\\new'
print(f"{path=!s}")                         # path=C:\temp\new

{name=} (available since Python 3.8) is one of the fastest debugging tools in the language. It uses repr() by default, and you can switch to str() with {name=!s}.

4. Dataclasses Generate repr() for You

@dataclass writes a __repr__ from the field list. Mark sensitive fields with repr=False so tokens and passwords never end up in logs or tracebacks.


from dataclasses import dataclass, field


@dataclass
class Order:
    order_id: int
    items: list[str] = field(default_factory=list)
    api_token: str = field(default="", repr=False)  # hide secrets from repr


o = Order(42, ["book", "pen"], api_token="s3cr3t")
print(o)   # Order(order_id=42, items=['book', 'pen'])

5. Keeping Huge or Recursive Objects Readable with reprlib

Logging the full repr of a 10,000-item list floods your logs. reprlib.repr() shortens the output with sensible limits, and a reprlib.Repr instance lets you set your own:


import reprlib

big = list(range(10_000))
print(reprlib.repr(big))           # [0, 1, 2, 3, 4, 5, ...]

short = reprlib.Repr(maxlist=3, maxstring=12)
print(short.repr(["alpha", "beta", "gamma", "delta"]))
print(short.repr("a very long log message"))

Output:


[0, 1, 2, 3, 4, 5, ...]
['alpha', 'beta', 'gamma', ...]
'a v...sage'

Objects that contain themselves would recurse forever in a hand-written __repr__. The @reprlib.recursive_repr() decorator replaces the repeated reference with ...:


from reprlib import recursive_repr


class Node:
    def __init__(self, name: str) -> None:
        self.name = name
        self.children: list["Node"] = []

    @recursive_repr()
    def __repr__(self) -> str:
        return f"Node({self.name!r}, children={self.children!r})"


root = Node("root")
root.children.append(root)   # a cycle
print(repr(root))            # Node('root', children=[...])

6. repr() Is Not Serialization

For built-in literals, repr() output is valid Python, so it can be tempting to save data with repr() and load it back with eval(). Don't. eval() runs arbitrary code. Use ast.literal_eval() when you need to read Python literals, and use json (or a format such as TOML) when you exchange data with other programs:


import ast
import json

settings = {"debug": True, "ratio": 0.25, "tags": ("a", "b")}

as_repr = repr(settings)
print(as_repr)
print(ast.literal_eval(as_repr) == settings)    # True: safe for literals only

print(json.dumps({"debug": True, "ratio": 0.25, "tags": ["a", "b"]}))

Output:


{'debug': True, 'ratio': 0.25, 'tags': ('a', 'b')}
True
{"debug": true, "ratio": 0.25, "tags": ["a", "b"]}

7. Best Practices Checklist

  • Give every class you debug often a __repr__. Add __str__ only if users need a different, friendlier form.
  • Make the repr unambiguous and short, and include the fields that identify the object.
  • Never include secrets. Use field(repr=False) in dataclasses or leave them out by hand.
  • Keep __repr__ cheap and free of side effects. Debuggers call it often, sometimes in the middle of an error.
  • Log with %r or {value!r} when the exact value matters.

Conclusion

repr() is a small function that does a lot of work every time you debug. Write clear __repr__ methods, use {x=} and !r in f-strings, and use reprlib for big structures. Keep repr() for people reading your output, and use json or ast.literal_eval for data.

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices