Search code examples
pythonpython-3.xio

What differences are between `format()` and `str()`?


>>> format(sys.stdout)
"<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>"
>>> str(sys.stdout)
"<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>"

I am not very sure about the following quotes from Python library references. What differences are between format() and str(), or between object.__format__(self, format_spec) and object.__str__(self )? When shall we use which?

object.__str__(self )

Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object.

object.__format__(self, format_spec)

Called by the format() built-in function, and by extension, evaluation of formatted string literals and the str.format() method, to produce a “formatted” string representation of an object.


Solution

  • I am gonna break down the main three (technically main 2 because __format__ is not commonly implemented). I'll go through all three then put it all together in one nice class.


    The most common __str__:

    This is how you want to represent this object as a str. Often this is less detailed than __repr__ and is used mainly for the end user. For example if we have a person class we may implement str this way:

    class Person:
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return f"Hello my name is {self.name}" 
    
    p = Person("John Smith")
    print(str(p)) # Prints "Hello my name is John Smith"
    

    The less common __repr__:

    To be honest I implement this more than str for most projects. This should return the representation of the object as a str in such a way that it is helpful to the developer instead of the end user and is called via repr(my_obj). If a class does not implement a __str__ magic method then python will attempt to call its __repr__ method automatically when you use str(my_obj) (thus you can implement repr first and str later). Often the repr of a class returns the way to recreate that object. For example:

    class Person:
        def __init__(self, name):
            self.name = name
        def __repr__(self):
            return f"Person(name='{self.name}')"
    
    p = Person("John Smith")
    print(repr(p)) # Prints "Person(name='John Smith')"
    print(str(p))  # Prints "Person(name='John Smith')"
    

    The cursed child __format__: [I just call it that because it is so uncommonly used :)]

    The main difference between __format__ and the other two methods is that it also accepts a format_spec that can be used to style the returned string.

    To understand this in further detail I must first explain some formatting stuff. You may have seen some of this stuff before or you may not have.

    Lets use a dummy class:

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def __repr__(self):
            return f'Person(name="{self.name}", age={self.age})'
        def __str__(self):
            return str(self.name)
    
    p = Person(name="John Smith", age=22)
    # This prints the str of p
    print("{!s}".format(p)) # John Smith
    # This prints the representation of p
    print("{!r}".format(p)) # Person(name="John Smith", age=22)
    

    Okay so you can format your class as its str or as its repr, but what if you want to format it your own way? Most people don't need to do this; but, this is the power of python, you can do anything. We can create different tags instead of r and s (though it uses : instead of !):

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def __repr__(self):
            return f'Person(name="{self.name}", age={self.age})'
        def __str__(self):
            return str(self.name)
        def __format__(self, format_spec):
            if format_spec == 'age': # get a persons age
                return f"{self.name} is {self.age} years old"
            elif format_spec == 'birthday':
                return "Yesterday"
            return str(self) # A default case
    
    p = Person(name="John Smith", age=22)
    # This prints the str of p
    print("{!s}".format(p))
    # This with representation of p
    print("{!r}".format(p))
    print("{:age}".format(p))
    print("{:birthday}".format(p))
    print(f"{p:age} and his birthday was {p:birthday}")
    

    There is obviously more to this than my limited knowledge can go into but this should be a good general scope :).

    There are some packages that use this such as datetime. If you are interested learning more about the power of format Pyformat walks through this pretty well: PyFormat.

    Disclaimer:

    I do not have much experience using __format__ so I can not provide a good use-case (though datetime's use-case isn't bad). Everything here is just to show the broad difference between str, repr, and format magic methods. If I got anything wrong (especially __format__'s use-case) please let me know and I will update it.