In Python, every object can have two distinct string representations: one for informal, human-readable output and another for formal, unambiguous debugging and development. These representations are provided by the __str__ and __repr__ special methods, respectively. Understanding the difference between them and implementing them correctly is crucial for creating robust, debuggable classes.

The Core Distinction: str vs. repr

The fundamental difference lies in their intended audience and purpose. The __str__ method is called by the str() built-in function and by the print() function. Its goal is to return a string that is “nicely printable” and easily understood by an end-user. It is meant to be informal and concise.

Conversely, the __repr__ method is called by the repr() built-in function. Its primary purpose is to be unambiguous and, ideally, should return a string that is a valid Python expression that could be used to recreate the object. This makes it an invaluable tool for developers during debugging. If at all possible, repr() should return a string that looks like a constructor call for the object. A good rule of thumb, often attributed to the language’s creator, Guido van Rossum, is: __repr__ is for developers, __str__ is for clients.

If a class defines __str__ but not __repr__, Python will use __repr__ as a fallback when str() is called. However, if __repr__ is defined but __str__ is not, calling str() will also use __repr__. It is considered best practice to always define at least __repr__ for your classes.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

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

# Creating an instance
p = Point(3, 4)

# repr() calls __repr__
print(repr(p))  # Output: Point(x=3, y=4)

# str() and print() call __str__
print(str(p))   # Output: (3, 4)
print(p)        # Output: (3, 4)

Implementing a Useful repr

The ideal __repr__ returns a string that is both descriptive and executable. It should include the class name and the values of all crucial attributes required to reconstruct the object. Using f-strings with explicit keyword arguments, as shown above, is a highly readable and effective pattern. This output, Point(x=3, y=4), is clear and could be pasted directly into code to create a new, identical Point object.

class Employee:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def __repr__(self):
        # A good, unambiguous representation
        return f"Employee(name='{self.name}', id={self.id})"

    # No __str__ defined, so it falls back to __repr__

emp = Employee("Alice", 12345)
print(repr(emp)) # Output: Employee(name='Alice', id=12345)
print(emp)       # Output: Employee(name='Alice', id=12345) (uses __repr__)

Crafting a User-Friendly str

The __str__ method should focus on presenting the object’s most relevant information in the clearest way possible for a user who might not care about the internal state or class name. It’s about summarizing the object’s value or purpose.

class Product:
    def __init__(self, name, price, sku):
        self.name = name
        self.price = price
        self.sku = sku

    def __repr__(self):
        return f"Product(name='{self.name}', price={self.price}, sku='{self.sku}')"

    def __str__(self):
        # Focus on the info a customer would want to see
        return f"{self.name} - ${self.price:.2f}"

product = Product("Coffee Mug", 9.99, "CM-001")
print(repr(product)) # Output: Product(name='Coffee Mug', price=9.99, sku='CM-001')
print(product)       # Output: Coffee Mug - $9.99

Common Pitfalls and Best Practices

A common pitfall is to only define one method and forget the other, or to make them identical. While Python provides a fallback, being explicit is always better. Another mistake is making the __repr__ output overly vague (e.g., just <Point object>) or incorrect (e.g., misstyping the class name or attribute values).

A critical best practice is to ensure your __repr__ implementation does not try to execute the expression it returns, especially if the object’s state is complex or contains circular references. Its job is to return the string that looks like the constructor call, not to actually run it.

For collections, the __repr__ method will typically call repr() on each of its elements. This ensures the entire structure’s representation is unambiguous.

class Inventory:
    def __init__(self):
        self.products = []

    def add_product(self, product):
        self.products.append(product)

    def __repr__(self):
        # repr() is called on each product in the list
        return f"Inventory(products={repr(self.products)})"

inventory = Inventory()
inventory.add_product(Product("Keyboard", 49.99, "KB-202"))
inventory.add_product(Product("Mouse", 24.99, "MS-557"))

print(repr(inventory))
# Output: Inventory(products=[Product(name='Keyboard', price=49.99, sku='KB-202'), Product(name='Mouse', price=24.99, sku='MS-557')])

In summary, always implement __repr__ to provide a clear, unambiguous string for developers. Implement __str__ if you need a more user-friendly representation distinct from the developer-focused one. This simple practice will significantly improve the debuggability and usability of your classes.