Зачем вам f-strings: особенности, о которых вы не слышали

автор рубрика
Зачем вам f-strings: особенности, о которых вы не слышали

F-strings появились в Python 3.6 и стали очень удобным и полезным инструментов для форматированного вывода. Но они обладают некоторыми особенностями, о которых не все знают. Поэтому в этой статье остановимся на них

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

Подготовка данных для Data Mining на Python

Код курса
DPREP
Ближайшая дата курса
14 июня, 2022
Длительность обучения
32 ак.часов
Стоимость обучения
60 000 руб.

Для нужного представления даты в виде строки согласно шаблону обычно используется функция strftime (см. тут). Причем есть еще функция strptime, и их нужно не путать. Но знали ли, что это можно сделать обычным f-strings? Пример кода на Python:

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

“По-честному” то же самое можно было бы сделать вот так:

print(today.strftime("%Y-%m-%d"))

Узнать, какие шаблоны для даты и времени можно через man-страницу: man strftime.

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

Начиная с Python 3.8, появилась возможность выводить имена переменных вместе с их значением через f-strings:

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=}, {y=}")
x=10, y=25

print(f"{x = :.3f}")
# x = 10.000

Эта особенность называется отладкой (debugging). Она позволяет писать немного меньше кода.

Магические методы __repr__ и __str__

Если вы передадите в функцию print экземпляр класса, то только узнаете имя класса, которому он принадлежит. Чтобы сделать форматированный вывод более информативным, используются магические методы __repr__ и __str__. Обычно __repr__ используется для отладочного вывода, а __str__ — для пользовательского. Поэтому чтобы вывести именно результат __repr__, в f-string добавляется флаг !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-string в Python. Посмотрите на скорость выполнения обычного вывода и с использованием f-string:

# 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 наносек за цикл - Быстро!
print(x + " " + y)  # 43.5 наносек за цикл
print(" ".join((x, y)))  # 58.1 наносек за цикл
print("%s %s" % (x, y))  # 103 наносек за цикл
print("{} {}".format(x, y))  # 141 наносек за цикл
print(Template("$x $y").substitute(x=x, y=y))  # 1.24 микросекунд за цикл - Медленно!

Все эти форматированные выводы были оценены с помощью timeit. И можно увидеть, что f-strings самые быстрые в этом списке. Уже ради скорости их можно использовать в своем коде.

Удобство использования

F-strings поддерживают специальный мини-язык для форматированного вывода, который дает возможность выводить так, как считается нужным.

text = "hello world"

# Center text:
print(f"{text:^15}")
# '  hello world  '

number = 1234567890
# Set separator
print(f"{number:,}")
# 1,234,567,890

number = 123
# Add leading zeros
print(f"{number:08}")
# 00000123

Вложенные f-strings в Python

Интересно, что f-strings можно вкладывать друг в друга:

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

Вложенные f-strings можно использовать еще для переменных в качестве модификаторов:

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

Форматированный вывод с условием

Вместе с вложенными f-strings можно добавлять условные конструкции:

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

x = 10
y = 5
print(f"{f'{x = }' if x > 0 else f'{y = }'}")
# x = 10

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

Форматированный вывод лямбда-выражений

Еще в копилку менее читаемого кода является форматированный вывод лямбда-выражений:

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

Скобки вокруг лямбда-выражения обязательны, иначе двоеточие будет считаться модификатором.

Подготовка данных для Data Mining на Python

Код курса
DPREP
Ближайшая дата курса
14 июня, 2022
Длительность обучения
32 ак.часов
Стоимость обучения
60 000 руб.

Еще больше подробностей о особенностях программирования на Python вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве:

Комментировать