Python repr Function: How It Works and When to Use It
python repr function: Understand how Python's repr function creates unambiguous string representations, how to override __repr__ in your classes, and when to use it fo...
Python repr Function: How It Works and When to Use It
The python repr function is a built-in that returns a string representation of an object. Unlike str(), which aims for readability, repr() aims for unambiguity: the returned string should, whenever possible, be a valid Python expression that recreates the object when passed to eval(). This distinction matters in debugging, logging, and interactive sessions.
What the repr Function Actually Returns
When you call repr(obj), Python looks for a __repr__ method on the object's class. If found, it calls that method and expects a string. If not found, it falls back to a default implementation that includes the object's type and memory address, like <__main__.MyClass object at 0x7f8a1c2b3d40>. The default is rarely useful for debugging because it doesn't tell you anything about the object's state.
For built-in types, repr is often defined to produce a valid expression. For example:
>>> repr([1, 2, 3]) '[1, 2, 3]' >>> repr({"a": 1}) "{'a': 1}" >>> repr((1, 2)) '(1, 2)'
Notice that strings are quoted inside the representation:
>>> repr("hello") "'hello'"
This is intentional. The representation must be unambiguous about the type. If you saw hello without quotes, you might think it's a variable name or an identifier. The quotes make it clear it's a string.
The Difference Between repr and str
str() and repr() serve different purposes. str() is meant for human consumption; repr() is meant for developers and debugging. The print() function uses str() on its arguments, while the interactive interpreter uses repr() to display results.
For many classes, __str__ and __repr__ can be the same, but they should not be. Consider a Person class:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f"{self.name} ({self.age})" def __repr__(self): return f"Person('{self.name}', {self.age})"
Here, str(person) gives John (30), which is readable. repr(person) gives Person('John', 30), which is a valid constructor call. This is the recommended pattern: __repr__ should be informative and, if feasible, reconstruct the object.
Overriding repr in Your Classes
When you define a class, you should almost always implement __repr__. The default object representation is nearly useless. A good __repr__ should include the class name and the essential attributes that define the instance's state.
class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Point({self.x}, {self.y})"
Now repr(point) returns Point(3, 4). This is both informative and a valid expression. If your class has many attributes, you might choose to include only the most important ones to keep the output concise. The goal is to make debugging easier, not to serialize the entire object.
If you want the representation to be exactly evaluable, you need to be careful with strings and other types. For example, if a name contains a single quote, the naive f-string will break:
class Person: def __init__(self, name): self.name = name def __repr__(self): return f"Person('{self.name}')"
repr(Person("O'Brien")) would produce Person('O'Brien'), which is not a valid expression. A safer approach is to use repr() on the attribute itself:
def __repr__(self): return f"Person({self.name!r})"
The !r conversion flag calls repr() on the value, so strings get properly quoted and escaped. This is a common pattern in __repr__ implementations.
Using repr in Debugging and Logging
The repr function is invaluable in debugging. When you log an object's state, using repr gives you a precise view of what the object contains. Many logging frameworks and assertion messages automatically use repr for non-string arguments.
For example, in an f-string:
user = User("alice", 42) logger.info("User created: %r", user)
The %r formatting specifier calls repr(). This is often better than %s because it shows the exact type and content.
When you're debugging a list of objects, repr is applied to each element automatically when you print the list:
print([Person("Alice", 30), Person("Bob", 25)])
This will output [Person('Alice', 30), Person('Bob', 25)] if you've implemented __repr__ correctly. Without it, you'd see memory addresses, which are useless for understanding the data.
The repr of Built-in Types and Containers
Containers like lists, dictionaries, and sets use repr on their elements to build their own representation. This means if you implement __repr__ on your custom class, it will automatically appear correctly inside containers.
class Tag: def __init__(self, name): self.name = name def __repr__(self): return f"Tag('{self.name}')" tags = [Tag("python"), Tag("debugging")] print(tags) # [Tag('python'), Tag('debugging')]
This behavior makes repr especially useful for logging collections of domain objects. However, be cautious with recursive structures. If an object contains a reference to itself, a naive __repr__ will cause infinite recursion. For example:
class Node: def __init__(self, value): self.value = value self.next = None def __repr__(self): return f"Node({self.value}, next={self.next!r})"
If you create a cycle, calling repr on the node will recurse until Python raises a RecursionError. To handle this, you need to detect cycles or limit the depth. A common approach is to use a reprlib helper or to check if you've already seen the object.
Common Pitfalls and Edge Cases
One common mistake is forgetting to return a string from __repr__. If you accidentally return an integer, Python will raise a TypeError when you call repr(). The method must always return a str.
Another issue is relying on eval(repr(obj)) to recreate the object. This only works if the representation is a valid Python expression and the object's constructor accepts those arguments. For many classes, this is not feasible, especially if the object holds file handles or network connections. In those cases, __repr__ should still be informative, but you shouldn't promise that eval will work.
The repr function also has a sibling called ascii(), which is like repr but escapes non-ASCII characters. This can be useful when you need to ensure the output is ASCII-safe, for example, when writing logs that might be consumed by systems that don't handle Unicode well.
When to Use repr Instead of Alternatives
You should use repr whenever you need a precise, unambiguous representation of an object for debugging, logging, or error messages. For user-facing output, use str. For serialization, consider json.dumps or pickle rather than relying on repr.
A practical rule: implement __repr__ on every class you create, even if you don't plan to use it immediately. The cost is low, and it pays off when you're debugging later. The __repr__ should be as informative as possible without being overly verbose.
When you're working in an interactive session, Python automatically displays the repr of expressions. This is why you see [1, 2, 3] when you type a list, not the str representation. Understanding this behavior helps you interpret REPL output correctly.