What is the purpose of the __repr__ method?
Categories:
Understanding Python's repr Method: A Guide to Object Representation

Explore the purpose and best practices of Python's __repr__
magic method, crucial for debugging and developer-friendly object representations.
In Python, objects are at the core of everything. When you're working with custom classes, you often need a way to represent instances of those classes in a human-readable and unambiguous format. This is where the __repr__
magic method comes into play. Unlike __str__
, which is designed for end-user readability, __repr__
is primarily for developers, providing a clear, unambiguous, and often evaluable string representation of an object. Understanding and implementing __repr__
correctly is a hallmark of good Python programming, significantly aiding in debugging and development.
The Core Purpose of repr
The __repr__
method (short for 'representation') is one of Python's 'magic methods' or 'dunder methods' (due to their double underscores). Its primary goal is to provide an 'official' string representation of an object. The Python documentation suggests that __repr__
should return a string that, if passed to eval()
, would ideally recreate the object. While this isn't always strictly possible or desirable for complex objects, the spirit of this guideline is to make the representation as unambiguous and informative as possible for a developer.
When you type an object's variable name into a Python interpreter or debugger, or use the repr()
built-in function, Python calls the object's __repr__
method. This makes it invaluable for inspecting the state of objects during development and debugging sessions. Without a custom __repr__
, Python falls back to a default representation that is often unhelpful, showing only the object's type and memory address.
class MyClass:
def __init__(self, value):
self.value = value
# Without __repr__
obj_no_repr = MyClass(10)
print(obj_no_repr) # Output: <__main__.MyClass object at 0x...>
# With __repr__
class MyClassWithRepr:
def __init__(self, value):
self.value = value
def __repr__(self):
return f"MyClassWithRepr(value={self.value})"
obj_with_repr = MyClassWithRepr(20)
print(obj_with_repr) # Output: MyClassWithRepr(value=20)
# Using repr() function
print(repr(obj_with_repr)) # Output: MyClassWithRepr(value=20)
Demonstrating the difference between objects with and without a custom __repr__
.
__repr__
to be unambiguous. If you have a choice between two representations, choose the one that makes it clearer what the object is and how it was constructed.Relationship with str and Best Practices
While __repr__
is for developers, __str__
is for end-users. The str()
built-in function and the print()
statement typically call __str__
. If __str__
is not defined for a class, Python falls back to __repr__
. This means that if you only define __repr__
, it will serve both purposes, which is often a good default strategy, especially for simple objects.
Best Practices for __repr__
:
- Unambiguous: The representation should clearly indicate the object's type and its essential attributes.
- Evaluable (if possible): Strive to make the output a valid Python expression that could recreate the object. For example,
ClassName(arg1=value1, arg2=value2)
. - Include Class Name: Always start the representation with the class name to avoid confusion.
- Use f-strings: F-strings (formatted string literals) are excellent for creating
__repr__
strings due to their readability and conciseness. - Handle Recursion: Be mindful of circular references if your objects can contain references to each other, as this can lead to infinite recursion when generating the representation.
Consider a scenario where you're debugging a list of custom objects. A well-defined __repr__
will allow you to quickly understand the state of each object in the list, rather than just seeing a series of memory addresses.
flowchart TD A[Object Instance] --> B{Is __repr__ defined?} B -- Yes --> C[Call __repr__()] B -- No --> D[Default repr() (type, memory address)] C --> E[Developer-friendly string] D --> E E --> F{Is __str__ defined?} F -- Yes --> G[Call __str__()] F -- No --> H[Use __repr__() output] G --> I[End-user friendly string] H --> I I --> J[Output to print() or str()] E --> K[Output to interpreter or repr()]
Flowchart illustrating how Python chooses between __repr__
and __str__
for object representation.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
# This representation is evaluable: eval("Point(1, 2)") would work
return f"Point(x={self.x}, y={self.y})"
def __str__(self):
# This is more user-friendly
return f"({self.x}, {self.y})"
p = Point(1, 2)
print(p) # Calls __str__: (1, 2)
print(repr(p)) # Calls __repr__: Point(x=1, y=2)
# In the interpreter, just typing 'p' would show: Point(x=1, y=2)
class User:
def __init__(self, user_id, username):
self.user_id = user_id
self.username = username
def __repr__(self):
return f"User(user_id={self.user_id!r}, username={self.username!r})"
# Using !r ensures string values are quoted in the repr output
u = User(101, "alice_smith")
print(repr(u)) # Output: User(user_id=101, username='alice_smith')
Example demonstrating __repr__
and __str__
together, and using !r
for string representation.
!r
conversion flag inside an f-string calls repr()
on the expression. This is particularly useful for ensuring that string attributes are properly quoted in the __repr__
output, making it more accurate and potentially evaluable.