Explain Codes LogoExplain Codes Logo

What is the difference between str and repr?

python
best-practices
debugging
logging
Alex KataevbyAlex Kataev·Aug 27, 2024
TLDR

__str__ forges a formatted string for end-users, while __repr__ concocts an explicit string encapsulating an object's essence for developers. Preferably, __repr__ should enable eval(repr(obj)) to reconstruct the object.

Example:

class Item: def __init__(self, name): self.name = name def __str__(self): return f'Item({self.name})' # More like it's on a date with user def __repr__(self): return f'Item({self.name!r})' # It's having a formal interview with a CIA agent item = Item("Widget") print(str(item)) # Item(Widget) print(repr(item)) # Item('Widget')

Calling str(item), you get a readable summary, repr(item) gives you a meticulous representation including object type and quotes around strings for precise reconstruction.

str vs repr: When to use which?

For everyday usage, __str__ serves up a user-oriented version of an object. It might format the data into a human-legible form or pare down information to only highlight the essentials.

Conversely, __repr__ is invaluable for debugging or logging, as it dishes out an intense and precise representation. Ideally, __repr__ can be used to recreate the object.

Dealing with container types

Did you know that containers like lists and dictionaries use __repr__ of their elements when rendered as strings? Therefore, you should always make your __repr__ string-friendly.

Logging? __repr__ got your back!

A detailed __repr__ is extremely helpful during logging. If reconstruction is not possible (like when you depend on external resources), making __repr__ informative is a must.

__str__ is MIA! Now what?

If __str__ is absent, Python will default to __repr__. The reverse is not true, underlining that creating a robust __repr__ is more important than __str__.

Guidelines for implementing str and repr

Squint, clarity ahead!

A transparent __repr__ is crucial. Use placeholders and type indications to ensure clarity:

# Literals and strings are as different as apples and Apple Inc. def __repr__(self): return f'YourClass(param1={self.param1!r}, param2={self.param2!r})'

Abridging with __str__ - a good read

With __str__, you can distil the information by focusing on what's most relevant:

# Less is more def __str__(self): return f'{self.param1} - {self.param2}'

repr() and str() in code... The Avengers, assemble!

Invoke repr() for unambiguous representation and str() for a user-friendly portrayal, keeping your objects in character in any context.

Constructing with __repr__

When feasible, it's best to structure __repr__ to be eval-able, to recreate the object:

# Look mom, no hands! eval(repr(your_object)) == your_object

However, if that's not feasible, focus on creating a useful diagnostic output.

Reworking default behaviors: To Change or Not to Change?

Setting up a clear __repr__

By redefining __repr__, we invite transparency and clarity. Even a class without much state should have a __repr__ to stand out from its instances:

# Just like every startup claims they're unique, so does this class def __repr__(self): return f'<{self.__class__.__name__} at {id(self)}>'

__str__ and __repr__ twinning

Synchronize __str__ with __repr__ to maintain consistency in both development and user-facing contexts. It's like having a smoothie with an identical nutritional supplement.

Tailoring __str__ for containers

Boost the __str__ presentation of containers with .join() and custom element presentations. It's like turning your simple list into an exotic fruit salad - more interesting and appealing:

# Now this is tastefully plated! def __str__(self): elements = ', '.join(str(x) for x in self.my_container) return f'Container({elements})'