Перейти к основному содержимому

Форматирование строк с помощью f-строк

В Python 3.6 добавился новый вариант форматирования строк - f-строки или интерполяция строк. F-строки позволяют не только подставлять какие-то значения в шаблон, но и позволяют выполнять вызовы функций, методов и т.п.

Во многих ситуациях f-строки удобней и проще использовать, чем format, кроме того, f-строки работают быстрее, чем format и другие методы форматирования строк.

Синтаксис

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

ip = '10.1.1.1'
mask = 24

f"IP: {ip}, mask: {mask}"
# 'IP: 10.1.1.1, mask: 24'

Аналогичный результат с format можно получить так: "IP: {ip}, mask: {mask}".format(ip=ip, mask=mask).

Очень важное отличие f-строк от format: f-строки это выражение, которое выполняется, а не просто строка. То есть, в случае с ipython, как только мы написали выражение и нажали Enter, оно выполнилось и вместо выражений {ip} и {mask} подставились значения переменных.

Поэтому, например, нельзя сначала написать шаблон, а затем определить переменные, которые используются в шаблоне:


f"IP: {ip}, mask: {mask}"
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-1-e6f8e01ac9c4> in <module>()
----> 1 f"IP: {ip}, mask: {mask}"

NameError: name 'ip' is not defined

Кроме подстановки значений переменных, в фигурных скобках можно писать выражения:

octets = ['10', '1', '1', '1']
mask = 24

print(f"IP: {'.'.join(octets)}, mask: {mask}")
# 'IP: 10.1.1.1, mask: 24'

После двоеточия в f-строках можно указывать те же значения, что и при использовании format:

oct1, oct2, oct3, oct4 = [10, 1, 1, 1]

print(f'''
IP address:
{oct1:<8} {oct2:<8} {oct3:<8} {oct4:<8}
{oct1:08b} {oct2:08b} {oct3:08b} {oct4:08b}''')

# IP address:
# 10 1 1 1
# 00001010 00000001 00000001 00000001

Особенности использования f-строк

При использовании f-строк нельзя сначала создать шаблон, а затем его использовать, как при использовании format.

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

ip = '10.1.1.1'
mask = 24

print(f"IP: {ip}, mask: {mask}")
# IP: 10.1.1.1, mask: 24

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

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

ip_list = ['10.1.1.1/24', '10.2.2.2/24', '10.3.3.3/24']

for ip_address in ip_list:
ip, mask = ip_address.split('/')
print(f"IP: {ip}, mask: {mask}")

# IP: 10.1.1.1, mask: 24
# IP: 10.2.2.2, mask: 24
# IP: 10.3.3.3, mask: 24

Примеры f-строк

Базовая подстановка переменных:

intf_type = 'Gi'
intf_name = '0/3'

print(f'interface {intf_type}/{intf_name}')
# 'interface Gi0/3'

Выравнивание столбцами:

topology = [['sw1', 'Gi0/1', 'r1', 'Gi0/2'],
['sw1', 'Gi0/2', 'r2', 'Gi0/1'],
['sw1', 'Gi0/3', 'r3', 'Gi0/0'],
['sw1', 'Gi0/5', 'sw4', 'Gi0/2']]


for connection in topology:
l_device, l_port, r_device, r_port = connection
print(f'{l_device:10} {l_port:7} {r_device:10} {r_port:7}')

# sw1 Gi0/1 r1 Gi0/2
# sw1 Gi0/2 r2 Gi0/1
# sw1 Gi0/3 r3 Gi0/0
# sw1 Gi0/5 sw4 Gi0/2

Ширина столбцов может быть указана через переменную:

topology = [['sw1', 'Gi0/1', 'r1', 'Gi0/2'],
['sw1', 'Gi0/2', 'r2', 'Gi0/1'],
['sw1', 'Gi0/3', 'r3', 'Gi0/0'],
['sw1', 'Gi0/5', 'sw4', 'Gi0/2']]


width = 10

for connection in topology:
l_device, l_port, r_device, r_port = connection

print(f'{l_device:{width}} {l_port:{width}} {r_device:{width}} {r_port:{width}}')

# sw1 Gi0/1 r1 Gi0/2
# sw1 Gi0/2 r2 Gi0/1
# sw1 Gi0/3 r3 Gi0/0
# sw1 Gi0/5 sw4 Gi0/2

Работа со словарями

session_stats = {'done': 10, 'todo': 5}

if session_stats['todo']:
print(f"Pomodoros done: {session_stats['done']}, TODO: {session_stats['todo']}")
else:
print(f"Good job! All {session_stats['done']} pomodoros done!")

# Pomodoros done: 10, TODO: 5

Вызов функции len внутри f-строки:

topology = [['sw1', 'Gi0/1', 'r1', 'Gi0/2'],
['sw1', 'Gi0/2', 'r2', 'Gi0/1'],
['sw1', 'Gi0/3', 'r3', 'Gi0/0'],
['sw1', 'Gi0/5', 'sw4', 'Gi0/2']]


print(f'Количество подключений в топологии: {len(topology)}')
Количество подключений в топологии: 4

Вызов метода upper внутри f-строки:

name = 'python'

print(f'Zen of {name.upper()}')
# Zen of PYTHON

Конвертация чисел в двоичный формат:

ip = '10.1.1.1'

oct1, oct2, oct3, oct4 = ip.split('.')

print(f'{int(oct1):08b} {int(oct2):08b} {int(oct3):08b} {int(oct4):08b}')
# 00001010 00000001 00000001 00000001

Округления

num = 2.3123

print(f'{num:.1f}') # Limit to 1 decimal
print(f'{num:.2f}') # Limit to 2 decimals

# 2.3
# 2.31

Что использовать format или f-строки

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

ip = [10, 1, 1, 1]

oct1, oct2, oct3, oct4 = ip
print(f'{oct1:08b} {oct2:08b} {oct3:08b} {oct4:08b}')

# 00001010 00000001 00000001 00000001

template = "{:08b} "*4

template.format(oct1, oct2, oct3, oct4)
# '00001010 00000001 00000001 00000001 '

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

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

def show_me_ip(ip, mask):
return f"IP: {ip}, mask: {mask}"


show_me_ip('10.1.1.1', 24)
# 'IP: 10.1.1.1, mask: 24'

show_me_ip('192.16.10.192', 28)
# 'IP: 192.16.10.192, mask: 28'