Python: the !r string format and the __repr__() and __str__() methods.
The !r string format is a convenient one to use in certain cases. It is related closely to the __repr__() dunder method; and this method and the __str__() are often discussed together. In this post, we review __repr__() and __str__() methods and then the !r string format.
Python: the !r string format and the __repr__() and __str__() methods. |
An usage example of the string format !r:
fmt_data = '{!r:^12} {!r:^15} {!r:^10}'
On !r, PEP 3101 – Advanced String Formatting states:
!r - convert the value to a string using repr().
repr() and str() official documentations can be found in the following links repr(object), class str(object=’’), object.__repr__(self) and object.__str__(self)
Basically:
- repr(object) calls object.__repr__(self); and we implement the later in our own codes.
- class str(object='') calls object.__str__(self); and we implement the later in our own codes.
- The purpose of object.__str__(self) is to provide a friendly human readable string presentation of an object instance.
- The purpose of object.__repr__(self) is to provide a string representation of an object instance, AND the eval(expression[, globals[, locals]]) function should be able to take this string and convert it to the same original object instance from which the string is generated from.
Let’s illustrate this with an example:
class Person( object ):
def __init__( self, given_name, surname ):
self.__given_name = given_name
self.__surname = surname
def __repr__( self ):
fmt = u"{}(given_name='{}', surname='{}')"
return fmt.format( self.__class__.__name__, \
self.__given_name, self.__surname )
def __str__( self ):
fmt = u"{}: Given Name: '{}', Surname: '{}')"
return fmt.format( self.__class__.__name__, \
self.__given_name, self.__surname )
– Please note, in case you wonder if I’ve copied this example from elsewhere… I have 😂, it is a very popular example used to illustrate this topic, I’ve also made some minor adjustments to it.
Let’s see how it works:
person = Person( 'Văn Bé Hai', 'Nguyễn' )
# My full name, written in Vietnamese: Nguyễn Văn Bé Hai 😂
print( person.__str__() )
print( str( person ) )
print( '---' )
print( person.__repr__() )
print( repr( person ) )
As expected, the output of object_instance.__str__() and str( object_instance ) are the same; and so do object_instance.__repr__() and repr( object_instance ).
Person: Given Name: 'Văn Bé Hai', Surname: 'Nguyễn')
Person: Given Name: 'Văn Bé Hai', Surname: 'Nguyễn')
---
Person(given_name='Văn Bé Hai', surname='Nguyễn')
Person(given_name='Văn Bé Hai', surname='Nguyễn')
Continue on, let’s see how person.__repr__() works with eval(expression[, globals[, locals]]):
repr_str = person.__repr__()
person1 = eval( repr_str )
print( str( person1 ) )
And it does work as expected:
Person: Given Name: 'Văn Bé Hai', Surname: 'Nguyễn')
Now, we try out !r string format with object instances person and person1:
print( '"person" instance reads: {!r}'.format(person) )
print( '"person1" instance reads: {!r}'.format(person1) )
And we get:
"person" instance reads: Person(given_name='Văn Bé Hai', surname='Nguyễn')
"person1" instance reads: Person(given_name='Văn Bé Hai', surname='Nguyễn')
– The !r format does eventually call __repr__().
Back to class Person, we could get rid of the double single quote around the curly bracket pairs ( ‘{}’ ) in the two variables fmt, and use {!r}:
class Person( object ):
...
def __repr__( self ):
# fmt = u"{}(given_name='{}', surname='{}')"
fmt = u"{}(given_name={!r}, surname={!r})"
...
def __str__( self ):
# fmt = u"{}: Given Name: '{}', Surname: '{}')"
fmt = u"{}: Given Name: {!r}, Surname: {!r})"
...
Finally, let’s look at the example listed in the beginning of this post: fmt_data = ‘{!r:^12} {!r:^15} {!r:^10}’ – please see this official document Format Specification Mini-Language for much more info. In a nutshell, ^ centres the string within the specified width, in this example, widths are 12, 15 and 10 respectively – I have three ( 3 ) Boolean fields, and I would like to display them in tabular format, in the middle of three ( 3 ) headers with different lengths:
create_own = False
create_other = False
view_own = True
fmt_header = '{:^12} {:^15} {:^10}'
fmt_data = '{!r:^12} {!r:^15} {!r:^10}'
print( fmt_header.format('Create Own', 'Create Other', 'View Own' ) )
print( fmt_data.format(create_own, create_other, view_own) )
And the output looks like:
Create Own Create Other View Own
False False True
This is what I mean in the beginning “The !r string format is a convenient one to use in certain cases.”
It seems Python offers a lot in term of string formatting. I find this information very useful. And I hope you do too. I certainly enjoy writing this post. Thank you for reading and stay safe as always.