Не смотря на один из принципов Python, гласящий: «Должен существовать один — и, желательно, только один – очевидный способ сделать что-то», в нашем любимом языке есть аж четыре способа отформатировать строку. Так сложилось исторически.
Это второй урок цикла, посвящённого форматированию строк. В него входят:
В данном уроке мы познакомимся с f-Строками.
В Python 3.6 появился новый способ форматирования строки – интерполяция строк (чаще называют «форматированные строковые литералы» или «f-Строки»).
f-Строки предоставляют возможность использовать выражения Питона внутри строк. Происходит это следующим образом: каждое выражение вычисляется, затем все части f-Строки отдельно преобразуется в обычные строки, затем все части конкатенируются (склеиваются в одну). Форматированные строковые литералы так же, как и описанный в предыдущем уроке метод строки .format(), поддерживают форматирование, описанное в атрибуте __format__ . Узнать f-Строки Вы можете по символу «f» в начале строки перед открывающей кавычкой.
Простой синтаксис
Для того, чтобы использовать форматированные строковые литералы, просто поставьте символ «f» перед строкой и укажите в ней в фигурных скобках выражения Питона. В простейшем случае это имена переменных. Python, как всегда, радует своей лаконичностью! Пример:
язык = 'Python'
проценты = 100
print(f'-Самый лучший язык - {язык}.\n'
f'-Точно?\n'
f'-{проценты}%!')
# Вывод:
-Самый лучший язык - Python.
-Точно?
-100%!
Как видите, форматированные строковые литералы могут быть многострочными, но символ «f» должен быть в начале каждой строки.
Если Вы используете заглавную «F» — интерпретатор всё равно Вас поймёт:
print(F'2 + 2 = {2 + 2}')
# Вывод:
2 + 2 = 4
Надеюсь, Вы в таком же восторге от этого синтаксиса, как и я!
Произвольные выражения
Обратите внимание, в предыдущем примере мы использовали в строке выражение {2 + 2}. Оно сперва вычисляется, а затем преобразуется в строку. Это, конечно, открывает большие возможности.
my_var = input('Введи и я умножу ')
print(f'{(my_var + " ") * 2}')
# Вывод:
Введи и я умножу Hello!
Hello! Hello!
Также вы можете вызывать функции. Вот упрощённое решение задачи из урока про input():
первое_число = int(input('Введите число '))
первый_знак = input('Введите знак ')
второе_число = int(input('Введите число '))
второй_знак = input('Введите знак ')
третье_число = int(input('Введите число '))
def calculate(first_digit, operator, second_digit):
if operator == '+':
return first_digit + second_digit
if operator == '-':
return first_digit - second_digit
if operator == '*':
return first_digit * second_digit
if operator == '/':
try:
return first_digit / second_digit
except ZeroDivisionError:
print('Делить на ноль нельзя - это опасно!')
print(f'{calculate(calculate(первое_число, первый_знак, второе_число), второй_знак, третье_число)}')
# Вывод:
Введите число 2
Введите знак +
Введите число 2
Введите знак /
Введите число 3
1.3333333333333333
Обратите внимание, это упрощённое решение. Здесь нет проверки на очерёдность выполнения арифметических действий, проверки на то, что введено цело число и т. д. Разработку этих деталей оставляю Вам для самостоятельной работы.
Также вы можете вызывать методы объектов:
my_var = input('Введи и я умножу ')
print(f'{(my_var + " ").split() * 2}')
# Вывод:
Введи и я разделю Hello!
['Hello!', 'Hello!']
Вы даже можете использовать объекты, созданные из классов при помощи f-строки.
class Foo:
pass
print(f'{Foo()} is instance of {Foo}')
# Вывод:
<__main__.Foo object at 0x000002425BE70E50> is instance of <class '__main__.Foo'>
Давайте разберёмся, что здесь происходит. Когда Вы пытаетесь распечатать объект, интерпретатор обращается к его методам __str__() и __repr__(). Мы можем переопределить один из них и получить красивый вывод:
class Foo:
def __str__(self):
return 'Foo object'
print(f'{Foo()} is instance of {Foo}')
# Вывод:
Foo object is instance of <class '__main__.Foo'>
Многострочные F-Strings
Как уже говорилось, у вас могут быть многострочные f-strings, но, если Вы забудете поставить «f» в начале каждой строки, Вы получите совсем не то, чего ожидали:
print(f'1 + 1 = {1 + 1}\n'
'2 + 2 = {2 + 2}')
# Вывод:
1 + 1 = 2
2 + 2 = {2 + 2}
Но у Вас есть возможность использовать «f» перед тройными кавычками, тогда всё сработает:
print(f"""1 + 1 = {1 + 1}
2 + 2 = {2 + 2}""")
# Вывод:
1 + 1 = 2
2 + 2 = 4
Скорость
Одной из целей создания форматированных строковых литералов являлось увеличение скорости по сравнению с, уже существовавшим на тот момент, методом строки .format(). И это цель была достигнута. Давайте проверим это утверждение:
import timeit
var_1 = timeit.timeit("""
язык = 'Python'
проценты = 100
'-Самый лучший язык - %s. -Точно? -%d' % (язык, проценты) + '%!'
""", number=10000)
var_2 = timeit.timeit("""
язык = 'Python'
проценты = 100
'-Самый лучший язык - {}. -Точно? {}'.format(язык, проценты) + '%!'
""", number=10000)
var_3 = timeit.timeit("""
язык = 'Python'
проценты = 100
f'-Самый лучший язык - {язык}. -Точно? {проценты}' + '%!'
""", number=10000)
var_4 = timeit.timeit("""
язык = 'Python'
проценты = 100
'-Самый лучший язык - ' + язык + '. -Точно? ' + str(проценты) + '%!'
""", number=10000)
time_sum = var_1 + var_2 + var_3 + var_4
var_1, var_2, var_3, var_4 = [round(i / time_sum * 100) for i in (var_1, var_2, var_3, var_4)]
print('Строковый оператор форматирования: ', var_1, '%', sep='')
print('Метод .format(): ', var_2, '%', sep='')
print('Форматированный строковый литерал: ', var_3, '%', sep='')
print('Конкатенация: ', var_4, '%', sep='')
# Вывод:
Строковый оператор форматирования: 26%
Метод .format(): 31%
Форматированный строковый литерал: 17%
Конкатенация: 27%
Конкатенация здесь приведена просто для наглядности. Как видите, «f»-Строки выигрывают в скорости.
При добавлении циклов вызова, разница остаётся такой же:
import timeit
var_2 = timeit.timeit("""
язык = 'Python'
проценты = 100
'-Самый лучший язык - {}. -Точно? {}'.format(язык, проценты) + '%!'
""", number=10000000)
var_3 = timeit.timeit("""
язык = 'Python'
проценты = 100
f'-Самый лучший язык - {язык}. -Точно? {проценты}' + '%!'
""", number=10000000)
time_sum = var_2 + var_3
var_2, var_3 = [round(i / time_sum * 100) for i in (var_2, var_3)]
print('Метод .format(): ', var_2, '%', sep='')
print('Форматированный строковый литерал: ', var_3, '%', sep='')
# Вывод:
Метод .format(): 65%
Форматированный строковый литерал: 35%
Python F-Строки: Детали
На данный момент мы узнали почему f-строки так хороши, так что вам уже может быть интересно их попробовать в работе. Рассмотрим несколько деталей, которые нужно учитывать:
Кавычки
Вы можете использовать несколько видов кавычек внутри выражений. Удостоверьтесь в том, что вы не применяете один и тот же тип кавычек внутри и снаружи f-строки.
Этот код будет работать:
print(f'{"Python"}')
# Вывод:
Python
И этот тоже:
print(f"{'Python'}")
# Вывод:
Python
Вы также можете применить тройные кавычки:
print(f"""{"Python"}""")
# Вывод:
Python
Если вам понадобиться использовать один и тот же тип кавычек внутри и снаружи строки, вам может помочь \:
print(f'\'Lorem\' \'ipsum\' \'dolor\' \'sit\' \'amet\'')
# Вывод:
'Lorem' 'ipsum' 'dolor' 'sit' 'amet'
Словари
Говоря о кавычках, будьте внимательны при работе со словарями Python. Вы можете вставить значение словаря по его ключу, но ключ и сама строка должны быть обрамлены разными кавычками:
arg_dict = {'язык': 'Python', 'проценты': 100}
print(f"-Самый лучший язык - {arg_dict['язык']}.\n"
f"-Точно?\n"
f"-{arg_dict['проценты']}%!")
# Вывод:
-Самый лучший язык - Python.
-Точно?
-100%!
Обратите внимание на количество возможных проблем, если допустить ошибку в синтаксисе SyntaxError:
arg_dict = {'язык': 'Python', 'проценты': 100}
print(f'-Самый лучший язык - {arg_dict['язык']}.\n'
f'-Точно?\n'
f'-{arg_dict['проценты']}%!')
# Вывод:
File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_1.py", line 3
print(f'-Самый лучший язык - {arg_dict['язык']}.\n'
^
SyntaxError: f-string: unmatched '['
Process finished with exit code 1
Скобки
Чтобы скобки появились в строке, вам нужно использовать двойные фигурные скобки:
print(f'-Самый лучший язык - {{Python}}.\n'
f'-Точно?\n'
f'-{{100}}%!')
# Вывод:
-Самый лучший язык - {Python}.
-Точно?
-{100}%!
Обратите внимание на то, что применение тройных фигурных скобок приведет к тому, что в строке будут только одинарные:
print(f'{{{1}}}')
# Вывод:
{1}
В целом, здесь мы имеем такое же неочевидное поведение, как и у метода строки .format():
print(f'{{{{{{{{1}}}}}}}}')
# Вывод:
{{{{1}}}}
Бэкслеши
Несмотря на то, что бэкслеш можно использовать в строке, у Вас не получится сделать это внутри выражения:
print(f'{"1\n2"}')
# Вывод:
File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_1.py", line 1
print(f'{"1\n2"}')
^
SyntaxError: f-string expression part cannot include a backslash
Process finished with exit code 1
Вы можете проработать это, оценивая выражение заранее и используя результат в f-строк:
temp = "1\n2"
print(f'{temp}')
# Вывод:
1
2
Междустрочные комментарии
Выражения не должны содержать комментарии с применением октоторпа (символ «#»). В противном случае, у вас будет ошибка синтаксиса SyntaxError:
print(f'2 + 2 = {4 # наверно}')
# Вывод:
File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_1.py", line 1
print(f'2 + 2 = {4 # наверно}')
^
SyntaxError: f-string expression part cannot include '#'
Process finished with exit code 1
Форматирование вставляемых выражений
Форматированные строковые литералы, как и метод строки .format() поддерживают форматирование:
print(f'2 + 2 = {2 + 2:010d}')
# Вывод:
2 + 2 = 0000000004
print(f'2 + 2 = {2 + 2:^010d}')
# Вывод:
2 + 2 = 0000400000
print(f'2 + 2 = {"четыре":.3}')
# Вывод:
2 + 2 = чет
заполнитель = '_'
выравнивание = '^'
ширина = 9
язык = 'Питон'
print(f"{язык:{заполнитель}{выравнивание}{ширина}}")
# Вывод:
__Питон__
Подробнее об этом читайте в нашем предыдущем уроке.