Understanding __str__ vs __repr__ in Python and Their Role in Container Printing

Dec 01, 2025 · Programming · 7 views · 7.8

Keywords: Python | _str__ method | _repr__ method | container printing | object string representation

Abstract: This article explores the distinction between __str__ and __repr__ methods in Python, explaining why custom object string representations fail when printed within containers like lists. By analyzing the internal implementation of list.__str__(), it reveals that it calls repr() instead of str() for elements. The article provides solutions, including defining both methods, and demonstrates through code examples how to properly implement object string representations to ensure expected output both when printing objects directly and as container elements.

Core Mechanisms of Object String Representation in Python

In Python, object string representation is achieved through two special methods: __str__ and __repr__. While both return strings, they serve fundamentally different purposes. __str__ is designed to provide a human-readable, friendly representation, typically used by the print() function and str() built-in; whereas __repr__ focuses on generating an official, unambiguous representation that should ideally allow object reconstruction via eval(), primarily used for debugging and development environments.

Special Behavior in Container Printing

When printing container objects such as lists, tuples, or dictionaries, Python's default behavior leads to a common misunderstanding. Consider the following example:

class Node:
    def __init__(self, id):
        self.id = id
        self.neighbours = []

    def __str__(self):
        return str(self.id)

uno = Node(1)
due = Node(2)
print(uno)  # Output: 1
print([uno, due])  # Output: [<__main__.Node object at 0x...>, <__main__.Node object at 0x...>]

When printing the uno object directly, Python invokes the __str__ method, yielding the expected 1. However, when this object is printed as a list element, it displays the default object representation (e.g., <__main__.Node object at 0x...>). This occurs because the list.__str__() method internally calls repr() rather than str() for each element within the container.

Root Cause and Solution

This design choice stems from Python's emphasis on consistency and clarity in container representations. The representation provided by repr() typically includes more type and state information, aiding in debugging. To resolve this issue, one must define both __repr__ and __str__ methods. The simplest approach is to have __repr__ return the same value as __str__:

class Node:
    def __init__(self, id):
        self.id = id
        self.neighbours = []

    def __str__(self):
        return str(self.id)

    __repr__ = __str__  # Key fix

After this modification, both direct object printing and printing as list elements produce consistent output:

uno = Node(1)
due = Node(2)
tri = Node(3)
uno.neighbours.append([[due, 4], [tri, 5]])
print(uno.neighbours)  # Now outputs: [[[2, 4], [3, 5]]]

Best Practices for Implementing repr()

While the simple assignment __repr__ = __str__ solves the problem, in practical development, it is advisable to provide richer debugging information in __repr__. A good practice is to return a string that clearly identifies the object type and key states:

class Node:
    def __init__(self, id):
        self.id = id
        self.neighbours = []
        self.distance = 0

    def __str__(self):
        return f"Node({self.id})"

    def __repr__(self):
        return f"Node(id={self.id}, neighbours_count={len(self.neighbours)}, distance={self.distance})"

This implementation maintains the simplicity of __str__ while offering detailed internal state via __repr__, aligning with Python community coding standards.

Comparison with Other Languages

Unlike Java's single toString() method, Python's dual string representation mechanism provides finer control. In Java, one typically overrides only toString(), whereas Python's separated design allows developers to distinguish between user-friendly and developer-debugging representations. This design is particularly valuable in complex systems, as it caters to the differing needs of end-users and developers simultaneously.

Conclusion

Understanding the distinction between __str__ and __repr__ is crucial for mastering object representation in Python. When custom objects need to be placed in containers and printed, both methods must be defined. By implementing them appropriately, one can ensure that objects provide suitable and consistent string representations across various contexts, thereby enhancing code readability and maintainability.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.