The __format__() method
The __format__()
method is used by string.format()
as well as the format()
built-in function. Both of these interfaces are used to get presentable string versions of a given object.
The following are the two ways in which arguments will be presented to __format__()
:
someobject.__format__("")
: This happens when the application doesformat(someobject)
or something equivalent to"{0}".format(someobject)
. In these cases, a zero-length string specification was provided. This should produce a default format.someobject.__format__(specification)
: This happens when the application doesformat(someobject, specification)
or something equivalent to"{0:specification}".format(someobject)
.
Note that something equivalent to "{0!r}".format()
or "{0!s}".format()
doesn't use the __format__()
method. These use __repr__()
or __str__()
directly.
With a specification of ""
, a sensible response is return str(self)
. This provides an obvious consistency between the various string representations of an object.
The format specification will be all the text after the ":"
in a format string. When we write "{0:06.4f}"
, the 06.4f
is the format specification that applies to item 0
of the argument list to be formatted.
Section 6.1.3.1 of the Python Standard Library documentation defines a sophisticated numeric specification as a nine-part string. This is the format specification mini-language. It has the following syntax:
[[fill]align][sign][#][0][width][,][.precision][type]
We can parse these standard specifications with a regular expression (RE) as shown in the following code snippet:
re.compile( r"(?P<fill_align>.?[\<\>=\^])?" "(?P<sign>[-+ ])?" "(?P<alt>#)?" "(?P<padding>0)?" "(?P<width>\d*)" "(?P<comma>,)?" "(?P<precision>\.\d*)?" "(?P<type>[bcdeEfFgGnosxX%])?" )
This RE will break the specification into eight groups. The first group will have both the fill
and alignment
fields from the original specification. We can use these groups to work out the formatting for the numeric data of the classes that we've defined.
However, Python's format specification mini-language might not apply very well to the classes that we've defined. Therefore, we might need to define our own specification mini-language and process it in our class __format__()
method. If we're defining numeric types, we should stick to the predefined mini-language. For other types, however, there's no reason to stick to the predefined language.
As an example, here's a trivial language that uses the character %r
to show us the rank and the character %s
to show us the suit. The %%
character becomes %
in the resulting string. All other characters are repeated literally.
We could extend our Card
class with formatting as shown in the following code snippet:
def __format__( self, format_spec ): if format_spec == "": return str(self) rs= format_spec.replace("%r",self.rank).replace("%s",self.suit) rs= rs.replace("%%","%") return rs
This definition checks for a format specification. If there's no specification, then the str()
function is used. If a specification was provided, a series of replacements is done to fold rank, suit, and any %
characters into the format specification, turning it into the output string.
This allows us to format cards as follows:
print( "Dealer Has {0:%r of %s}".format( hand.dealer_card) )
The format specification ("%r of %s"
) is passed to our __format__()
method as the format
parameter. Using this, we're able to provide a consistent interface for the presentation of the objects of the classes that we've defined.
Alternatively, we can define things as follows:
default_format= "some specification" def __str__( self ): return self.__format__( self.default_format ) def __format__( self, format_spec ): if format_spec == "": format_spec = self.default_format # process the format specification.
This has the advantage of putting all string presentations into the __format__()
method instead of spreading it between __format__()
and __str__()
. This has a disadvantage because we don't always need to implement __format__()
, but we almost always need to implement __str__()
.