Formatting Strings with Python

Introduction

Sooner or later string formatting becomes a necessary evil for most programmers. More so in the past before the thick client GUI era, but the need to have a specific string representation is still a common enough use case. My first introduction was back in college when I had an old-school prof that had a impure love for making us write Java console applications with neurotic specifications for outputting with the printf(...) function. One thing that held true then and still does now is that the documentation for string formatting (practically for all languages) leaves a lot to be desired. I hope to ease this pain today by writing about how to accomplish string formatting in Python.

In Python there happens to be multiple techniques for formatting strings, four to be exact. Interestingly this kinda goes against the grain of the Python mindset that there should generally be one clear best way to accomplish a task. However, if you have spent any reasonable amount of time with the language you have likely seen these disparate array of techniques and wondered, "what's the deal with all these?".

Run-Through of the Four String Formatting Techniques

Let's start off with the most commonly seen method which uses the % operator. I say the most common simply because it has been around the longest and you see it all over the place (books, blog posts, Stack Overflow, etc...). To utilize this method you specify a placeholder in a string using %s for strings and %d for numbers.

>>> "Hello reader, welcome to the %s form of string formatting." % 'modulus'
'Hello reader, welcome to the modulus form of string formatting.'  
>>>
>>> "Formatting multiple (%d, %d, %d, ...) values requires a %s." % (1, 2, 3, 'tuple')
'Formatting multiple (1, 2, 3, ...) values requires a tuple.'  
>>>
>>> print("""If you prefer named placeholders for values %(one)d, %(two)d,
... %(three)d ... you can use a dict""" % {'one':1, 'two':2, 'three':3})
If you prefer named placeholders for values 1, 2,  
3 ... you can use a dict  

The next technique to cover is the str.format(...) method. This was intended to be the replacement for the % style shown previously. This technique uses the curly brackets {} designator to specify where and how to format a value into a string.

>>> "Hello reader, welcome to the {} form of string formatting".format('str.format(...)')
'Hello reader, welcome to the str.format(...) form of string formatting'  
>>>
>>> print("""Formatting multiple ({0}, {1}, {2}, ...) values requires  
... that you use multiple {3} brackets and optionally specify ordering
... values.""".format(1,2,3,'{}'))
Formatting multiple (1, 2, 3, ...) values requires  
that you use multiple {} brackets and optionally specify ordering  
values.  
>>>
>>> print("""The {language} str.format() method also allows you to use
... named parameters which help keep code {adjective}
... """.format(language='Python', adjective='concise'))
The Python str.format() method also allows you to use  
named parameters which help keep code concise  

Then there is the string Template technique, which is a class in the string module. This method of string formatting is a bit more verbose and doesn't support type specifiers (s, d, f, etc...), unlike the previous two. With this technique you specify the placeholder in a string by prefixing the desired value with a $ in the constructor of the Template(...) class, then you call a substitute(...) method on the instantiated object with a named parameter. This method is far less common due to its reduced power and flexibility.

>>> from string import Template
>>> tmpl = Template("Hello my name is $name")
>>> tmpl.substitute(name='Adam')
'Hello my name is Adam'  

The last technique, and most recent implementation which is only available in Python 3.6, is known as string interpolation. This has similarities to the Javascript ES6 template string. String interpolations requires that the string literal be prefixed with an f"" and allows for both expressions and variables to be specified directly within the string as long they are surrounded by {} brackets.

>>> method="String Interpolation"
>>> f"Hello reader, I am the {method} of formatting"
'Hello reader, I am the String Interpolation of formatting'  
>>>
>>> f"With this method you can have expressions like {{1 + 1}} = {1 + 1}"
'With this method you can have expressions like {1 + 1} = 2'  

Digging Deeper into String Formatting

In the following sections I am going to restrict the discussion to only the str.format() method and f"" interpolation technique as they are the preferred methods for formatting strings. The topics I would like to dig deeper into include:

  1. Text alignment
  2. Formatting numerics
  3. Type conversions

Both the str.format() and interpolation techniques share the same syntax for defining the formatting between the {} brackets which make use of the : to separate named or ordinal identifiers to the left and formatting specifications to the right.

Text Alignment

You can align values within a specified length of text by using the <, >, or ^ symbols to specify left align, right align, or centering, respectively. Then you follow the those symbols with a character width you desire.

Python > 2.6:

>>> left_aligned = "Left Align"
>>> center = "Centered"
>>> right_aligned = "Right Align"
>>> "{left_aligned:<15}{center:^10}{right_aligned:>15}".format(
...     left_aligned=left_aligned,
...     center=center,
...     right_aligned=right_aligned)
'Left Align      Centered     Right Align'  

You can also specify the ordinal positions rather than keywords.

>>> "{1:<15}{0:^10}{2:>15}".format(center, left_aligned, right_aligned)
'Left Align      Centered     Right Align'  

Or, you can omit them if the order of the parameters to format(...) are the same order of the {}.

>>> "{:<15}{:^10}{:>15}".format(left_aligned, center, right_aligned)
'Left Align      Centered     Right Align'  

Python 3.6:

>>> f"{left_aligned:<15}{center:^10}{right_aligned:>15}"
'Left Align      Centered     Right Align'  

In the previous examples I implicitly filled the remaining padded space with blank spaces which is the default behavior. However, if this is not what you desire you can fill them with something different by specifying a character immediately after the colon.

Python > 2.6:

>>> "{:><15}|{:-^10}|{:<>15}".format(left_aligned, center, right_aligned)
'Left Align>>>>>|-Centered-|<<<<Right Align'  

Python 3.6:

>>> f"{left_aligned:><15}{center:-^10}{right_aligned:<>15}"
'Left Align>>>>>-Centered-<<<<Right Align'  

Numbers

Formatting floating point numbers, those containing decimal places, is a cinch in Python. All you need to do is follow the colon with an f.

Python > 2.6:

>>> rounded_pi = 3.14
>>> "A rounded representation of Pi {:f}".format(rounded_pi)
'A rounded representation of Pi 3.140000'  

Python 3.6:

>>> f"A rounded representation of Pi {rounded_pi:f}"
'A rounded representation of Pi 3.140000'  

Notice that the string has six decimal places. This is because by default the float specifier is given six places which it will either fill with zeros or round to only contain six depending on the input. For example, if I import the longer pi constant from the math module you'll see the rounding in action.

Python > 2.6:

>>> from math import pi
>>> pi
3.141592653589793  
>>> "A rounded representation of Pi {:f}".format(pi)
'A rounded representation of Pi 3.141593'  

Python 3.6:

>>> f"A rounded representation of Pi {pi:f}"
'A rounded representation of Pi 3.141593'  

To specify a different precision (number of decimal places) simply precede the f by the number of decimal places desired like so.

Python > 2.6:

>>> "A rounded representation of Pi {:.3f}".format(pi)
'A rounded representation of Pi 3.142'  

Python 3.6:

>>> f"A rounded representation of Pi {pi:.3f}"
'A rounded representation of Pi 3.142'  

Another formatting use-case for floating point numbers is the percent specifier. This works by converting what is expected to be a proportion or ratio (0-1) to a value out of 100 and treats the leftover decimal portion similar to the f specifier with a default precision of six.

Python > 2.6:

>>> receptions = 17
>>> passes = 29
>>> "The completion percentage is {:.2%}".format(receptions/passes)
'The completion percentage is 58.62%'  

Python 3.6:

>>> f"The completion percentage is {receptions/passes:.2%}"
'The completion percentage is 58.62%'  

Ok, so that takes care of most use cases involving floating point numbers, but what about large numbers? Python also has support for formatting these by putting in commas to increase readability of large numbers. To take advantage of this simply place a , after the colon.

Python > 2.6:

>>> house_price = 299999.99
>>> "The price of the house is ${:,}".format(house_price)
'The price of the house is $299,999.99'  

Python 3.6:

>>> f"The price of the house is ${house_price:,}"
'The price of the house is $299,999.99'  

Type Conversions

Type conversions are a bit of a less common use case but, they do come up from time to time. The major type conversions are the following for numbers:

Conversion Description
b Binary
o Octal
x Hexadecimal
d Decimal

As with the other format specifiers they work by adding them after the colon. Hopefully this general pattern of formatting syntax is becoming clear to you.

Python > 2.6:

>>> number = 157
>>> print("Binary: {:b}\nOctal {:o}\nHexadecimal: {:x}\nDecimal: {:d}".format(
...     number,
...     number,
...     number,
...     number))
Binary: 10011101  
Octal 235  
Hexadecimal: 9d  
Decimal: 157  

Python 3.6:

>>> print(f"Binary: {number:b}\nOctal {number:o}\nHexadecimal: {number:x}\nDecimal: {number:d}")
Binary: 10011101  
Octal 235  
Hexadecimal: 9d  
Decimal: 157  

Conclusion

In this brief tutorial on string formatting I have barely scratched the surface, but I hope that I was able to give some concrete examples of common use cases that you are likely to run across in your day-to-day Python programs. My goal has been to provide a basic explanation of the string formatting syntax and implementation techniques. From here you should be armed with enough understanding to dig into the details of the documentation. Thanks for reading along and feel free to comment below.

Author image
Lincoln, Nebraska Twitter