Перейти до змісту

Форматування рядків за допомогою f-рядків

Python 3.6 додав новий варіант форматування рядків - f-рядки або інтерполяція рядків.

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

У багатьох ситуаціях f-рядки зручніше і простіше використовувати, ніж format.

Синтаксис

F-рядки – це літерал рядка з літерою f перед ним. Усередині f-рядка в парі фігурних дужок вказуються імена змінних, які треба підставити:

In [1]: ip = '10.1.1.1'

In [2]: mask = 24

In [3]: f"IP: {ip}, mask: {mask}"
Out[3]: 'IP: 10.1.1.1, mask: 24'

Аналогічний результат format можна отримати так:

"IP: {ip}, mask: {mask}".format(ip=ip, mask=mask)

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

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

In [1]: 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

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

In [1]: octets = ['10', '1', '1', '1']

In [2]: mask = 24

In [3]: f"IP: {'.'.join(octets)}, mask: {mask}"
Out[3]: '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

In [9]: print(f"IP: {ip}, mask: {mask}")
IP: 10.1.1.1, mask: 24

Якщо необхідно підставити інші значення, треба створити нові змінні (з тими самими іменами) і знову написати f-рядок:

ip = '10.2.2.2'
mask = 24

In [13]: print(f"IP: {ip}, mask: {mask}")
IP: 10.2.2.2, mask: 24

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

In [1]: ip_list = ['10.1.1.1/24', '10.2.2.2/24', '10.3.3.3/24']

In [2]: 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-рядків

Базова підстановка змінних:

In [1]: intf_type = 'Gi'

In [2]: intf_name = '0/3'

In [3]: f'interface {intf_type}/{intf_name}'
Out[3]: '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']]


In [7]: 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

In [8]: 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}

In [2]: 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']]


In [3]: print(f'Кількість підключень в топології: {len(topology)}')
Кількість підключень в топології: 4

Виклик методу upper всередині f-рядка:

In [1]: name = 'python'

In [2]: print(f'Zen of {name.upper()}')
Zen of PYTHON

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

In [7]: ip = '10.1.1.1'

In [8]: oct1, oct2, oct3, oct4 = ip.split('.')

In [9]: print(f'{int(oct1):08b} {int(oct2):08b} {int(oct3):08b} {int(oct4):08b}')
00001010 00000001 00000001 00000001

Що використовувати format або f-рядки

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

In [6]: ip = [10, 1, 1, 1]

In [7]: oct1, oct2, oct3, oct4 = ip
   ...: print(f'{oct1:08b} {oct2:08b} {oct3:08b} {oct4:08b}')
   ...:
00001010 00000001 00000001 00000001

In [8]: template = "{:08b} "*4

In [9]: template.format(oct1, oct2, oct3, oct4)
Out[9]: '00001010 00000001 00000001 00000001 '

Ще одна ситуація, коли format, як правило, зручніше використовувати: необхідність використовувати в скрипті той самий шаблон багато разів. F-рядок виконається перший раз і підставить поточні значення змінних та для використання шаблону ще раз, його треба заново писати. Це означає, що в скрипті будуть копії одного і того ж рядка. У той же час, format дозволяє створити шаблон в одному місці і потім використовувати його повторно, підставляючи змінні при необхідності.

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

In [1]: def show_me_ip(ip, mask):
   ...:     return f"IP: {ip}, mask: {mask}"
   ...:

In [2]: show_me_ip('10.1.1.1', 24)
Out[2]: 'IP: 10.1.1.1, mask: 24'

In [3]: show_me_ip('192.16.10.192', 28)
Out[3]: 'IP: 192.16.10.192, mask: 28'