9.1. Use repr Strings for Debugging Output

When debugging a Python program, the print function and format strings (see Item 4: “Prefer Interpolated F-Strings Over C-style Format Strings and str.format”), or output via the logging built-in module, will get you surprisingly far. Python internals are often easy to access via plain attributes (see Item 42: “Prefer Public Attributes Over Private Ones”). All you need to do is call print to see how the state of your program changes while it runs and understand where it goes wrong.

The print function outputs a human-readable string version of whatever you supply it. For example, printing a basic string prints the contents of the string without the surrounding quote characters:

>>> print('foo bar')
foo bar

This is equivalent to all of these alternatives:

  • Calling the str function before passing the value to print

  • Using the ‘%s’ format string with the % operator

  • Default formatting of the value with an f-string

  • Calling the format built-in function

  • Explicitly calling the __format__ special method

  • Explicitly calling the __str__ special method

Here, I verify this behavior:

>>> my_value = 'foo bar'
>>> print(str(my_value))
>>> print('%s' % my_value)
>>> print(f'{my_value}')
>>> print(format(my_value))
>>> print(my_value.__format__('s'))
>>> print(my_value.__str__())
foo bar
foo bar
foo bar
foo bar
foo bar
foo bar

The problem is that the human-readable string for a value doesn’t make it clear what the actual type and its specific composition are. For example, notice how in the default output of print, you can’t distinguish between the types of the number 5 and the string ‘5’:

>>> print(5)
>>> print('5')
>>>
>>> int_value = 5
>>> str_value = '5'
>>> print(f'{int_value} == {str_value} ?')
5
5
5 == 5 ?

If you’re debugging a program with print, these type differences matter. What you almost always want while debugging is to see the repr version of an object. The repr built-in function returns the printable representation of an object, which should be its most clearly understandable string representation. For most built-in types, the string returned by repr is a valid Python expression:

>>> a = '\x07'
>>> print(repr(a))
'x07'

Passing the value from repr to the eval built-in function should result in the same Python object that you started with (and, of course, in practice you should only use eval with extreme caution):

>>> b = eval(repr(a))
>>> assert a == b

When you’re debugging with print, you should call repr on a value before printing to ensure that any difference in types is clear:

>>> print(repr(5))
>>> print(repr('5'))
5
'5'

This is equivalent to using the ‘%r’ format string with the % operator or an f-string with the !r type conversion:

>>> print('%r' % 5)
>>> print('%r' % '5')
>>>
>>> int_value = 5
>>> str_value = '5'
>>> print(f'{int_value!r} != {str_value!r}')
5
'5'
5 != '5'

For instances of Python classes, the default human-readable string value is the same as the repr value. This means that passing an instance to print will do the right thing, and you don’t need to explicitly call repr on it. Unfortunately, the default implementation of repr for object subclasses isn’t especially helpful. For example, here I define a simple class and then print one of its instances:

>>> class OpaqueClass:
>>>     def __init__(self, x, y):
>>>         self.x = x
>>>         self.y = y
>>>
>>> obj = OpaqueClass(1, 'foo')
>>> print(obj)
<__main__.OpaqueClass object at 0x7f76a46bbe20>

This output can’t be passed to the eval function, and it says nothing about the instance fields of the object.

There are two solutions to this problem. If you have control of the class, you can define your own __repr__ special method that returns a string containing the Python expression that re-creates the object. Here, I define that function for the class above:

>>> class BetterClass:
>>>     def __init__(self, x, y):
>>>         self.x = x
>>>         self.y = y
>>>     def __repr__(self):
>>>         return f'BetterClass({self.x!r}, {self.y!r})'

Now the repr value is much more useful:

>>> obj = BetterClass(2, 'bar')
>>> print(obj)
BetterClass(2, 'bar')

When you don’t have control over the class definition, you can reach into the object’s instance dictionary, which is stored in the __dict__ attribute. Here, I print out the contents of an OpaqueClass instance:

>>> obj = OpaqueClass(4, 'baz')
>>> print(obj.__dict__)
{'x': 4, 'y': 'baz'}

9.1.1. Things to Remember

✦ Calling print on built-in Python types produces the humanreadable string version of a value, which hides type information.

✦ Calling repr on built-in Python types produces the printable string version of a value. These repr strings can often be passed to the eval built-in function to get back the original value.

✦ %s in format strings produces human-readable strings like str. %r produces printable strings like repr. F-strings produce humanreadable strings for replacement text expressions unless you specify the !r suffix.

✦ You can define the __repr__ special method on a class to customize the printable representation of instances and provide more detailed debugging information.