Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Python: __str__( ) vs. __repr__( )

DZone 's Guide to

Python: __str__( ) vs. __repr__( )

Here's a hint. One is for official string representation and one is for informal string representation. Read on for more!

· Web Dev Zone ·
Free Resource

Every time I find myself in front of an ambiguity in Python, the official documentation is there for me. It is very well written and, trust me, very explicit. So, let us start by discovering what it has to say about this subject.    

According to the official documentation,   __repr__() is used to compute an official string representation of an object which, and, if possible, can be used to recreate the object itself, however, __str__() is used to compute an informal string representation of that object.

Both of them are built-in functions and both return a string representation of an object, however, the format and the purpose behind these string representations are different.

Although the __repr__() official representation is for developers so they can use it to debug, the __str__() informal representation is for the final users.

A concrete example is always better to understand. In the below code block, we consider three Python object types: integer, string, and datetime.

from datetime import datetime

if __name__ == "__main__":

    nb = 1
    print (str(nb))
    #1
    print (repr(nb))
    #1
    print '*' * 20
#********************

    chaine = "hello world"
    print (str(chaine))
    #hello world
    print (repr(chaine))
    #'hello world'
    print '*' * 20
#********************

    d_cur = datetime.now()
    print (str(d_cur))
    #2019-03-14 12:01:44.377000
    print (repr(d_cur))
    #datetime.datetime(2019, 3, 14, 12, 1, 44, 377000)
    print '*' * 20
    #********************

Wait a second! If we are discussing __str__() and __repr__()why, in the example, are we useing str() and repr() instead? 

When we call str(object) the interpreter automatically invokes the  __str__() function of that object and, yes, as you have already deduced, it is the same case with the repr(object) call. 

For the integer object nb, the output is the same. For integer objects, there is no difference between the official representation and the informal one. For the string object chaine and the datetime object d_cur, you can easily notice the difference between both representations. 

The main difference is that the official representation can be evaluated with the eval() function to recreate the object itself, it is unambiguous. However, the informal one favorites readability rather than precision.

Once again, an example is more explicit: 

import datetime

if __name__ == "__main__":

    chaine = "hello world"
    print (str(chaine))
    #hello world
    print (repr(chaine))
    #'hello world'

    print chaine == eval(repr(chaine))
    #True 
    # the result of repr when used by eval recreate the same initial object
    #the comparaison returns True 

    print chaine == eval(str(chaine))
    #TypeError: eval() arg 1 must be a string or code object
    #The result of str used by eval does not recreate the initial object
    #TypeError exception is raised

    print '*' * 20
#********************

    d_cur = datetime.datetime.now()
    print (str(d_cur))
    #2019-03-14 12:01:44.377000
    print (repr(d_cur))
    #datetime.datetime(2019, 3, 14, 12, 1, 44, 377000)

    print d_cur == eval(repr(d_cur))
    #True 
    # the result of d_cur when used by eval recreate the same initial object
    #the comparaison returns True

    print d_cur == eval(str(d_cur))
    #TypeError: eval() arg 1 must be a string or code object
    #The result of str used by eval does not recreate the initial object
    #TypeError exception is raised

    print '*' * 20
    #********************

With Customized Objects

With the examples I used, I considered built-in Python objects. But what about your own objects?

Let's consider the below Python class that represents an IPv4 address.

class IP_Adress(object):

  def __init__(self, nb_1, nb_2, nb_3, nb_4):
    self.nb_1 = nb_1
    self.nb_2 = nb_2
    self.nb_3 = nb_3
    self.nb_4 = nb_4

if  __name__ == '__main__':

    addr = IP_Adress(192, 192, 233, 232)
    print str(addr)
#<__main__.IP_Adress object at 0x03412590>
    print repr(addr)
    #<__main__.IP_Adress object at 0x03412590>

Without the definition of  __str__() and  __repr__() in your class , the default implementation of  _repr__( ) is used for both str( ) and repr( ) functions. 

The default __repr__() implementation is given below:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

By default, it prints the module the object it's from, the class name, and the hexadecimal representation of its location in memory.

Let's add these two functions in our IP_Adress class.

class IP_Adress(object):

    def __init__(self, nb_1, nb_2, nb_3, nb_4):
        self.nb_1 = nb_1
        self.nb_2 = nb_2
        self.nb_3 = nb_3
        self.nb_4 = nb_4

    def __str__(self):
        return '{}.{}.{}.{}'.format(str(self.nb_1), str(self.nb_2), str(self.nb_3), str(self.nb_4))

    def __repr__(self):
        return 'IP_Adress({},{},{},{})'.format(repr(self.nb_1), repr(self.nb_2), repr(self.nb_3), repr(self.nb_4))


if  __name__ == '__main__':

    addr = IP_Adress(192,192,233,232)
    print str(addr)
    #192.192.233.232
    print repr(addr)
    #IP_Adress(192,192,233,232)
    addr_eval = eval(repr(addr))
    print str(addr1)
    #192.192.233.232

Within the __str__() function the returned representation is a string that's easily readable (IP address as known by all) and within the __repr__() the returned string is a call to the class constructor with our attributes' values. so you can evaluate it with the eval() function and recreate the object if you want to.

Note that in the  __repr__(), you need to use the repr() function to pass the parameters to your .format() function or your representation could not be evaluated correctly (if you do not see why then the first example with the string and the datetime types is the answer to your question).

If a class defines  __repr__() but not __str__() , then  __repr__() is also used when an informal string representation of instances of that class is required — that is why you need to implement __repr__() for any class you implement. This should be second nature.

An important thing to mention at this level is that if you find yourself with complex objects (which is usually the case). You do need to go into the same detail as the __repr__() official representation, just make it as precise as possible and include only the most important attributes.

With Containers 

A container's __str__() uses a contained objects's__repr__(). I know it seems weird, but an example will help to clarify. Let's consider our previous IP_Adress class and create a list of IP_Adress objects.

if  __name__ == '__main__':

    addr = IP_Adress(192, 192, 233, 232)
    addr_1 = IP_Adress(0, 0, 0, 0)
    l = [addr,addr_1]
    print str(l)
    #[IP_Adress(192,192,233,232), IP_Adress(0,0,0,0)]

When you print the list, you invoke its  __str__(), which iterates on the objects within it to invoke their __repr__() method. That is why we see the outputas: 

[IP_Adress(192,192,233,232), IP_Adress(0,0,0,0)] and not  [192.192.233.232, 0.0.0.0]

Conclusion 

Both __str__() and __repr__( ) are used to compute a string representation for objects under Python. __repr__( ) representation is more precise, unambiguous, and can be used by developers and creating logs.  __str__( ) representation is for final users and must prioritize readability rather than precision.  

PS

In all the previous examples, I used str(object) with theprint() function. Note that the print() function automatically invokes the str() function even if you don't explicitly call it. So, print(str(object)) is equivalent to print(object).

Topics:
web dev ,python tutorial ,python tutorial for beginners ,python web development

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}