Найти в Дзене
Хабр Карьера

F-строки в Python мощнее, чем можно подумать

Статья на Хабре Форматированные строковые литералы, которые ещё называют f-строками (f-strings), появились довольно давно, в Python 3.6. Поэтому все знают о том, что это такое, и о том, как ими пользоваться. Правда, f-строки обладают кое-какими полезными возможностями, некоторыми особенностями, о которых кто-нибудь может и не знать. Разберёмся с некоторыми интересными возможностями f-строк, которые могут оказаться очень кстати в повседневной работе Python-программиста. Форматирование даты и времени Форматирование чисел средствами f-строк — это обычное дело. А вы знали, что с их помощью можно ещё форматировать значения, представляющие даты и временные метки? import datetime
today = datetime.datetime.today()
print(f"{today:%Y-%m-%d}")
# 2022-03-11 print(f"{today:%Y}")
# 2022 С помощью f-строк можно форматировать дату и время так, как если бы для этого использовался бы метод datetime.strftime. Это особенно приятно, когда понимаешь, что тут имеется больше возможностей форматирования значен
Оглавление

Статья на Хабре

Форматированные строковые литералы, которые ещё называют f-строками (f-strings), появились довольно давно, в Python 3.6. Поэтому все знают о том, что это такое, и о том, как ими пользоваться. Правда, f-строки обладают кое-какими полезными возможностями, некоторыми особенностями, о которых кто-нибудь может и не знать. Разберёмся с некоторыми интересными возможностями f-строк, которые могут оказаться очень кстати в повседневной работе Python-программиста.

Форматирование даты и времени

Форматирование чисел средствами f-строк — это обычное дело. А вы знали, что с их помощью можно ещё форматировать значения, представляющие даты и временные метки?

import datetime
today = datetime.datetime.today()
print(f"{today:%Y-%m-%d}")
# 2022-03-11 print(f"{today:%Y}")
# 2022

С помощью f-строк можно форматировать дату и время так, как если бы для этого использовался бы метод datetime.strftime. Это особенно приятно, когда понимаешь, что тут имеется больше возможностей форматирования значений, чем те немногие, которые упомянуты в документации. Так, Python-метод strftime поддерживает, кроме прочего, все способы форматирования значений, поддерживаемые его базовой реализацией на C. Эти возможности могут зависеть от платформы, именно поэтому в документации они не упоминаются. Но получается, что этими возможностями по форматированию значений, всё равно, можно воспользоваться. Например, можно применить спецификатор формата %F, являющийся эквивалентом %Y-%m-%d, или спецификатор %T, аналогичный %H:%M:%S. Стоит ещё упомянуть и о спецификаторах формата %x и %X, представляющих собой, соответственно, принятые в используемом языковом стандарте способы представления даты и времени. Использование этих возможностей форматирования значений, конечно, не ограничивается только f-строками. Полный список спецификаторов формата даты и времени можно найти в Linux-справке по strftime.

Имена переменных и отладка

Функционал f-строк сравнительно недавно (начиная с Python 3.8) дополнен возможностями по выводу имён переменных вместе с их значениями:

x = 10 y = 25 print(f"x = {x}, y = {y}")
# x = 10, y = 25 print(f"{x = }, {y = }") # Лучше! (3.8+) # x = 10, y = 25
print(f"{x = :.3f}")
# x = 10.000

Эта возможность называется «отладкой» («debugging»), её можно применять вместе с другими модификаторами. Она, кроме того, сохраняет пробелы, поэтому при обработке конструкций вида f»{x = }» и f»{x=}» получатся разные строки.

Методы __repr__ и __str__

Для формирования строковых представлений экземпляров классов по умолчанию используется метод __str__. Но если вместо этого метода нужно применить метод __repr__ — можно воспользоваться флагом преобразования !r:

class User: def __init__(self, first_name, last_name): self.first_name = first_name
self.last_name = last_name

def __str__(self): return f"{self.first_name} {self.last_name}"
def __repr__(self): return f"User's name is: {self.first_name} {self.last_name}"
user = User("John", "Doe")
print(f"{user}")
# John Doe print(f"{user!r}")
# User's name is: John Doe

Тут, внутри f-строки, можно было бы просто вызвать repr(some_var), но использование флага преобразования — это образец приятного стандартного и краткого решения подобной задачи.

Отличная производительность

За некие мощные возможности чего-либо и за «синтаксический сахар» часто приходится платить производительностью. Но в случае с f-строками это не так:

# python -m timeit -s 'x, y = "Hello", "World"' 'f"{x} {y}"' from string import Template

x, y = "Hello", "World"
print(f"{x} {y}") # 39.6 nsec per loop - Быстро! print(x + " " + y) # 43.5 nsec per loop print(" ".join((x, y))) # 58.1 nsec per loop print("%s %s" % (x, y)) # 103 nsec per loop print("{} {}".format(x, y)) # 141 nsec per loop print(Template("$x $y").substitute(x=x, y=y)) # 1.24 usec per loop - Медленно!

Вышеприведённый код протестирован с помощью модуля timeit (python -m timeit -s 'x, y = «Hello», «World»' 'f»{x} {y}»'). Как видите, f-строки оказались самым быстрым из всех механизмов форматирования данных, которые даёт нам Python. Поэтому, даже если вы предпочитаете пользоваться другими средствами форматирования строк, рассмотреть возможность перехода на f-строки стоит хотя бы ради повышения производительности.

Вся сила мини-языка спецификаций форматирования

F-строки поддерживают мини-язык спецификаций форматирования Python. Поэтому в модификаторы, используемые в f-строках, можно внедрить множество операций форматирования данных:

text = "hello world"
# Центрирование текста: print(f"{text:^15}")
# ' hello world '
number = 1234567890 # Установка разделителя групп разрядов print(f"{number:,}")
# 1,234,567,890
number = 123 # Добавление начальных нулей print(f"{number:08}")
# 00000123

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

Вложенные f-строки

Если чьи-то нужды по форматированию данных не удаётся удовлетворить с помощью простых f-строк, можно прибегнуть к f-строкам, вложенным друг в друга:

number = 254.3463 print(f"{f'${number:.3f}':>10s}")
# ' $254.346'

Одни f-строки можно встраивать в другие f-строки, поступая так ради решения хитрых задач форматирования данных. Например — чтобы, как показано выше, добавить знак доллара к числу с плавающей точкой, выровненному по правому краю.

Вложенные f-строки могут применяться и в тех случаях, когда в спецификаторах формата нужно использовать переменные. Это, кроме прочего, способно улучшить читабельность кода с f-строками:

import decimal
width = 8 precision = 3 value = decimal.Decimal("42.12345")
print(f"output: {value:{width}.{precision}}")
# 'output: 42.1'

Условное форматирование

Взяв за основу предыдущий пример с вложенными f-строками, можно пойти немного дальше и воспользоваться во внутренних f-строках тернарными условными операторами:

import decimal
value = decimal.Decimal("42.12345")
print(f'Result: {value:{"4.3"
if value < 100 else "8.3"}}')
# Result: 42.1 value = decimal.Decimal("142.12345")
print(f'Result: {value:{"4.2"
if value < 100 else "8.3"}}')
# Result: 142

Подобные конструкции могут очень быстро стать нечитабельными. Поэтому, пользуясь ими, возможно, есть смысл разделять их на несколько строк кода.

Лямбда-выражения

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

print(f"{(lambda x: x**2)(3)}")
# 9

Скобки вокруг лямбда-выражения в данном случае обязательны. Это так из-за двоеточия, (:), которое, в противном случае, будет восприниматься системой как часть f-строки.

Итоги

Как видите, f-строки — это, и правда, весьма мощный механизм. Они обладают гораздо большими возможностями, чем думает большинство программистов. Основная часть этих «неизвестных» возможностей, правда, описана в документации по Python. Поэтому рекомендую читать документацию, причём — не только по f-строкам, но и по другим используемым вами модулям или возможностям Python. Углубление в документацию часто помогает обнаружить какие-то очень полезные вещи, которые не найти даже зарывшись в Stack Overflow.

Комментарии на Хабре