Python string formatting with custom classes

Jacob Unna
FullStackAI
2 min readJan 20, 2021

--

Strings in Python have a .format() method that allows placeholders to be substituted with values:

In this article we will see how you can use custom classes to do cool stuff with .format().

Format options

A useful feature of .format() is that formatting options can be provided:

In the above example, we have formatted a float type with the .2f option. Other Python types that support format options include str, int and datetime. A good source for learning about all the possibilities for format options is pyformat.info.

A neat thing about Python is you can create your own types that accept format options by implementing the __format__ method. For example, using pykakasi we can create a class that can render Japanese script in different ways:

Something particularly attractive about this is that it separates copywriting from engineering. Suppose a utility company is writing to all its customers with their predicted spend next year. Copywriters provide the following template:

Your predicted spend next year is ${amount}.

However, they also stipulate that if amount is greater than $100, it should be rounded to the nearest dollar. Instead of writing custom code to deal with this situation, the engineering organisation could inform the copywriting team that from now on, they can stipulate this requirement in the following way:

Your predicted spend next year is ${amount:rnd_if_gt_100}.

This is implemented as follows:

This reduces the need for coordination between engineers and copywriters and empowers the copywriters with finer control over how text is displayed.

Missing values

When you format a string, all values must be provided or a KeyError is raised:

>>> '{a} {b}'.format(a=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'b'

Oftentimes this is desirable, but what if you want to allow a string to be only “partly” formatted? We can use a subclass of dict:

Notice that we have used format_map instead of format. The former accepts a dictionary as the argument whereas the latter requires the dictionary to be unpacked. For a dict object, the following are equivalent:

vals = {"a": 1}
"{a}".format(**vals)
"{a}".format_map(vals)

However, when vals is a not a dict but a subtype of dict, we need to use format_map because unpacking using ** causes type information to be lost.

Using every value

Whereas not supplying every parameter causes string formatting to fail, supplying too many parameters is fine:

>>> "{a}".format(a=1, b=2)
'1'

How can we detect this case and raise an exception? Again, using a subtype of dict:

Conclusion

Python formatting is a great tool, and by combining it with custom classes it solves a wide array of problems. In this article we have seen a few examples of this.

--

--

Jacob Unna
FullStackAI

Software Engineer @ Deloitte Analytics & Cognitive