Pros of Different Python String Formatting Methods

Peter Xie
4 min readAug 13, 2019

There are three methods to format a string in Python:

  • %-format - Python 2+
  • {} str.format() - Python 3.1+
  • f-string - Python 3.6+

Note: From version 3.1, the positional argument specifiers can be omitted for str.format(), so '{} {}'.format(a, b) is equivalent to '{0} {1}'.format(a, b), though the method was firstly introduced in version 2.6.

I was confused when I started learning Python. If you have the same feeling, I recommend you have a read of Roberto Preste’s post first. He explains quite well what they are. Basically, they are equally effective and efficient. You can just pick one you like. But each of them is good in some cases, which I will explain in this post.

First, let’s have a look at how they are equally effective for formatting string.

# %-format
print(‘%s is %s’ % (‘pi’, 3.14) )
# str.format()
print(‘{} is {}’.format(‘pi’, 3.14) )
# f-string
var1 = ‘pi’
var2 = 3.14
print(f’{var1} is {var2}’)

All the three methods prints pi is 3.14.

Now let’s see the pros of each method one by one.

%-format

%-format is very similar to the classic C printf() function. So it is easy to understand or share with other people who are new to Python.

The big advantage of %-format is that you can pass a tuple or list as argument directly. This is very useful if you want to pass a long list of arguments, for instance, you read data from database or spreadsheet and format a long string in XML or JSON format.

replacement_str = '%s is %s'
arg_tuple = ('pi', 3.14)
print(replacement_str % arg_tuple)
# out:
pi is 3.14

Note: You can achieve the same in str.format() by passing the address of a tuple/list, e.g., *arg_tuple, which is not intuitive and simple, IMHO.

And it is handy if you want to define the same format for multiple data, where you define the format only once.

str_format = '%-10s, %s'
headers = ('Result','Message')
row1 = ('Successful','abc')
row2 = ('Failed','efg')
print(str_format % headers)
print(str_format % row1)
print(str_format % row2)
# Out:
Result , Message
Successful, abc
Failed , efg

Note: You can achieve the same in str.format() but not f-string.

str.format()

str.format() can avoid passing repeated arguments.

You can use the same position or argument name in replacement fields if the same argument is used more than once in the string.

print('{0} + {0} = {1}'.format('pi', 6.28) )
# out:
pi + pi = 6.28

Or you can pass one argument and access its multiple attributes or items in the string.

tuple1=('pi', 3.14)
print('{v[0]} is {v[1]}'.format(v=tuple1))
# out:
pi is 3.14

Equivalent formatting:

print('{} is {}'.format(tuple1[0],tuple1[1]))

Another useful case for str.format() is nesting argument. You can also achieve it using %-format, but it looks nicer using str.format(), IMHO.

In this example, we convert a dict to a list of string using {}-format, and join them with ‘\n’, then pass it as a nesting argument to the main string in {}-format.

dict1={'k1':1, 'k2':'two'}
print('{}\n{}'.format(
'Line 1',
'\n'.join('{}: {}'.format(k, v) for k, v in dict1.items())
)
)

Out:

Line 1
k1: 1
k2: two

f-string

f-string, also called formatted string literal, is pretty much identical to str.format(), but just put arguments directly in the string. It has a unique function, though. You can call an argument’s methods directly in the formatted string, while str.format() can call attributes or items only.

var1='abc'
print(f'{var1.upper()}')
# Out:
ABC

Performance Comparison

Inspired by Syed Komail Abbas’s post, I’ve done a performance comparison between these three methods in Python 3.7.4. It shows they are almost equally efficient.

Evaluate:

Formatting a + b = (a + b) as string for 1 million times.

Test Code:

from timeit import timeit
print(‘%-format:’,timeit("a=1;b=2;’%s + %s = %s’ % (a, b, a + b)", number=1000000))
print(‘f-string:’,timeit("a=1;b=2;f’{a} + {b} = {a + b}’", number=1000000))
print(‘str.format:’,timeit("a=1;b=2;’{} + {} = {}’.format(a, b, a + b)", number=1000000))

Test Environment:
Python 3.7.4
Windows 7
Ubuntu 16.04

Windows Performance Result:

%-format: 0.591294408000067
f-string: 0.6269198930003768
str.format: 0.7412427509998452

Linux Performance Result:

%-format: 0.3382550929673016
f-string: 0.312985380878672
str.format: 0.45186589611694217

As you can see %-format has the best performance on Windows while f-string has the best performance on Linux. But the performance difference between the three methods is not significant, so it won’t impact much on your choice of them.

Note: Surprisingly the result is quite different from Syed’s result, which shows f-string is much faster. Maybe there are some changes in Python version 3.7.4.

Without the string formatting statement, the same loop will take about 13% of the time, as shown below for Windows.

from timeit import timeit
print(timeit(“a=1;b=2”, number=1000000))
# Out:
0.0805546650008182

Summary

Each method has some small pros in some cases, but they are really interchangeable. It is more about a choice of taste in my opinion.

Thanks for reading.

--

--