Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 12 из 20 Начальный 15 мин 100 XP

format() и старый процент: история форматирования

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

Редакция Питоники

f-строки из уроков 3 и 4 — не первый способ форматирования в Python, а третий. До них был метод format() (версия 2.6), а ещё раньше — оператор процент (времена Python 2). Разбираемся с обоими: не для того, чтобы писать по-старому, а чтобы свободно читать чужой код и старые туториалы — их в сети большинство.

format(): шаблон с фигурными скобками

Идея format: строка-шаблон с фигурными скобками-пустотами и аргументы, которые в них подставляются. Фигурные скобки без номера заполняются по порядку, с номером {0} — по индексу аргумента, с именем {city} — по ключевому слову. Одна и та же заготовка-шаблон переиспользуется с разными данными — в этом format всё ещё полезен сегодня.

три способа указать подстановку
print('{}, {}!'.format('Привет', 'мир'))
print('{1} важнее, чем {0}'.format('объём', 'качество'))

template = 'Доставка в {city}: {days} дн.'
print(template.format(city='Казань', days=3))
print(template.format(city='Сочи', days=5))
Вывод
Привет, мир!
качество важнее, чем объём
Доставка в Казань: 3 дн.
Доставка в Сочи: 5 дн.

Последние две строки — главная фишка format: шаблон сохранён в переменную и переиспользуется. f-строка так не умеет: она подставляет значения в момент написания. Если нужен многократный шаблон — format до сих пор лучший выбор.

Легаси: оператор процент

Самый старый стиль пришёл из языка C: слева строка с метками-процентами, справа после знака % — значения. %s подставляет строку, %d — целое число, %f — дробное. Метки не нумеруются — подстановка строго по порядку.

чтение легаси-кода
name = 'Олег'
score = 87
print('Игрок: %s, очки: %d' % (name, score))
print('Точность: %.1f%%' % 99.5)
Вывод
Игрок: Олег, очки: 87
Точность: 99.5%

Почему f-строки победили

Сравните один и тот же текст тремя способами — и станет видно, за что f-строки забрали победу: значение стоит прямо на месте подстановки, а не где-то после строки.

один текст — три эпохи
city, days = 'Казань', 3

print('Доставка в {}: {} дн.'.format(city, days))
print('Доставка в %s: %d дн.' % (city, days))
print(f'Доставка в {city}: {days} дн.')
Вывод
Доставка в Казань: 3 дн.
Доставка в Казань: 3 дн.
Доставка в Казань: 3 дн.
СтильВидСтатус
f-строкаf'Доставка в {city}'Современный стандарт — пишем так
format()'...{}'.format(x)Нужен переиспользуемый шаблон
Процент %'...%s' % xЛегаси — только читать

Итог урока: новый код пишем f-строками, многократные шаблоны — format(), а процентный стиль распознаём глазами — он ещё десятилетие будет попадаться в старых проектах и статьях.

Как отформатировать строку в Python: три способа

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

одна задача — три способа
city, days = 'Казань', 3
print('Доставка в {}: {} дн.'.format(city, days))
print('Доставка в %s: %d дн.' % (city, days))
print(f'Доставка в {city}: {days} дн.')
Вывод
Доставка в Казань: 3 дн.
Доставка в Казань: 3 дн.
Доставка в Казань: 3 дн.

Три строки дают одинаковый вывод, и это важно: выбор между ними — не про результат, а про поддерживаемость. Смотрите на первую строку: чтобы понять, что подставится в «{}», надо прыгнуть вниз до аргументов. Во второй — ещё дальше, за скобки. В третьей всё видно на месте. Правило простое: если значения известны в момент написания строки — берите f-строку.

format(): подстановка по индексу и по имени

Внутри format() пустые скобки заполняются по порядку. Но можно указать номер — {0}, {1} — и тогда порядок в вызове не важен, а один аргумент можно подставить несколько раз. А можно назвать поле — {city} — и передать его ключевым словом. Именованная подстановка — самая читаемая форма, когда полей больше двух.

три способа указать подстановку
print('{}, {}!'.format('Привет', 'мир'))
print('{1} важнее, чем {0}'.format('объём', 'качество'))

template = 'Доставка в {city}: {days} дн.'
print(template.format(city='Казань', days=3))
print(template.format(city='Сочи', days=5))
Вывод
Привет, мир!
качество важнее, чем объём
Доставка в Казань: 3 дн.
Доставка в Сочи: 5 дн.

Обратите внимание на последние две строки: заготовка template описана один раз, а заполнена дважды разными данными. Это и есть главный сценарий для format() — он оправдывает себя, когда один текст уходит в разные места.

format с словарём
data = {'city': 'Казань', 'days': 3}
print('Доставка в {city}: {days} дн.'.format(**data))
print('{0} / {1}'.format(*data.values()))
Вывод
Доставка в Казань: 3 дн.
Казань / 3

Двойная звёздочка в format(**data) — это распаковка словаря в именованные аргументы. Работает, но читается тяжело, и при опечатке в ключе вы получите KeyError в неожиданном месте. Альтернатива — format_map(data), который берёт словарь целиком и не требует распаковки: 'Доставка в {city}: {days} дн.'.format_map(data). Ещё аккуратнее — f-строка со словарём: f'Доставка в {data[city]}'.

Формат-спецификации: то же, что в f-строках

После двоеточия в format() пишется спецификация — тот же язык, что и в f-строках из урока 4. {:.2f} — два знака после запятой, {:>10} — выравнивание вправо в поле шириной 10. Язык спецификаций f-строки унаследовали как раз от format(), поэтому, выучив один, выучили оба.

спецификации в format()
print('{:.2f}'.format(3.14159))
print('{:>10}'.format('право'))
print('{:<10}|'.format('лево'))
print('{:^10}|'.format('центр'))
Вывод
3.14
     право
лево      |
  центр   |

Выравнивание — самая полезная спецификация на практике. {:>10} прижимает текст к правому краю поля, {:<10} — к левому, {:^10} — по центру. Вертикальная черта в конце нужна, чтобы увидеть, где заканчивается поле: без неё пробелы невидимы. Это тот же приём, что и в уроке про ljust и rjust, только записанный в виде спецификации.

СпецификацияЧто делаетПример
{:.2f}два знака после запятой3.14159 → '3.14'
{:>10}выравнивание вправо в поле 10'право' → ' право'
{:<10}выравнивание влево'лево' → 'лево '
{:^10}выравнивание по центру'центр' → ' центр '
{:,}разделитель тысяч1234567 → '1,234,567'
{:+d}явный знак плюса5 → '+5'
{:08d}ведущие нули до ширины 842 → '00000042'

Легаси: стиль с процентом

Самый старый способ — оператор %. Он пришёл из языка C, и в нём метки не нумеруются: %s означает строку, %d — целое, %f — дробное, а значения перечисляются справа в кортеже. При нескольких метках скобки обязательны. Отдельно стоит помнить, что сам процент печатается как %%.

чтение легаси-кода
name = 'Олег'
score = 87
print('Игрок: %s, очки: %d' % (name, score))
print('Точность: %.1f%%' % 99.5)
Вывод
Игрок: Олег, очки: 87
Точность: 99.5%

Этот стиль живёт в миллионах строк чужого кода, и читать его нужно уметь. Но писать новое на % я бы не стал: там невозможно вызвать метод, нельзя вставить выражение с пробелами без скобок, а любая ошибка в количестве аргументов падает в рантайме с невнятным сообщением. Процентный стиль — это «понять и починить», а не «писать».

Что выбрать: шпаргалка

  • f-строка — стандарт современного Python: значения известны на месте, код читается сверху вниз.
  • format() — шаблон живёт отдельно от данных: письма, отчёты, конфиги, переиспользуемые заготовки.
  • Именованная подстановка {name} — когда полей больше двух и важно, чтобы было видно, что куда попадёт.
  • Процент % — только для чтения чужого легаси-кода; в новом коде не пишется.
  • Спецификации после двоеточия (.2f, >10, ,) работают одинаково в format() и в f-строках.

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

И последняя практическая деталь: в f-строках внутри скобок можно писать любые выражения, включая вызовы методов и словарные ключи с кавычками другого типа. В старом %-стиле для вызова метода нужно писать %(name)s со словарём — отсюда все эти % в старых логах. Разница в читаемости очевидна.

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

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

округление и разделители тысяч
price = 1234.5
print(f'{price:.2f}')
print(f'{price:,.2f}')
print(f'{price:.0f}')
print(f'{1234567:,}')
Вывод
1234.50
1,234.50
1234
1,234,567

Спецификация .2f — два знака после запятой, , — разделитель тысяч (пробел или запятая, зависит от локали), .0f — ноль знаков после запятой с округлением. Для денег в Python есть ещё и модуль decimal, который считает точно, — но это уже не строки, а отдельная тема.

И последнее. Форматирование — это про вид, а не про данные. Отформатированная строка '1 234,50' не годится для повторного разбора: это уже текст, и чтобы получить из него число, его придётся снова чистить. Храните числа числами и форматируйте только в момент показа человеку. Тот же принцип, что и с выравниванием колонок.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

print('{0} {0} {1}!'.format('ха', 'ха-ха'))
print('Итого: %d руб.' % 1234.9)
print('{:.1f}'.format(7.25))
Проверь себя
0 / 8

1. Что подставится в '{} и {}'.format('соль', 'сахар')?

2. Что делает {1} в шаблоне '{1} и {0}'.format('a', 'b')?

3. Что подставит %d в 'Очки: %d' % (87.6,)?

4. Как напечатать сам знак процента в легаси-стиле?

5. Когда format() всё ещё лучше f-строки?

6. Как в format() подставить одно и то же значение дважды?

7. Что вернёт '{:.2f}'.format(3.14159)?

8. Что произойдёт при '{city}'.format_map({'citi': 'Казань'})?

Карточки терминов
Запомнено: 0 / 6
Практика

Оформите заготовку-шаблон ticket = 'Рейс {flight}: {city}, посадка {gate}' и напечатайте её с данными flight='SU-101', city='Сочи', gate='5'. Затем тем же методом выведите 'Рейс SU-202: Казань, посадка 2'.

practice.py
Вопросы и ответы по уроку

Какой способ форматирования использовать в новом коде?

f-строки — во всех случаях, где значения известны на месте написания. format() — когда нужен сохраняемый шаблон (письма, отчёты по заготовке). Процентный стиль в новом коде не пишут.

Почему f-строки нельзя сохранить как шаблон?

f-строка вычисляется сразу при создании: если city не определена — упадёт на месте. format же возвращает незаполненный шаблон с пустотами, а данные подставляются вызовом. Это разные моменты жизни строки.

Какой способ форматирования использовать в новом коде?

f-строки — во всех случаях, где значения известны на месте написания. format() — когда нужен сохраняемый шаблон (письма, отчёты по заготовке). Процентный стиль в новом коде не пишут, но читать его нужно: он встречается в старых проектах и статьях.

Урок 3: f-строки

Почему f-строки нельзя сохранить как шаблон?

f-строка вычисляется сразу при создании: если city не определена — упадёт на месте. format же возвращает незаполненный шаблон с пустотами, а данные подставляются вызовом. Это разные моменты жизни строки: шаблон может пережить десятки заполнений.

Что означают %r и %a в чужом коде?

Та же идея, что %s, но подставляется repr-представление объекта (с кавычками и экранированием) — удобно для отладочных логов. %a работает как %r, но с escape-последовательностями в ASCII-виде. Встречается в старых логах и тестах.

Как передать в format() данные из словаря?

Двойной звёздочкой: '{city}'.format(**data) — словарь распакуется в именованные аргументы. Чище и без распаковки — format_map(data), который берёт словарь целиком: '{city}: {days}'.format_map(data). Оба способа упадут с KeyError, если в шаблоне опечатка в имени поля.

Понравился урок? Сошлитесь на него

«f-строки из уроков 3 и 4 — не первый способ форматирования в Python, а третий.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.