The __str__ and __repr__ Method in Python
Next week, I'd like to cover operator overloading in Python. However, first, it seems appropriate to cover some of the more basic overloading that can be done with Python, starting with the methods that print an object as a string.
Coming from C++ land, it was confusing to me to have two methods which can be used to represent a
string. In C++, I might have had a few options on how to implement a 'print' method, but it always
came down to passing a string to an overridden method of <<
, whether it was
overridden by my own class or by std::cout
. Regardless of how it was done, the
same procedure could be used. First, break the object into plain-old-data which could be
represented by a string. Second, convert those underlying types to a string. Simple as that. Once
you had a string, it would be used for any string-related methods, regardless of purpose (unless
there were two user-defined print methods, but that just sounds like bad design).
When I saw that Python has two methods used to convert complex types to strings, I was confused. I just assumed one might be a low-level function that never was implemented, and the other was the function that I actually needed to override in my class. I wasn't quite right.
First, let me explain what the __str__
and __repr__
methods in
Python are actually used for...
Like operator<<
in C++ (though both more explicit and exclusive than
<<
), __str__
and __repr__
are Python magic
methods (AKA dunder methods) which convert complex objects into string representations of those
objects. A really simple example using __str__
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class IncrementingVector:
''' A vector of length 'n' which increments from 0 to N
def __init__(self, n):
''' Constructor method'''
self.vec = list(range(n))
def __str__(self):
''' Converts IncrementingVector to string'''
return '[' + ' '.join([str(ii) for ii in self.vec]) + ']'
# Main method creates a IncrementingVector and prints it
if __name__ == '__main__':
vector = IncrementingVector(10)
print(vector)
This program results in:
chris@desktop: ~# python print_incrementing_vector.py
[0 1 2 3 4 5 6 7 8 9]
However, if I did the same thing, substituting __repr__
for __str__
,
I get the same thing. What gives? Is __repr__
just an alias for __str__
?
No!
Though they do very similar things, __str__
and __repr__
are
not the same. The best way I've heard it explained are that they are used for different audiences.
The __str__
method is used to satisfy the user audience. The
__repr__
method is used to satisfy the programmer audience (those poor folks who
have to debug your code after you are done with it).
What this is really going to come down to is that the __str__
method is going to
meaningfully show the internals of the class as a string (similar to the print_incrementing_vector.py
example) and the __repr__
method is going to be useful for debugging. Most of the
time, __repr__
is going to be the call to __init__
which would
create the class if the string was passed to eval()
. The __str__
method will be what you typically would associate with printing a complex class. See the following
for a more concrete example:
>>> print(str(IncrementingVector(10)))
[0 1 2 3 4 5 6 7 8 9]
>>> print(repr(IncrementingVector(10)))
IncrementingVector(10)
Just think: the end user is never going to care that the class representing their data is an
IncrementingVector
with length 10. However, they may care that their vector is a
list from 0 through 9. A developer may need both pieces of information: how to create the object
and what it actually represents under the hood.
Now when just calling print()
on an object, the Python intrepreter first tries
the __str__
method. If that's not defined, it tries the __repr__
as backup. If neither are defined, the interpreter defaults to printing the address, shown in the
next example:
>>> class NewObject:
... pass
...
>>> new_object = NewObject()
>>> print(new_object)
...
<__main__.NewObject object at 0x7f2ecd825310>
... well that's not very helpful.
Interestingly, if a __repr__
method is defined for NewObject
,
the Python interpreter will represent the class as the string returned by __repr__
.
This makes sense since the Python interpreter isn't really printing the end-user representation
of the object, but the object itself. Here's an example:
>>> class NewObject:
... def __repr__(self):
... return 'Hello world'
...
>>> new_object = NewObject()
>>> new_object
Hello world
So in summary, the __str__
method should define the string representation of the
object as if it is being shown to the end user. The __repr__
method should define
the interface to construct the object as if the string was being passed to eval()
.
Of course, there is no need to implement both methods, unless both are needed by the application or the end-user of a framework. That's the benefit of Python - only define what is practical without all the boilerplate required in other languages.