String Interpolation in Python: Exploring Available Tools

String Interpolation in Python: Exploring Available Tools

by Leodanis Pozo Ramos Jun 03, 2024 basics best-practices python

String interpolation allows you to create strings by inserting objects into specific places in a target string template. Python has several tools for string interpolation, including f-strings, the str.format() method, and the modulo operator (%). Python’s string module also provides the Template class, which you can use for string interpolation.

In this tutorial, you’ll:

  • Learn how to use f-strings for eager string interpolation
  • Perform lazy string interpolation using the str.format() method
  • Learn the basics of using the modulo operator (%) for string interpolation
  • Decide whether to use f-strings or the str.format() method for interpolation
  • Create templates for string interpolation with string.Template

To get the most out of this tutorial, you should be familiar with Python strings, which are represented by the str class.

Take the Quiz: Test your knowledge with our interactive “String Interpolation in Python: Exploring Available Tools” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

String Interpolation in Python: Exploring Available Tools

Take this quiz to test your understanding of the available tools for string interpolation in Python, as well as their strengths and weaknesses. These tools include f-strings, the .format() method, and the modulo operator.

String Interpolation in Python

Sometimes, when working with strings, you’d make up strings by using multiple different string values. Initially, you could use the plus operator (+) to concatenate strings in Python. However, this approach results in code with many quotes and pluses:

Python
>>> name = "Pythonista"
>>> day = "Friday"  # Of course 😃

>>> "Hello, " + name + "! Today is " + day + "."
'Hello, Pythonista! Today is Friday.'

In this example, you build a string using some text and a couple of variables that hold string values. The many plus signs make the code hard to read and write. Python must have a better and cleaner way.

The modulo operator (%) came to make the syntax a bit better:

Python
>>> "Hello, %s! Today is %s." % (name, day)
'Hello, Pythonista! Today is Friday.'

In this example, you use the modulo operator to insert the name and day variables into the string literals. The process of creating strings by inserting other strings into them, as you did here, is known as string interpolation.

The %s combination of characters is known as a conversion specifier. They work as replacement fields. The % operator marks the start of the specifier, while the s letter is the conversion type and tells the operator that you want to convert the input object into a string. You’ll learn more about conversion specifiers in the section about the modulo operator.

But the story doesn’t end with the modulo operator. Later, Python introduced the str.format() method:

Python
>>> "Hello, {}! Today is {}.".format(name, day)
'Hello, Pythonista! Today is Friday.'

The method interpolates its arguments into the target string using replacement fields limited by curly brackets. Even though this method can produce hard-to-read code, it represents a significant advance over the modulo operator: it supports the string formatting mini-language.

Python continues to evolve, and every new version brings new, exciting features. Python 3.6 introduced formatted string literals, or f-strings for short:

Python
>>> f"Hello, {name}! Today is {day}."
'Hello, Pythonista! Today is Friday.'

F-strings offer a more readable and clean way to create strings that include other strings. To make an f-string, you must prefix it with an f or F. Again, curly brackets delimit the replacement fields.

F-strings are probably the most popular interpolation tool nowadays in Python. They’re readable, quick to write, and efficient. So, you’ll start with f-strings and how to use them to build new strings using interpolation.

Using F-String Literals to Build Strings

For most interpolation use cases, f-strings are probably the best choice if you’re using Python 3.6 or greater. Their syntax is readable and concise. Additionally, they’ll run faster than other tools.

One use case where f-strings aren’t the way to go is when you need to do lazy interpolation. In other words, when you need to create a string template and insert the components later depending on the code’s execution. In this case, Python takes the lazy option and delays working out the value returned by an expression until that value is needed.

Another consideration to keep in mind is security. Because f-strings evaluate at runtime, they could potentially expose your application to code injection attacks if you don’t carefully sanitize users’ input before interpolatation.

In the following sections, you’ll learn how to use f-strings for eager string interpolation in Python.

Interpolating Values in F-Strings

Using f-strings, you can interpolate variables and expressions directly into your strings. Then, when Python executes the f-string, the variable’s content or the expression’s result will be interpolated into the f-string literal to build the final string:

Python
>>> x = 5
>>> y = 10

>>> f"The sum of {x} and {y} is {x + y}."
'The sum of 5 and 10 is 15.'

In this example, you have two variables, x and y. Then, you create an f-string literal with three replacement fields. The first two fields hold the variables, and the third field holds an expression.

It’s important to note that Python evaluates f-strings at runtime. So, in this example, x, y, and x + y are evaluated and interpolated into the string literal when Python runs the line of code containing the f-string.

You can embed almost any Python expression in an f-string, such as arithmetic, comparison, and Boolean expressions. You can also use functions and method calls, and even comprehensions or other more complex expressions:

Python
>>> import math
>>> radius = 16
>>> f"The area of your circle is {math.pi * radius ** 2}"
'The area of your circle is 804.247719318987'

>>> name = "Pythonista"
>>> site = "real python"
>>> f"Hello, {name.upper()}! Welcome to {site.title()}!"
'Hello, PYTHONISTA! Welcome to Real Python!'

>>> f"{[2**n for n in range(3, 9)]}"
'[8, 16, 32, 64, 128, 256]'

In the first f-string, you embed a math expression into the replacement field. In the second example, you use the .upper() and .title() string methods in the replacement fields. Python evaluates the expression and calls the method for you. Then, it inserts the results into the resulting f-string literal. In the final example, you create an f-string that embeds a list comprehension. The comprehension creates a new list of powers of 2.

F-strings before Python 3.12 have a few limitations that you must consider when working with them. Inside the replacement fields, you can’t:

  • Reuse quotes or string delimiters
  • Embed backslashes, which means you can’t use escape sequences
  • Add inline comments
  • Nest f-strings beyond the available quoting variations

PEP 536 lists all these limitations. To see them in action, check out the F-Strings Had Some Limitations Before Python 3.12 section in the Python 3.12 Preview tutorial.

Self-Documenting the Interpolated Value

With f-strings, you can use a feature known as self-documenting expressions that adds an equal sign after the interpolated variable or expression. This feature can help you debug your code. For quick debugging, most people use the built-in print() function to check the value of a variable or the result of an expression:

Python
>>> value = "Suspicious value"

>>> print(f"{value = }")
variable = 'Suspicious value'

>>> f"{2 + 3 = }"
'2 + 3 = 5'

You can use a variable or an expression followed by an equal sign (=) in an f-string to create a self-documenting expression. When Python runs the f-string, it builds an expression-like string containing the variable or expression, the equal sign, and the current result.

In these examples, the whitespaces around the equal sign aren’t required, but they make the output more readable.

The self-documenting expressions feature adds readability to your string interpolation process and can be an excellent tool for quick debugging with print().

Using Different String Representations in F-Strings

F-strings allow you to use two flags with special meaning in the interpolation process. These flags relate to how Python deals with an object’s string representation. Here are the flags and their intended meaning:

Flag Description
!s Interpolates the string representation using .__str__()
!r Interpolates the string representation using .__repr__()

Ideally, the .__str__() special method should provide a user-friendly string representation of an object. Python falls back to calling this method when you use the str() function. Meanwhile, the .__repr__() method returns a developer-friendly representation, which you get when you use the repr() function.

To illustrate how these flags work, consider the following sample class:

Python article.py
class Article:
    def __init__(self, title, author, pub_date):
        self.title = title
        self.author = author
        self.pub_date = pub_date

    def __str__(self):
        return (
            f"Article: {self.title}\n"
            f"Author: {self.author}\n"
            f"Published: {self.pub_date}\n"
        )

    def __repr__(self):
        return (
            f"{type(self).__name__}("
            f"title={self.title!r}, "
            f"author={self.author!r}, "
            f"pub_date={self.pub_date!r})"
        )

This Article class has three instance attributes .title, .author, and .pub_date. The .__str__() method returns a string containing the article’s information in a user-friendly format. This message is targeted to end users rather than developers.

The .__repr__() method returns a string that’s a developer-friendly representation of the object. In short, the representation tells the developer how the current instance was created. Ideally, the developer should be able to copy this string representation and create an equivalent object.

Now your class is ready for the !s and !r flags:

Python
>>> from article import Article

>>> article = Article(
...     title="String Interpolation in Python: Exploring Available Tools",
...     author="Real Python",
...     pub_date="2024-06-03",
... )

>>> print(f"{article!s}")
Article: String Interpolation in Python: Exploring Available Tools
Author: Real Python
Published: 2024-06-03

>>> print(f"{article!r}")
Article(
    title='String Interpolation in Python: Exploring Available Tools',
    author='Real Python',
    pub_date='2024-06-03'
)

In the first f-string, you use the !s tag to interpolate the string representation that .__str__() returns. In the second f-string, you use the !r flag to interpolate the developer-friendly string representation of your object. Notice that, in the latter case, the resulting string object represents a valid Python code that you can evaluate.

Creating Strings With the str.format() Method

If you need to interpolate values into strings lazily, then the str.format() method is for you. This method is a versatile tool for string interpolation in Python. It provides a readable syntax and allows for both eager and lazy interpolation.

In the following sections, you’ll learn how to use the .format() method for lazy interpolation because, in most cases, you’d use f-strings for eager interpolation.

Using Positional and Named Arguments

To interpolate objects into a string using the .format() method, you can use three different approaches. You can use:

  1. Empty replacement fields, {}
  2. Replacement fields with zero-based indices, {0} ... {n}
  3. Replacement fields with named arguments, {arg_1} ... {arg_n}

To illustrate how these options work, say that you need to automate the process of generating emails for the customers who purchase products from your company. You can create an email template and then interpolate the customer data dynamically:

Python
>>> template = """
... Dear {},
...
... Thank you for your recent purchase of {}.
...
... Remember, our support team is always here to assist you.
...
... Best regards,
... {}
... """

>>> print(template.format("Emily", "MacBook Pro 16-inch", "John"))

Dear Emily,

Thank you for your recent purchase of MacBook Pro 16-inch.

Remember, our support team is always here to assist you.

Best regards,
John

In this example, you create an email template with three empty replacement fields. The .format() method inserts the passed values in the appropriate field using their relative position. If you want to have a bit more control over how the values are interpolated, then you can use integer indices:

Python
>>> template = """
... Dear {0},
...
... Thank you for your recent purchase of {1}.
...
... Remember, our support team is always here to assist you.
...
... Best regards,
... {2}
... """

>>> print(template.format("Linda", "Samsung Galaxy S22", "Jane"))

Dear Linda,

Thank you for your recent purchase of Samsung Galaxy S22.

Remember, our support team is always here to assist you.

Best regards,
Jane

In this example, the result is similar. However, now you’re certain that the first argument, "Linda", will go to index 0, the second argument, "Samsung Galaxy S22" will go to index 1, and so on. This way of handling the arguments can be useful when the arguments’ original order isn’t the same as the order in the final string:

Python
>>> template = """
... Dear {1},
...
... Thank you for your recent purchase of {0}.
...
... Remember, our support team is always here to assist you.
...
... Best regards,
... {2}
... """

>>> purchase = ("Samsung Galaxy S22", "Linda", "Jane")

>>> print(template.format(*purchase))

Dear Linda,

Thank you for your recent purchase of Samsung Galaxy S22.

Remember, our support team is always here to assist you.

Best regards,
Jane

Here, the order of the items in the purchase tuple doesn’t match the natural order in the template. So, you move the indices according to the new order and get the desired result.

Even though the above options work okay, they aren’t completely readable. Fortunately, there’s a better way. You can use keyword arguments with .format(). Here’s how:

Python
>>> template = """
... Dear {customer},
...
... Thank you for your recent purchase of {product}.
...
... Remember, our support team is always here to assist you.
...
... Best regards,
... {employee}
... """

>>> print(
...     template.format(
...         customer="Bob",
...         product="Canon EOS R5",
...         employee="Kate"
...     )
... )

Dear Bob,

Thank you for your recent purchase of Canon EOS R5.

Remember, our support team is always here to assist you.

Best regards,
Kate

In this update, you’ve used explicit names in the replacement fields. These names match the keyword arguments you used when calling .format(). Your code now looks much more readable.

Finally, it’s also possible to use dictionaries to feed the .format() method. Say that you’re retrieving your data from a CSV file that looks something like this:

CSV sales.csv
product,customer,employee
MacBook Pro 16-inch,Emily,John
Samsung Galaxy S22,Linda,Jane
Canon EOS R5,Bob,Kate

You can use the csv module from the standard library to process this file and load its content so that you can create emails for each sale. The csv.DictReader is a good tool for this task. This reader allows you to read every line in a CSV file into a dictionary. The keys will be the file headings, and the values will be the values in each row.

Here’s the code that you can use to do the job:

Python emails.py
import csv

template = """
Dear {customer},

Thank you for your recent purchase of {product}.

Remember, our support team is always here to assist you.

Best regards,
{employee}
"""

def display_emails(template, path):
    with open(path) as file:
        for customer in csv.DictReader(file):
            print(template.format(**customer))

display_emails(template, "sales.csv")

In this code, you first import csv from the standard library. Then, you have the usual email template with names in the replacement fields. The names match the headings in the CSV file.

Next, you have the display_emails() function. This function takes two arguments: the email template and the path to the CSV file. Inside the function, you open the input file for reading using a with statement.

The for loop iterates over the lines of the file using the DictReader class. Finally, you use the .format() method to interpolate the values in the current line into the email template. In this example, you use the dictionary unpacking operator (**) to provide the arguments to .format().

Go ahead and run this script from your command line to check the output.

There’s another interesting behavior of .format() when you use it with dictionaries. Here’s a quick toy example:

Python
>>> numbers = {"one": 1, "two": 2, "three": 3}

>>> "{one}-{two}".format(**numbers)
'1-2'

>>> "{one}-{two}-{three}".format(**numbers)
'1-2-3'

>>> "{one}-{two}-{three}-{four}".format(**numbers)
Traceback (most recent call last):
    ...
KeyError: 'four'

When the keys in the input dictionary match the named arguments in the string, the interpolation works even if you have unused keys. When the keys don’t match the named arguments, then you get a KeyError exception.

Using Different String Representations With .format()

Like with f-strings, you can also use the !s and !r flags with .format() to insert objects into your strings using different string representations. Reusing the Article class from the Using Different String Representations in F-Strings section, here are two examples that show how the flags work:

Python
>>> from article import Article

>>> article = Article(
...     title="String Interpolation in Python: Exploring Available Tools",
...     author="Real Python",
...     pub_date="2024-06-03",
... )

>>> print("{article!s}".format(article=article))
Article: String Interpolation in Python: Exploring Available Tools
Author: Real Python
Published: 2024-06-03

>>> print("{article!r}".format(article=article))
Article(
    title='String Interpolation in Python: Exploring Available Tools',
    author='Real Python',
    pub_date='2024-06-03'
)

Again, the !s flag allows you to use the user-friendly string representation of the object at hand. In contrast, the !r flag allows you to use the developer-friendly representation. You’ll decide which flag to use in your code by considering the target audience of your code.

Using the Modulo Operator (%) for Interpolation

Using the modulo operator (%) for string interpolation is largely obsolete in modern Python. However, this tool still works, and you’ll probably find legacy code that uses it. So, it’s good to know how it works.

The modulo operator (%) is the oldest tool for performing string interpolation in Python. Even though you can use this operator for both eager and lazy string interpolation, the syntax could be more readable and clean.

You need to insert conversion specifiers into your strings and then use the modulo operator to interpolate the desired values:

Python
>>> x = 5
>>> y = 10

>>> "The sum of %d and %d is %d." % (x, y, x + y)
'The sum of 5 and 10 is 15.'

A combination of characters starting with the percent sign (%) is known as a conversion specifier. In this example, you’ve used the %d specifier, which means that you want to convert a signed integer decimal value into a string. The conversion specifiers work as replacement fields for the modulo operator.

To do lazy interpolation, you can do something like the following:

Python
>>> template = "The sum of %d and %d is %d."

>>> template % (x, y, x + y)
'The sum of 5 and 10 is 15.'

In this example, you create a template string with the required replacement fields. Then, you use the modulo operator to interpolate values into the template later in your code. This practice allows you to reuse the template string in several different parts of your code.

Then, you have the modulo operator and a tuple of values or expressions. The operator will interpolate each value in this tuple into the appropriate specifier using their position.

Interpolating One or More Values

In the previous section, you saw an example where you interpolated several values or expressions into a string using the modulo operator. If you need to insert only one value, then you can skip the tuple or use a single-item tuple:

Python
>>> "Hello, %s!" % "Pythonista"
'Hello, Pythonista!'

>>> "Hello, %s!" % ("Pythonista",)
'Hello, Pythonista!'

The first syntax is a bit cleaner than the second syntax. However, for consistency throughout your code, you may want to use the second syntax.

Now, what happens when you need to interpolate a tuple object? Here’s an example:

Python
>>> "Interpolating a tuple: %s" % (1, 2, 3)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    "Interpolating a tuple: %s" % (1, 2, 3)
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
TypeError: not all arguments converted during string formatting

In this example, Python interprets the tuple as three different values, and you get an error because the string has only one replacement field. To work around this issue, you need to use a single-item tuple:

Python
>>> "Interpolating a tuple: %s" % ((1, 2, 3),)
'Interpolating a tuple: (1, 2, 3)'

Now, the interpolation works correctly, and you end up with the tuple inserted into the string. It’s important to note that you must use tuples if you intend to feed multiple values into the modulo operator:

Python
>>> "Hello, %s! Today is %s." % [name, day]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    "Hello, %s! Today is %s." % [name, day]
     ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
TypeError: not enough arguments for format string

>>> "The sum of %d and %d is %d." % [x, y, x + y]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    "The sum of %d and %d is %d." % [x, y, x + y]
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
TypeError: %d format: a real number is required, not list

If you use a list object, or another iterable, then Python will interpret it as a single object, and the interpolation will fail with a TypeError exception.

Using Named Replacement Fields

In the previous section, you learned that you should use a tuple to provide multiple arguments to the modulo operator for string interpolation. This way, the operator inserts the values in the tuple into the target string by their position, but this isn’t that readable.

Fortunately, there’s a better way. You can also use dictionaries and named replacement fields:

Python
>>> jane = {"name": "Jane", "job": "Python Dev"}

>>> "My name is %(name)s. I'm a %(job)s." % jane
"My name is Jane. I'm a Python Dev."

In this example, the modulo operator inserts each value using the corresponding key, which is way more readable and intuitive. To build the named replacement fields, you need to insert the name in parentheses between the % sign and the format specifier.

Using Different String Representations

You can also use the different string representations of objects with the modulo operator for string interpolation. You already learned that the %s conversion specifier converts the object into strings. To do this, the specifier uses the user-friendly representation from the .__str__() special method.

To use the developer-friendly string representation provided in .__repr__(), you can use the %r conversion specifier.

To illustrate how to do this, you can use the Article class again:

Python
>>> from article import Article

>>> article = Article(
...     title="String Interpolation in Python: Exploring Available Tools",
...     author="Real Python",
...     pub_date="2024-06-03",
... )

>>> print("%s" % article)
Article: String Interpolation in Python: Exploring Available Tools
Author: Real Python
Published: 2024-06-03

>>> print("%r" % article)
Article(
    title='String Interpolation in Python: Exploring Available Tools',
    author='Real Python',
    pub_date='2024-06-03'
)

The resulting behavior is the same as with f-strings and the .format() method. Again, the choice of which string representation to use will depend on your code’s target audience.

Using F-Strings vs .format() vs %

Up to this point, you’ve learned about three different Python tools that you can use for string interpolation. One question that may arise is: Which tool should you use? As with many things, the answer is: It depends.

If you want readable syntax and good performance in string interpolation and you’re only doing eager interpolation, then f-strings are for you. If you need a tool for doing lazy string interpolation, then the .format() method is the way to go.

In contrast, the modulo operator (%) is an old-fashioned tool not commonly used in modern Python. You could say that this tool is almost dead. However, you may find it in legacy Python code, so it’s good to learn how it works.

The following table compares the three tools using several comparison criteria:

Feature F-strings .format() %
Readability High Medium Low
Supports lazy evaluation ⛔️
Supports dictionary unpacking ⛔️
Supports the format mini-language ⛔️

F-strings are the clear winner in terms of readability. However, they don’t allow you to do lazy interpolation. There’s no way to use an f-string to create a reusable string template that you can interpolate later in your code.

Additionally, you can’t use a dictionary to provide the input values in one go. This doesn’t mean that you can’t interpolate dictionary keys into an f-string:

Python
>>> numbers = {"one": 1, "two": 2, "three": 3}

>>> f"{numbers['one']}-{numbers['two']}-{numbers['three']}"
'1-2-3'

To interpolate dictionary keys into an f-string, you need to insert the key in the desired replacement field. This can make your f-string look cluttered and hard to read and write. You’re better off using the .format() method in such cases:

Python
>>> "{one}-{two}-{three}".format(**numbers)
'1-2-3'

>>> "{one}-{two}".format(**numbers)
'1-2'

This code is much more readable and quicker to write than the f-string version. As an added benefit, the number of keys in the input dictionary doesn’t have to match the number of replacement fields in the template string, making the code more flexible and generic.

Finally, both f-strings and the .format() method support the Python string formatting mini-language, which allows you to nicely format the interpolated values. For a quick example, here’s how you can format the π constant using four decimal places:

Python
>>> import math
>>> math.pi
3.141592653589793

>>> f"{math.pi:.4f}"
'3.1416'

>>> "{pi:.4f}".format(pi=math.pi)
'3.1416'

Formatting interpolated values using the formatting mini-language is beyond the scope of this tutorial. If you want to explore this topic further, check out the Python’s Format Mini-Language for Tidy Strings tutorial.

Building Templates With the Template Class

Python has yet another tool for performing string interpolation. In the string module, you’ll find the Template class. As its name suggests, this class allows you to create string templates that you can use for lazy interpolation.

You’ll find two main differences between Template and standard string interpolation tools. With Template, the type of interpolated values isn’t considered. The values are automatically converted into strings and then inserted into the template.

Also, Template doesn’t support string formatting. On the other hand, the standard tools have the advantage of supporting the string formatting mini-language, which will help you fine-tune your strings.

To create a template string with Template, you need a regular Python string with embedded placeholders. These placeholders consist of two parts:

  1. The dollar sign ($)
  2. A valid Python identifier

Valid identifiers are those that you can use as variable names in Python. They combine uppercase and lowercase letters, underscores (_), and digits. Identifiers can’t begin with digits or match a Python keyword. For example, $name, $age, $Tag, $class_, and $item_1 are all valid placeholders.

Once you’ve created the string with the appropriate placeholders, you need to:

  1. Import Template from the string module
  2. Instantiate Template using the template string as an argument
  3. Perform the substitution with one of two relevant methods

Here’s a quick example of how you can use Template in your code:

Python
>>> from string import Template

>>> template = Template("Hello, $name! Today is $day.")
>>> template.substitute(name="John", day="Friday")
'Hello, John! Today is Friday.'

In this example, you use a template string with two placeholders, $name and $day, as an argument to Template. Once you’ve instantiated the class, you can call .substitute() to interpolate values. Note that the names of the arguments you pass to .substitute() need to match the identifiers used in the placeholders of your template string.

In the following sections, you’ll learn more about creating template strings with the Template class.

Building Template Strings

To create valid template strings that you can feed to the Template class, you consider some basic rules. Here’s how PEP 292 describes these rules:

  1. $$ is an escape; it is replaced with a single $
  2. $identifier names a substitution placeholder matching a mapping key of “identifier”. By default, “identifier” must spell a Python identifier as defined in [2]. The first non-identifier character after the $ character terminates this placeholder specification.
  3. ${identifier} is equivalent to $identifier. It is required when valid identifier characters follow the placeholder but are not part of the placeholder, e.g. "${noun}ification". (Source)

To kick things off, you’ll start with an example of how to escape the dollar sign ($), which is required to express currency values, for example:

Python
>>> from string import Template

>>> Template("$$$amount").substitute(amount="1,000.00")
'$1,000.00'

In this string template, the first two dollar signs escape the required dollar sign, and the last dollar sign defines the placeholder.

The second rule states that every placeholder needs a $ character followed by a valid Python identifier. Here’s an example:

Python
>>> Template("$greeting, $who!").substitute(greeting="Hello", who="World")
'Hello, World!'

In this example, you form the placeholders using valid Python identifiers, greeting and who. As the second rule states, the first non-identifier character terminates the placeholder, so that’s the case of the comma after $greeting and the exclamation point after $who.

The third rule applies to situations where you need to substitute a word in a string, and the characters that follow the identifier are valid for building identifiers. In this situation, Python won’t know where the identifier finishes.

For example, say that you need a template that allows you to display an amount of money in USD. In this situation, you can do something like the following:

Python
>>> Template("${amount}USD").substitute(amount="100")
'100USD'

>>> Template("$amountUSD").substitute(amount="100")
Traceback (most recent call last):
    ...
KeyError: 'amountUSD'

Because USD are all characters that you can use in a valid Python identifier, you need to use the ${identifier} placeholder style. Otherwise, you get a KeyError.

Finally, the template string that you supply to the Template() constructor is stored in the .template attribute. This allows you to modify the template dynamically:

Python
>>> greeting = Template("Hello, $name! Today is $day.")
>>> greeting.substitute(name="John", day="Friday")
'Hello, John! Today is Friday.'

>>> greeting.template = "Hello, $name! Welcome!"
>>> greeting.substitute(name="John")
'Hello, John! Welcome!'

You can modify the .template attribute whenever you need to. However, it’s best to create new instances of Template for every different template string in your code. This way, you’ll prevent subtle bugs or even breaking your templates.

Substituting Values With .substitute()

Up to this point, you’ve used the .substitute() method with keyword arguments to interpolate values in your string templates. You can also use the methods with dictionaries:

Python
>>> from string import Template

>>> numbers = {"one": 1, "two": 2, "three": 3}

>>> Template("$one-$two-$three").substitute(**numbers)
'1-2-3'

Again, when you use a dictionary as an argument to substitute(), you need to use the dictionary unpacking operator (**). This operator will unpack the key-value pairs into keyword arguments that will be inserted into the appropriate placeholders in the template string.

Note that the placeholder names have to match the dictionary keys. If a placeholder doesn’t match any key or if the number of keys doesn’t match the number of placeholders, then you get an error:

Python
>>> numbers = {"one": 1, "two": 2}

>>> Template("$one-$two-$three").substitute(**numbers)
Traceback (most recent call last):
    ...
KeyError: 'three'

If you call .substitute() with a dictionary whose keys don’t match all the placeholders in the template string, then you get a KeyError.

Substituting Values With .safe_substitute()

Template has another method that you can use to interpolate the values into the string template. The method is called .safe_substitute() and works similarly to .substitute(). However, when you use an incomplete or non-matching set of arguments, the method doesn’t raise a KeyError:

Python
>>> from string import Template

>>> numbers = {"one": 1, "two": 2}

>>> Template("$one-$two-$three").safe_substitute(**numbers)
'1-2-$three'

In this code snippet, you call .safe_substitute() using a dictionary whose keys don’t match all the existing placeholders. Rather than getting a KeyError exception, you get a string that literally shows the missing placeholder. It can be useful in identifying the missing values inside a view of an HTML page to render.

The .safe_substitute() method might be an advantage of using Template over the .format() method:

Python
>>> "{one}-{two}-{three}".format(**numbers)
Traceback (most recent call last):
    ...
KeyError: 'three'

The .format() method doesn’t have a safe way to perform the interpolation when you use an incomplete or non-matching set of arguments.

Conclusion

You’ve learned how to do string interpolation and create new strings by inserting objects into a string template. Now you know that Python has several tools for string interpolation. These tools include f-strings, the str.format() method, and the modulo operator (%).

You also learned about the Template class, which you can also use for string interpolation. This class comes in a standard-library module called string.

In this tutorial, you’ve:

  • Learned how to use f-strings for eager string interpolation
  • Performed lazy string interpolation with str.format()
  • Used the modulo operator (%) for string interpolation
  • Learned when to use f-strings or str.format() for interpolation
  • Done string interpolation with the string.Template class

Now, you have the required skills to start creating strings using different interpolation tools. Which tool you use will depend on your specific use case.

Take the Quiz: Test your knowledge with our interactive “String Interpolation in Python: Exploring Available Tools” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

String Interpolation in Python: Exploring Available Tools

Take this quiz to test your understanding of the available tools for string interpolation in Python, as well as their strengths and weaknesses. These tools include f-strings, the .format() method, and the modulo operator.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Leodanis Pozo Ramos

Leodanis is an industrial engineer who loves Python and software development. He's a self-taught Python developer with 6+ years of experience. He's an avid technical writer with a growing number of articles published on Real Python and other sites.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!