Skip to content

Python f-string #
Find similar titles

Structured data

Category
Programming

Overview #

Beginning with version 3.6, Python has a new type of string - f-string, which literally means "formatted string". These string improves the readability of the code, and also works faster than other methods of formatting. F-strings are specified using the literal "f" before the quotation marks.

>>> "simple string"
>>> f"f-string"

Methods #

Concatenation #

A rough way of formatting, in which we simply glue several lines using the addition operation:

>>> name = "John Doe"
>>> age = 25
>>> print("My name is " + name + ". I am " + str(age) + " years old.")
My name is John Doe. I am 25 years old.

% format #

The most popular way that has passed in Python from the C language. You can pass values to a string through lists and tuples, as well as using a dictionary. In the second case, the values are placed not by position, but in accordance with the names.

>>> name = "John Doe"
>>> age = 25
>>> print("My name is %s. I am %d years old." % (name, age))
My name is John Doe. I am 25 years old.
>>> print("My name is %(name)s. I am %(age)d years old." % {"name": name, "age": age})
My name is John Doe. I am 25 years old.

Template-string #

This method appeared in Python 2.4, as a replacement for% formatting (PEP 292), but it never became popular. Supports the transfer of values by name and uses $ -syntax as in PHP.

>>> from string import Template
>>> name = "John Doe"
>>> age = 25
>>> s = Template('My name is $name. I am $age years old.')
>>> print(s.substitute(name=name, age=age))
My name is John Doe. I am 25 years old.

Formatting with format() method #

This method appeared in Python 3 as a replacement for % formatting. It also supports passing values by position and by name.

>>> name = "John Doe"
>>> age = 25
>>> print("My name is {}. I am {} years old.".format(name, age)
My name is John Doe. I am 25 years old.
>>> print("My name is {name}. I am {age} years old.".format(age=age, name=name)
Мy name is John Doe. I am 25 years old.

f-string #

The formatting that appeared in Python 3.6 (PEP 498). This method is similar to formatting using the format () method, but is more flexible, readable, and faster.

>>> name = "John Doe"
>>> age = 25
>>> print(f"My name is {name}. I am {age} years old.")
Мy name is John Doe. I am 25 years old.

Dive into f-string #

F-string does a very simple thing - it takes the values of the variables that are in the current scope, and substitutes them in a string. In the string itself, you only need to specify the name of this variable in curly brackets.

>>> name = "John Doe"
>>> age = 25
>>> print(f"My name is {name}. I am {age} years old.")
Мy name is John Doe. I am 25 years old.

F-string also supports extended formatting of numbers:

>>> from math import pi
>>> print(f"Value of number pi: {pi:.2f}")
Value of number pi: 3.14

We can also format the date without calling the strftime() method:

>>> from datetime import datetime as dt
>>> now = dt.now()
>>> print(f"Current time is {now:%Y.%m.%d %H:%M}")
Current time is 2017.04.26 08:46

They support basic arithmetic operations. Yes, inside the string:

>>> x = 10
>>> y = 5
>>> print(f"{x} x {y} / 2 = {x * y / 2}")
10 x 5 / 2 = 25.0

It also allows us to access the values of lists by index:

>>> planets = ["Mercury", "Venus", "Earth", "Mars"]
>>> print(f"We live on the planet {planets[2]}")
We live on planet Earth

And also to the elements of the dictionary by the key:

>>> planet = {"name": "Earth", "radius": 6378000}
>>> print(f"Planet {planet ['name']}. Radius {planet ['radius'] / 1000} km.")
Planet Earth. Radius 6378.0 km.

And we can use both string and numeric keys. Just like in the usual Python code:

>>> digits = {0: 'zero', 'one': 'one'}
>>> print(f"0 - {digits [0]}, 1 - {digits ['one']}")
0 - zero, 1 - one

We can call the object methods in f-rows:

>>> name = "John Doe"
>>> print(f"Name: {name.upper()}")
Name: JOHN DOE

And also call functions:

>>> print(f"13 / 3 = {round(13/3)}")
13 / 3 = 4

F-string is very flexible and powerful tool for creating a wide variety of templates.

With all the possibilities of f-string you can read in PEP498 1.

Performance #

F-string are not only flexible, but also fast. And to compare the performance of different approaches to formatting, I prepared two templates:

  • Simple, in which you need to insert only two values: a string and a number;

  • Complex, the data for which are collected from different variables, and inside it converts the date, the real number, and rounding.

I wrote 5 functions, each of which returns a string formatted in one way, and after running each function many times.

And the final output string of simple benchmark is following:

Hi. My name is Alice. I am 10 years old.

And, this is code of simple benchmark:

import time
from random import shuffle, choice
from string import Template


def old_style(name, age):
    return "Hi. My name is %s. I am %d years old." % (name, age)


def concat_style(name, age):
    return "Hi. My name is " + name + ". I am " + str(age) + " years old."


def new_style(name, age):
    return "Hi. My name is {}. I am {} years old.".format(name, age)


def template_style(name, age):
    return Template("Hi. My name is $name. I am $age years old.").substitute(name=name, age=age)


def f_style(name, age):
    return "Hi. My name is {name}. I am {age} years old."


if __name__ == "__main__":
    styles = [concat_style, old_style, template_style, new_style, f_style]
    shuffle(styles)
    for style in styles:
        t0 = time.time()
        for i in range(300000):
            style(choice(["Alice", "Bob", "Eve", "Oscar"]), choice([10, 20, 55, 76]))
        t1 = time.time()
        total = t1 - t0
        print(style.__name__, total)

This is code of complex benchmark:

import time
from random import shuffle
from string import Template
from datetime import datetime as dt
year = 365.25

planet = {
    "name": "Earth",
    "radius": 6378000,
    "mass": 5.9726*10**24
}

now = dt.now()


def old_style():
    return "Today is %s.\n" \
            "We live in a planet %s. Radius is %d km., weight is %.3e.\n" \
            "Period of revolution of Earth around Sun is %d days." % (
    now.strftime("%d.%m.%Y"),
    planet["name"],
    planet["radius"] / 1000,
    planet["mass"],
    round(year)
)


def concat_style():
    return "Today is " + now.strftime("%d.%m.%Y") + ".\n" \
           "We live in a planet " + planet["name"] + ". Radius is " + str(int(planet["radius"] / 1000)) + " km., weight is " + str(planet["mass"]) + "\n" \
           "Period of revolution of Earth around Sun is " + str(round(year)) + " days."


def new_style():
    return "Today is {now:%d.%m.%Y}.\n" \
            "We live in a planet {name}. Radius is {radius} km., weight is {mass:.3e}\n" \
            "Period of revolution of Earth around Sun is {year} days.".format(
    now=now, name=planet["name"], radius=int(planet["radius"] / 1000), mass=planet["mass"], year=round(year))


def template_style():
    return Template("Today is $now.\n"
            "We live in a planet $name. Radius is $radius km., weight is $mass\n"
            "Period of revolution of Earth around Sun is $year days.").substitute(
    now=now.strftime("%d.%m.%Y"), name=planet["name"], radius=int(planet["radius"] / 1000),
    mass=planet["mass"], year=round(year))


def f_style():
    return f"Today is {now:%d.%m.%Y}.\n" \
           f"We live in a planet {planet['name']}. Radius is {int(planet['radius'] / 1000)} km., weight is {planet['mass']:.3e}\n" \
           f"Period of revolution of Earth around Sun is {round(year)} days."


if __name__ == "__main__":
    styles = [concat_style, old_style, template_style, new_style, f_style]
    shuffle(styles)
    for style in styles:
        t0 = time.time()
        for i in range(100000):
            style()
        t1 = time.time()
        total = t1 - t0
        print(style.__name__, total)

After a short test I got the following results:

Image

On simple examples, f-string shows the best results. 25% faster than the % format and format() method.

Image

On complex examples, f-string behaves as well as % format. (The difference in favor of % format does not exceed the error value). But with respect to format() method, f-string is 27% faster.

Conclusion #

If you are using Python 3.6 or later, it's time to use f-strings - they are flexible and fast.

References #

  1. https://www.python.org/dev/peps/pep-0498/
  2. https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498
0.0.1_20140628_0