Python __str__ vs __repr__: What's the Difference?
python **str** vs **repr**: Understand the difference between Python's __str__ and __repr__ methods, when to implement each, and how they affect debugging and user-fac...
In Python, every object can be converted to a string in two different ways, controlled by the __str__ and __repr__ methods. The distinction matters because the two methods serve different audiences: __str__ is for end users, while __repr__ is for developers debugging the code. Understanding python str vs repr helps you decide which one to implement and how to use them effectively.
The Default Behavior of str and repr
If you do not define either method, Python falls back to the default implementation inherited from object. That default produces a string like <__main__.MyClass object at 0x7f8b1c0b4a90>, which contains the class name and the memory address of the instance. This output is rarely useful in practice, which is why most custom classes override at least one of these methods.
class Point: pass p = Point() print(str(p)) # <__main__.Point object at 0x7f8b1c0b4a90> print(repr(p)) # <__main__.Point object at 0x7f8b1c0b4a90>
The default behavior is identical for both methods. The memory address is not stable between runs and does not convey any information about the object's state, so relying on it is rarely a good idea.
What str Is For
The __str__ method is called by the built-in str() function, by print(), and by f-strings when you use {obj} without the !r conversion. Its purpose is to produce a readable, human-friendly representation of the object. This is the output that an end user of your application might see in a report, a UI, or a log file that is meant to be read by non-developers.
Consider a class representing a date:
class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def __str__(self): return f"{self.year}-{self.month:02d}-{self.day:02d}" d = Date(2024, 5, 17) print(d) # 2024-05-17
The __str__ method returns a clean, formatted date that a user would recognize. It does not include class names or memory addresses because those are not relevant to the user.
What repr Is For
The __repr__ method is called by the built-in repr() function, by the interactive interpreter when you evaluate an expression, and by debuggers and logging frameworks that use %r or the !r conversion in f-strings. Its purpose is to produce an unambiguous, developer-oriented representation of the object. The ideal __repr__ should be as close as possible to the source code needed to recreate the object, or at least contain enough information to understand its state.
For the same Date class, a good __repr__ might look like this:
class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def __repr__(self): return f"Date({self.year}, {self.month}, {self.day})" d = Date(2024, 5, 17) print(repr(d)) # Date(2024, 5, 17)
This string is unambiguous: it tells you the exact class and the values of all attributes. In many cases, you can copy this output and paste it back into Python to recreate the object, which is extremely useful during debugging.
Implementing Both Methods in a Class
When you implement both methods, you give users and developers the appropriate view of the object. Here is a complete example:
class Temperature: def __init__(self, celsius): self.celsius = celsius def __str__(self): return f"{self.celsius}°C" def __repr__(self): return f"Temperature({self.celsius})" t = Temperature(22) print(str(t)) # 22°C print(repr(t)) # Temperature(22)
The __str__ version is compact and user-friendly, while the __repr__ version is precise and shows the constructor call. This separation keeps user-facing output clean without sacrificing debugging clarity.
Using repr for Debugging and Logging
One of the most common mistakes is to rely on __str__ for debugging. When you insert an object into a log message using f"{obj}", you get the user-facing string. If that string is ambiguous or missing state, debugging becomes harder. Instead, use !r to force the __repr__ representation:
import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) t = Temperature(22) logger.info("Current temperature: %r", t) # Uses __repr__
The %r formatting directive and the !r conversion in f-strings both call repr(). This is particularly valuable when you need to log the exact state of an object without writing custom formatting logic every time.
When to Implement Only One or the Other
Python provides a fallback: if you define only __repr__, then __str__ will automatically use the same implementation. This is because the default __str__ method calls __repr__ internally. If you define only __str__, __repr__ remains the default object representation, which is usually not helpful for debugging.
A practical rule is to always implement __repr__ for your custom classes, and add __str__ only when you need a user-friendly version. If you skip __str__, the user-facing output will fall back to __repr__, which is often acceptable for internal tools but may be too technical for end users.
Common Pitfalls and Best Practices
A frequent mistake is making __repr__ too verbose or including mutable state that changes between calls. The representation should be stable and unambiguous. For example, if an object has a large list attribute, including the entire list in __repr__ may make the output unreadable. In that case, consider showing a summary, such as the length of the list, while still giving enough context.
Another pitfall is forgetting to update __repr__ when you add or rename attributes. An outdated __repr__ can mislead developers during debugging. Keep the two methods in sync with the actual state of the object.
Here is a comparison of the two methods:
| Aspect | __str__ | __repr__ |
|---|---|---|
| Primary audience | End users | Developers |
| Called by | print(), str(), f-strings | repr(), debugger, %r, !r |
| Goal | Readable, friendly output | Unambiguous, detailed output |
| Fallback behavior | Falls back to __repr__ if missing | Falls back to object.__repr__ |
| Typical use | UI, reports, user-facing logs | Debugging, logging, REPL inspection |
How the Two Methods Affect Maintainability
The choice between __str__ and __repr__ has direct consequences for code maintenance. A well-designed __repr__ makes debugging faster because you can see the exact state of an object in a traceback or log without opening a debugger. It also makes your classes easier to test: you can assert on the repr output to verify that an object was constructed correctly.
On the other hand, __str__ is part of your public API for user-facing output. If you change the format of __str__, you might break downstream code that parses that output. Therefore, treat __str__ as a contract with your users, while __repr__ can be more freely adjusted during development.
When implementing both, avoid duplicating logic. If the __str__ output is a subset of the __repr__ information, you can compose it from __repr__ to reduce duplication. For example:
class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Point({self.x}, {self.y})" def __str__(self): return f"({self.x}, {self.y})"
This keeps the logic in one place and ensures that the two representations stay consistent when the class evolves.