Сообщить об ошибке.

Форматированные строки. F-строки в Python

С версии Python 3.14 появилась поддержка символа подчеркивания и запятой в качестве разделителей тысяч для типов представления чисел с плавающей запятой при использовании нового стиля строкового форматирования (с помощью str.format() или f-строк). Примеры смотрите ниже.

"F-строки" обеспечивают краткий, читаемый способ включения значения выражений Python внутри строк. В исходном коде Python форматированная строка или по другому f-string - это буквальная строка с префиксом 'f' или 'F', которая содержит выражения внутри фигурных скобок {}. Выражения заменяются их значениями.

Escape-последовательности декодируются как в обычных строковых литералах, за исключением случаев, когда литерал также помечается как необработанная строка r'string'.

Части строки вне фигурных скобок обрабатываются буквально, за исключением того, что любые двойные фигурные скобки {{ или }} заменяются соответствующей одиночной фигурной скобкой. Одна открывающая фигурная скобка { обозначает поле замены, которое начинается с выражения Python. После выражения может быть поле преобразования, введенное восклицательным знаком !. Спецификатор формата также может быть добавлен, введенный двоеточием :. Поле замены заканчивается закрывающей фигурной скобкой }.

Выражения в форматированных строках рассматриваются как обычные выражения Python, окруженные круглыми скобками.

Ограничения, дополнения и изменения синтаксиса f-string:

  • Пустое выражение недопустимо.

  • Как лямбда-выражения, так и выражения присваивания := должны быть заключены в явные круглые скобки.

  • Каждое выражение вычисляется в контексте, где отображается форматированный строковый литерал, в порядке слева направо.

  • В Python 3.14 Появилась поддержка символа подчеркивания и запятой в качестве разделителей тысяч для типов представления чисел с плавающей точкой при использовании нового стиля строкового форматирования (с помощью str.format() или f-строк).

    x = 1234567.8912345
    
    # С запятыми
    print(f"Вывод: {x:,.2f}")
    # Вывод: 1,234,567.89
    
    # С подчеркиваниями
    print(f"Вывод: {x:_.2f}")
    # Вывод: 1_234_567.89
    
  • До версии Python 3.12 кавычки в выражениях f-string не должны были конфликтовать с кавычками, используемыми во внешнем форматированном строковом литерале. В Python 3.12 это ограничение снято.

  • До версии Python 3.12 выражения (в фигурных скобках) могли содержать переносы только в строках с тройными кавычками, но не могли содержать комментарии. В Python 3.12 ограничение на использование переносов строк и комментариев было снято.

    В Python 3.12, все, что идет после # внутри поля замены, является комментарием (!!! даже закрывающие скобки и кавычки). В этом случае поля замены необходимо закрыть в другой строке.

    >>> a = 2
    >>> f'abc{a # This is a comment }'
    ... + 3}'
    # 'abc5'
    

    А еще в Python 3.12 стало возможным делать такие вещи:

    >>> f"This is the playlist: {", ".join([
    ...     'Take me back to Eden',  # My, my, those eyes like fire
    ...     'Alkaline',              # Not acid nor alkaline
    ...     'Ascensionism'           # Take to the broken skies at last
    ... ])}"
    'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
    
  • С версии Python 3.12 в полях замены стала доступна для использования обратная косая черта и оценивается так же, как и в любом другом контексте. Это означает, что выражения могут содержать символы новой строки '\n' как в одинарных, так и в тройных кавычках.

    a = ["a", "b", "c"]
    print(f"List a contains:\n{"\n".join(a)}")
    # List a contains:
    # a
    # b
    # c
    
  • В Python 3.8 добавлен спецификатор '='. Если указан знак равенства =, то выходные данные будут содержать текст выражения, знак = и вычисленное значение. Пробелы после открывающей скобки '{', внутри выражения и после '=' сохраняются в выходных данных. По умолчанию '=' приводит к предоставлению repr() выражения, если не указан формат. Когда указан формат, по умолчанию используется str() выражения, если не объявлено преобразование '!r'.

    >>> a = 1
    >>> f'{a + 1 = }'
    # 'a + 1 = 2'
    
  • Если указано преобразование (!o или !a или !r), то перед форматированием результат вычисления выражения преобразуется. Преобразование '!s' вызывает str() для результата, '!r' вызывает repr(), а '!a' вызывает ascii(). Затем результат форматируется с помощью протокола str.format(). Спецификатор формата передается в метод __format__() выражения или результата преобразования. Отформатированный результат затем включается в конечное значение всей строки.

  • Спецификация формата Mini-Language совпадает с языком, используемым во встроенной функции format().

  • f-строки нельзя использовать в качестве строк документации, даже если они не содержат выражений.

В качестве положительного побочного эффекта от того, как была реализована новая функциональность в Python 3.12 (путем анализа f-строк с помощью синтаксического анализатора PEG), сообщения об ошибках стали включать её точное местоположение. Например, до версии Python 3.12 следующая f-строка вызывает SyntaxError:

>>> my_string = f"{x z y}" + f"{1 + 1}"
#   File "<stdin>", line 1
#     (x z y)
#      ^^^
# SyntaxError: f-string: invalid syntax. Perhaps you forgot a comma?

Сообщение об ошибке не содержит точного местоположения внутри строки, а также содержит выражение, искусственно заключенное в круглые скобки. В Python 3.12 сообщения об ошибках стали более точными и могут отображать всю строку:

>>> my_string = f"{x z y}" + f"{1 + 1}"
#   File "<stdin>", line 1
#     my_string = f"{x z y}" + f"{1 + 1}"
#                    ^^^
# SyntaxError: invalid syntax. Perhaps you forgot a comma?
Содержание:

Простое форматирование вывода f-строкой.

>>> name = 'Fred'
>>> f'He said his name is {name!r}.'
# 'He said his name is 'Fred'.'

# repr() является эквивалентом '!r'
>>> f'He said his name is {repr(name)}.'
# He said his name is 'Fred'.

Вложение f-строк друг в друга.

>>> import decimal
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f'result: {value:{width}.{precision}}'
'result:      12.35'

Использование кавычек в f-строках.

До версии Python 3.12 кавычки в полях замены/выражениях f-string не должны конфликтовать с кавычками, используемыми во внешнем форматированном строковом литерале:

>>> a = {'x': 120}
>>> f'abc {a['x']} def'
#   File "<stdin>", line 1
#     f'abc {a['x']} def'
#               ^
# SyntaxError: invalid syntax

# Нужно было использовать различные кавычки
f"abc {a['x']} def"
# 'abc 120 def'

В версии Python 3.12 и выше теперь можно делать такие вещи (обратите внимание, что кавычки " не конфликтуют):

>>> songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
>>> f"This is the playlist: {", ".join(songs)}"
# 'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'

Обратите внимание, что до Python 3.12 не было явного ограничения на вложение f-строк, но тот факт, что строковые кавычки не могли быть повторно использованы внутри компонента выражения f-string, делал невозможным произвольное вложение f-строк. Фактически, это самая вложенная f-строка, которую можно написать:

>>> f"""{f'''{f'{f"{1+1}"}'}'''}"""
'2'

Так как в Python 3.12 и выше f-строки могут содержать любое допустимое выражение Python внутри компонентов выражения, то теперь f-строки можно вкладывать произвольное количество раз:

>>> f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"
# '2'

Обратная косая черта и символы юникода в f-строках.

До версии Python 3.12 выражения f-string не могли содержать ни одного символа \. Это также повлияло на управляющие последовательности unicode (такие как \N{snowman}), поскольку они содержат часть \N, которая ранее не могла быть частью компонентов выражения f-строк.

Пример того, что до версии Python 3.12 обратные косые черты не допускались внутри компонента выражения f-строк и вызывали ошибку:

>>> f'newline: {ord("\n")}'
#   File "<stdin>", line 1
# SyntaxError: f-string expression part cannot include a backslash

Чтобы включить значение, в котором используется обратная косая черта, нужно было создать временную переменную.

>>> newline = ord('\n')
>>> f'newline: {newline}'
# 'newline: 10'

В версии Python 3.12 и выше стало возможным определить выражения следующим образом:

>>> songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
>>> print(f"This is the playlist: {"\n".join(songs)}")
# This is the playlist: Take me back to Eden
# Alkaline
# Ascensionism
>>> print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")
# This is the playlist: Take me back to Eden♥Alkaline♥Ascensionism

Фигурные скобки и f-строки.

F-строки используют одинарные фигурные скобки {} для вывода выражений Python. Но как быть, если в итоговой строки необходим вывод самой фигурной скобки? Чтобы в итоговой строке появились фигурные скобки, необходимо использовать двойные скобки, при этом выражение Python вычисляться не будет:

>>> num = 53
>>> f"{{num}}"
# {num}

Обратите внимание, что если нужно вычислить выражение Python и при этом вывести скобки, то нужно использовать тройные скобки:

>>> num = 53
>>> f"{{{num}}}"
# {53}

Другими словами для итогового вывода одной скобки в f-строке, необходимо использовать две скобки + одна, если нужно вычислить выражение. Для вывода 2-х скобок, в f-строке используйте 4 + 1 для вычисления выражения Python, и так далее (в общем четное количество + одна, если нужно вычислить выражение ):

>>> num = 53
>>> f"{{{{{num}}}}}"
# {{53}}

Многострочные выражения в f-строках.

До Python 3.12 сами выражения f-string (в фигурных скобках) должны были быть определены только в одной строке при использовании одинарных кавычек или могли содержать переносы строк только в строках с тройными кавычками, что было не совсем удобно.

>>> f"""This is the playlist: {", ".join([
...     'Take me back to Eden',
...     'Alkaline',
...     'Ascensionism'
... ])}"""
# 'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'

В Python 3.12 это ограничение снято и теперь можно определять f-строки, выражения которых охватывают несколько строк, при этом разрешено повторное использование одинаковых кавычек во внешнем обрамлении f-строки и внутри выражения:

>>>  f"This is the playlist: {", ".join([
...     'Take me back to Eden',
...     'Alkaline',
...     'Ascensionism'
... ])}"
# 'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'

Спецификатор '=' в f-строках:

Для документирования выражений и отладки в Python 3.8 к f-строкам добавлен спецификатор '='. F-строка, такая как f'{expr=}', расширится до текста выражения и знака равенства, а затем представления вычисляемого выражения. Например:

>>> import datetime
>>> user = 'eric_idle'
>>> member_since = datetime.date(1975, 7, 31)
>>> f'{user=} {member_since=}'
# "user='eric_idle' member_since=datetime.date(1975, 7, 31)"

Обычные спецификаторы формата f-string позволяют более точно контролировать способ отображения результата выражения:

>>> import datetime
>>> delta = datetime.date.today() - member_since
>>> f'{user=!s}  {delta.days=:,d}'
# 'user=eric_idle  delta.days=16,075'

Спецификатор = будет отображать все выражение, чтобы можно было показать вычисления:

>>> from math import cos, radians
>>> theta = 30
>>> print(f'{theta=}  {cos(radians(theta))=:.3f}')
theta=30  cos(radians(theta))=0.866

Использование однострочного if/else в f-строке.

>>> a = 10
>>> b = 5
>>> f"{a if a > b else b}"
# `10`
>>> f"{a if a < b else b}"
# `5`

Использование спецификатора формата даты.

Форматирование datetime.date

>>> from datetime import date, datetime
>>> day = date(year=2022, month=9, day=1)
>>> f"{day}"
# 2022-09-01

# формат по умолчанию
>>> f"{day:%Y-%m-%d}"
# 2022-09-01

# использование / в качестве разделителя
>>> f"{day:%Y/%m/%d}" 
# 2022/09/01

# Преобразование месяца в текст (короткая/длинная версия)
>>> f"{day:%Y %b %d}"
# 2022 Sep 01
>>> f"{day:%Y %B %d}"
# 2022 September 01

# Повторное использование одной и той же переменной несколько раз и в разных форматах
>>> f"{day:%b or %B}?"
# Sep or September?
>>> f"{day:%Y %Y %Y}"
# 2022 2022 2022

>>> f"{day:%Y %b %d (%A)}"
# 2022 Sep 01 (Thursday)
>>> f"{day:%y.%m.%d}"
# 22.09.01

Форматирование datetime.datetime

day_and_time = datetime(
    year=2022,
    month=9,
    day=1,
    hour=17,
    minute=30,
    second=45
)
>>> now = datetime.now()

>>> f"{day_and_time}"
# 2022-09-01 17:30:45
>>> f"{now}"  # с микросекундами
# 2023-01-17 10:15:11.858254

# формат по умолчанию:
>>> f"{now:%Y-%m-%d %H:%M:%S.%f}"
# 2023-01-17 10:15:11.858254

>>> type(f"{now:%Y-%m-%d %H:%M:%S.%f}")
# str

# формат по умолчанию с уменьшенной точностью микросекунд
>>> f"{now:%Y-%m-%d %H:%M:%S.%f}"[:22]
# 2023-01-17 10:15:11.85

# Изменяем 24-часовой формат на 12-часовой:
>>> f"24hr: {day_and_time:%Y-%m-%d %H:%M:%S}"
# 24hr: 2022-09-01 17:30:45
>>> f"12hr: {day_and_time:%Y-%m-%d %I:%M:%S}"
# 12hr: 2022-09-01 05:30:45
>>> f"12hr with AM/PM: {day_and_time:%Y-%m-%d %I:%M:%S %p}"
# 12hr with AM/PM: 2022-09-01 05:30:45 PM

Примечание: другие полезные приемы форматирования дат.

day = date(
    year=2018,
    month=9,
    day=17
)

>>> f"The date: {day}"
# The date: 2018-09-17
>>> f"Day of the year: {day: %j}"
# Day of the year:  260
>>> f"Week of the year (Mon): {day: %W}"
# Week of the year (Mon):  38
>>> f"Week of the year (Sun): {day: %U}"
# Week of the year (Sun):  37

Cтроки документации и f-строки.

Форматированные строковые литералы нельзя использовать в качестве строк документации, даже если они не содержат выражений.

>>> def foo():
...     f'Not a docstring'
...
>>> foo.__doc__ is None
True

F-строки не могут использоваться как шаблоны строк str.format().

>>> tmp = "R: {r}\nG: {g}\nB: {b}"
>>> rgb = {'r': 123, 'g': 145, 'b': 255}
>>> print(tmp.format(**rgb))
# R: 123
# G: 145
# B: 255

>>> tmp = "Шаблон строки: {}"
>>> line = "Привет"
>>> tmp.format(line)
# 'Шаблон строки: Привет'

Поддержка спецификации формата Mini-Language.

>>> b = 2999
>>> pi = 3.1415926
>>> f'Pi={pi:.3f}, b={b:*^11.2f}'
# 'Pi=3.142, b=**2999.00**'

>>> print("\n".join(f'{a:{a}<{a}}' for a in range(1, 6)))
# 1
# 22
# 333
# 4444
# 55555

# форматирование процентов
>>> perc = 34 / 87
>>> perc
# 0.39080459770114945
>>> f"Процентное отношение: {perc:%}"
# 'Процентное отношение: 39.080460%'
>>> f"Процентное отношение: {perc:.2%}"
# 'Процентное отношение: 39.08%'

Тип данных float в f-строках.

>>> pi_val = 3.141592

>>> f"Example 1: {pi_val:f}"
# Example 1: 3.141592
>>> f"Example 2: {pi_val:.0f}"
# Example 2: 3
>>> f"Example 3: {pi_val:.1f}"
# Example 3: 3.1
>>> f"Example 4: {pi_val:.3f}"
# Example 4: 3.142

Примечание: возможны вложенные выражения.

>>> float_val = 1.5
>>> precision = 3
>>> f"{float_val:.{precision}f}"
# 1.500

Проценты в f-строках.

>>> val = 0.5
>>> f"Example 1: {val:%}"
# Example 1: 50.000000%
>>> f"Example 2: {val:.0%}"
# Example 2: 50%

>>> val = 1.255
>>> f"Example 1: {val:.0%}"
# Example 1: 125%
>>> f"Example 2: {val:.1%}"
# Example 2: 125.5%

Экспоненциальное обозначение в f-строках.

>>> val = 1.23e3  # 1.23 * 10^3
>>> f"Example 1: {val:e}"
# Example 1: 1.230000e+03
>>> f"Example 2: {val:E}"
# Example 2: 1.230000E+03

# ограниченная точность
>>> val = 1.2345e3
>>> f"{val:.2e}"
# 1.23e+03

# Вывод обычного числа в научной записи
>>> val = 2022
>>> f"{val:.3e}"
# 2.022e+03

Целые числа в f-строках.

val = 1
f"{val:d}"
1

# Печать разделителей тысяч
int_1 = 1000
int_2 = 1000_000_000
f"{int_1:,d}"
1,000
f"{int_2:,d}"
1,000,000,000

Форматирование просто числа в f-строках.

>>> val_int = 1
>>> val_float = 1.234
>>> val_scient = 4.567e2
>>> f"{val_int =: n}"
# val_int = 1
>>> f"{val_float =: n}"
# val_float = 1.234
>>> f"{val_scient =: n}"
# val_scient = 456.7

>>> val_float_1 = 1.234
>>> val_float_2 = 20.234
>>> val_float_3 = 123.456
>>> f"{val_float_1 =: .2n}"  # печатается в виде усеченного плавающего значения
# val_float_1 = 1.2
>>> f"{val_float_2 =: .2n}"  # печатается как int
# val_float_2 = 20
>>> f"{val_float_3 =: .2n}"  # печатается в виде научной записи
# val_float_3 = 1.2e+02

Отступы/выравнивание в f-строках.

>>> greetings = "hello"
# отступы справа и слева
>>> f"{greetings:>10}"
# '     hello'
>>> f"{greetings:<10}"
# 'hello     '

# выравнивание чисел
>>> a = "1"
>>> b = "21"
>>> c = "321"
>>> d = "4321"
>>> print("\n".join((f"{a:>10}", f"{b:>10}", f"{c:>10}", f"{d:>10}")))
#          1
#         21
#        321
#       4321

# выравнивание посередине
>>> hello = "world"
>>> f"{hello:^11}"
# '   world   '
>>> f"{hello:*^11}"
# '***world***'

Использование спецификатора целочисленного формата.

>>> number = 1024
>>> f"{number:#0x}"
'0x400'

Разделители тысяч для чисел с плавающей запятой

В Python 3.14 Появилась поддержка символа подчеркивания и запятой в качестве разделителей тысяч для типов представления чисел с плавающей точкой

x = 1234567.8912345

# С запятыми
print(f"Вывод: {x:,.2f}")
# Вывод: 1,234,567.89

# С подчеркиваниями
print(f"Вывод: {x:_.2f}")
# Вывод: 1_234_567.89

# С отрицательными числами
y = -9876543.210987
print(f"Вывод: {y:,.3f}")
# Вывод: -9,876,543.211

# С нулями после точки
z = 1234.0
print(f"Вывод: {z:,.2f}")
# Вывод: 1,234.00