Python String Formatting: Best Practices (with Examples)

In Python, string (str) is a common data type that is present in practically every code project. This is why it’s important to know how to format strings properly, such as how to execute code inside a string.

Even though string formatting is not hard, there are painfully many ways to do it.

This is a comprehensive guide to string formatting in Python.

After reading this guide, you know how to format strings in different ways to cover all versions of Python.

You will see three native string formatting approaches as well as an external library that gets the job done. Moreover, you’ll learn the pros and cons of each approach to help to choose the best approach.

4 Ways to Format Strings in Python

4 different python string formatting methods
4 ways to do the same thing!

This table summarizes the string formatting options in Python.

Method Example Use Cases
1. % Operator ‘%s %s’ % (a, b) Old method. Don’t use it anymore!
2. str.format() ‘{} {}’.format(a, b) Format strings in < Python 3.6
3. F-Strings f'{a} {b}’ Format strings in Python 3.6+
4. Template Strings Template(‘$a $b’).substitute(a=a, b=b) Format user-supplied strings.

1. Old-School String Formatting in Python (% Operator)

Formatting Python strings with the % operator

The old-school way to format strings in Python is by using the % operator. If you happen to have a background in C, you’ll notice the pattern instantly.

But for those who have no background in the printf function and C, let’s take a closer look at how the % operator works in Python.

For example, let’s embed a string variable inside another string:

name = "Alice"
sentence = "Hi, %s" % name

print(sentence)

Output:

Hi, Alice

Here the %s acts as a placeholder for a string variable that is going to be embedded in the string.

Syntax

The generic syntax for using the % operator to format strings is as follows:

"%t" % value

Where:

  • %t acts as a placeholder for the embedded code and t is one of the supported data types. For example, %s for string, %x for hexadecimal number.
  • value is the variable or code expression that will be embedded into the string.

Substituting Multiple Values

With the % formatting operator, you can substitute as many values to the strings as you want. But to make more than one substitution using the % operator, wrap the substituted values into a tuple.

For example, let’s embed two variables into a string:

her_name = "Alice"
my_name = "Bob"
my_age = 25

sentence = "Hi, %s. I'm %s and I'm %i years old!" % (her_name, my_name, my_age)

print(sentence)

Output:

Hi, Alice. I'm Bob and I'm 25 years old!

Substitute Executable Code in a String

Thus far you’ve seen examples of substituting static values into strings with the % operator. But you can also substitute executable code into the string.

For example, let’s randomize a number between 1 and 1000 by substituting the function call directly into the string:

from random import randrange

print("Random number is %i" % (randrange(1000)))

Output:

Random number is 584

Substitute Values with a Mapping

One more useful thing with the % formatting operator is you can directly refer to the substitution values by passing a mapping to the % operator.

Here’s what I mean:

her_name = "Alice"
my_name = "Bob"
my_age = 25

sentence = "Hi, %(her_name)s. I'm %(my_name)s and I'm %(my_age)i years old!" % {
    "her_name": her_name,
    "my_name": my_name,
    "my_age": my_age
}

print(sentence)

Output:

Hi, Alice. I'm Bob and I'm 25 years old!

When you do this, there’s no reason to worry about passing the substitutes in the right order. The downside to this is there’s a little more code to write.

Formatting Numbers

When it comes to formatting numeric types, there are lots of formatting options when using the % operator.

You can for example limit the number of decimal places or change the numeric system of a number.

Here are some examples of different formatting data types you can use with the % operator in Python.

# %c — a placeholder for single character
char = "A"
sentence = "The first letter in alphabetics is %c" % char
# --> The first letter in alphabetics is A

# %s — a string placeholder
name = "John"
sentence = "My name is %s" % name
# --> My name is John

# %i, %d — Placeholders for signed decimal integers
number = 10
sentence = "Number %i is my favorite number" % number
# --> Number 10 is my favorite number

# %x — a placeholder for a hexadecimal integer (in lowercase characters)
num = 255
sentence = "Number %i is %x in hexadecimal" % (num, num)
# --> Number 255 is ff in hexadecimal

# %e - a placeholder for exponential notation with lowercase 'e'
num = 1324314234
sentence = "Number %i is %e in exponential notation" % (num, num)
# --> Number 1324314234 is 1.324314e+09 in exponential notation

# %f - a placeholder for
price = 5.99
sentence = "The price is %f" % price
# --> The price is 5.990000

Make sure to check the rest of the supported data types if you’re interested.

Should You Use % Operator to Format Strings in Python?

In the newer versions of Python, the % formatting operator isn’t used anymore.

This is because more manageable and modern solutions exist. The % formatting dates back to Python versions under 2.7.

In other words, don’t use the % operator to format strings anymore (unless you’re running a really old version of <Python 2.7)

Next, let’s take a look at the newer version for string formatting.

2. The New Way to String Formatting (.format() Method)

string.format method in Python

When Python 3 came, they introduced a new way to format strings in Python. The whole point of the new string formatting approach was to get rid of the previously mentioned % operator and make string formatting easier.

In Python 3, the string formatting happens with the str.format() method you can call on any string. You can use the .format() method to do similar formatting you would do with the % operator.

Syntax

Here’s the generic syntax of the format() method.

'{}'.format(value)

Here the {} acts as a placeholder for the value (or code) to be inserted.

Also, you can substitute as many values as you like by adding more curly braces and arguments to the format() call.

'{} {} {}'.format(value1, value2, value3)

When you do this, the order of the values matter. That is, the value1 will be inserted into the first set of curly braces and so on.

To truly understand how the format() method works, let’s see examples.

Example 1. Inserting a Variable into a String

For instance, let’s insert a variable into a string using the format() method:

name = "Alice"
sentence = "Hi, {}".format(name)

print(sentence)

Output:

Hi, Alice

Example 2. Inserting Multiple Values into a String

Similar to the % operator, you can use the format() method to directly substitute a variable into a string by using its name.

For example:

her_name = "Alice"
my_name = "Bob"
my_age = 25

sentence = "Hi, {her_name}. I'm {my_name} and I'm {my_age} years old!".format(
    her_name=her_name,
    my_name=my_name,
    my_age=my_age
)

print(sentence)

Output:

Hi, Alice. I'm Bob and I'm 25 years old!

When you do this, there’s no reason to worry about the order you pass the arguments in the format() method.

Example 3. Substitute Executable Code

Notice that you can also run code inside a string with the format() method.

For example:

from random import randrange

print("Random number is {}".format(randrange(1000)))

Output:

Random number is 540

The above example uses the randrange() function to generate a random value and then inserts it inside the string.

Formatting Numbers

The str.format() method lets you specify the type of number data you’re inserting into a string. Besides, you can round numbers or add padding to the substituted values to make the strings look nicer.

Here’s what the generic syntax of number formatting looks like:

"{field:conversion}".format(value)

Where:

  • field is the index or name of the field to be inserted.
  • conversion is the supported number code with optional formatting codes, such as the number of decimal places.

The supported conversion number formats are:

Type Meaning
d Decimal integer
c Decimal integer’s Unicode character
b Binary format
o Octal format
x The hexadecimal format in lowercase
X The hexadecimal format in upper case
n Decimal integer with current locale setting for the number separator
e Exponential notation with lowercase e
E Exponential notation with upper case E
f Shows a fixed point number
F Shows a fixed point number but displays inf as INF and nan as NAN
g Rounds a number to n significant digits
G Rounds a number to n significant digits and uses E if the number is large.
% The percentage multiplies a number by 100 and puts % at the end.

With floating point values (f), you have an option for limiting the number of decimal places with . operator followed by the number of decimal places.

Here are some examples:

# Limiting decimal places
print("{:.3f}".format(3.141592))

# Showing the same integer in different basis
print("binary: {0:b}, octal: {0:o}, hexadecimal: {0:x}".format(235))

# Padding the formatted strings:
print("{:1d}".format(3))
print("{:2d}".format(2))
print("{:3d}".format(1))

Output:

3.142
binary: 11101011, octal: 353, hexadecimal: eb
3
 2
  1

Should You Use .format() Method to Format Strings in Python?

The str.format() method was added to Python 3 and it was later backported to Python versions as early as 2.7. In case you’re running a Python version between 2.7-3.5, the .format() method is the way to go.

But in case you’re running a newer version of Python 3.6+, there’s yet another string formatting approach you should use. Let’s talk about it next.

3. F-Strings and String Interpolation

formatted string literals or f-strings in python

Python 3.6 introduced one more string formatting method that’s even more intuitive and readable than the str.format() method. The new string formatting method is called the string formatting literals or f-strings for short.

With f-strings, you can get rid of the inconvenient .format() method and insert values and expressions directly inside strings by placing them inside curly braces. To create an f-string, you also need to add f in front of the string.

Here’s an example that gives you an idea of an f-string in Python:

name = "Alice"
sentence = f"Hi, {name}"

print(sentence)

Output:

Hi, Alice

Syntax

Here’s the generic syntax for using the f-string in Python:

f"{val}"

Here the label f tells the Python interpreter that it’s going to be a formatted string literal. The val is a value or expression that Python runs and inserts into the string.

Similar to other formatting approaches, you can insert multiple values and even runnable code inside the f-string.

f"{val1} {val2} {val3}"

Let’s take a look at some examples.

Example 1. Inserting a Variable into a String

For instance, let’s insert a variable into a string using the f-string formatting:

name = "Alice"
sentence = f"Hi, {name}"

print(sentence)

Output:

Hi, Alice

Example 2. Inserting Multiple Values into a String

For example:

her_name = "Alice"
my_name = "Bob"
my_age = 25

sentence = f"Hi, {her_name}. I'm {my_name} and I'm {my_age} years old!"

print(sentence)

Output:

Hi, Alice. I'm Bob and I'm 25 years old!

Notice how clean this formatting approach is. If you compare this example with the earlier formatting examples, you either had to use the format() method or the % operator followed by the mapping of the arguments. Now there’s no need to do anything else than insert the values where they belong.

Example 3. Substituting Executable Code

Notice that you can also run code inside the f-strings.

For example, let’s randomize a number between 1-1000 using the randrange() function:

from random import randrange

print("Random number is {}".format(randrange(1000)))

Output:

Random number is 540

The above code uses the randrange() function to generate a random number directly inside the formatted string literal. Python interpreter runs this code and inserts it into the string.

Formatting Numbers

Similar to the % operator and .format() method, the f-string supports extensive formatting options for numbers. For instance, you can limit the number of decimal places or change the basis of the numbers.

Here’s the generic syntax for advanced number formatting:

f"{value:conversion}"

Where:

  • value is the value or expression to be substituted.
  • conversion is the supported number code with optional formatting codes, such as the number of decimal places.

Here are the supported number types (these are the same as the .format() method). You can place any of these numeric types as the conversion label.

Type Meaning
d Decimal integer
c Decimal integer’s Unicode character
b Binary format
o Octal format
x The hexadecimal format in lowercase
X The hexadecimal format in upper case
n Decimal integer with current locale setting for the number separator
e Exponential notation with lowercase e
E Exponential notation with upper case E
f Shows a fixed point number
F Shows a fixed point number but displays inf as INF and nan as NAN
g Rounds a number to n significant digits
G Rounds a number to n significant digits and uses E if the number is large.
% The percentage multiplies a number by 100 and puts % at the end.

Here are some examples of formatting these numeric types:

# Limiting decimal places
print(f"{3.141592:.3f}")

# Showing the same integer in different basis
n = 235
print(f"binary: {n:b}, octal: {n}, hexadecimal: {n:x}")

# Padding the formatted strings:
print(f"{3:1d}")
print(f"{2:2d}")
print(f"{1:3d}")

Output:

3.142
binary: 11101011, octal: 235, hexadecimal: eb
3
 2
  1

Notice how the idea of formatting f-strings is the same as the .format() method. The only difference is you can just place the values into their placeholders directly instead of using a redundant .format() method.

Should You Use f-Strings to Format Strings in Python?

F-Strings offers you the most convenient and intuitive way to format strings in Python.

Notice that this way of string formatting is only available in Python 3.6+. If you’re running Python 3.6+, you should use f-strings as they make your code readable and easier to manage.

But if you’re running a lower Python version, then you should stick with the .format() method.

This concludes the native string formatting approaches in Python.

Last but not least, let’s talk about a string formatting library that offers slightly less extensive string formatting that might sometimes be what you’re looking for.

4. Template Strings (Library)

Python string template for string formatting

In Python, you can also rely on a built-in string formatting library called Template Strings. This library offers a less powerful and simpler string formatting approach that is a secure choice for formatting user-provided string inputs.

To use the Template formatting class, import the Template class from the string module.

from string import Template

Example

Here’s an example similar to what you’ve seen earlier that inserts a value into a string with the Template formatter:

from string import Template

name = "Alice"
t = Template('Hi, $name')

print(t.substitute(name=name))

Output:

Hi, Alice

The key difference between Template formatter and other string formatting approaches is you cannot use format specifiers with the Template.

This means you cannot use something like “{n:x}” to convert a number to hexadecimal, as you did in the f-strings, for example. Instead, you need to manually convert the value with the hex() function.

from string import Template

n = 235
print(Template("hexadecimal: $n").substitute(n=hex(n)))

Output:

hexadecimal: 0xeb

When Should I Use Template to Format Strings?

A great place to use the Template String for formatting strings is when dealing with user-supplied inputs. This is because there’s always a risk of a malicious user trying to do harm with the input.

Thanks to the reduced complexity of the Template, a malicious user has fewer options to craft the input in a hazardous manner.

For example, let’s see what a hacker could do if you used .format() method to substitute the user input in a string:

SECRET = 'secret value - no user should access this'

class Sample:
    def __init__(self):
        pass

# An input crafted with malicious intent
user_input = '{sample.__init__.__globals__[SECRET]}'

s = Sample()

# The user can read the SECRET with the input
print(user_input.format(sample=s))

Output:

secret value - no user should access this

By doing some careful thinking, a malicious user could craft input to reveal the values of variables of your code which is obviously a bad thing.

But with the Template String, this is no longer a risk, because an input like that cannot be inserted:

from string import Template

SECRET = 'secret value - no user should access this'

class Sample:
    def __init__(self):
        pass

# An input crafted with malicious intent
user_input = '${error.__init__.__globals__[SECRET]}'

s = Sample()

# The user can no longer read the SECRET with the input!
print(Template(user_input).substitute(sample=s))

Output:

ValueError: Invalid placeholder in string: line 1, col 1

As you can see, instead of accessing the __globals__, the malicious user only triggers an error.

Summary

Today you learned how to do string formatting in Python.

To recap, there are four ways you can use to format strings.

  1. The % operator (< Python 2.7)
  2. The str.format() method (Python 2.7 – Python 3.5)
  3. The F-strings (Python 3.6+)
  4. The Template library (User-supplied strings)

The approach you should use depends on the Python version. Also, if you’re formatting user-supplied strings, you should go with the 4. approach.

Thanks for reading. Happy coding!

Read Also