Be Hai Nguyen
Posted on September 21, 2022
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.
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 <a href="https://docs.python.org/3/reference/datamodel.html#object.str" title="object.str(self)" target="_blank">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.
Posted on September 21, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.