Руководство по новым методам форматирования строк в Python

Оглавление

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

Поскольку f-строки появились в языке Python сравнительно недавно, вам будет полезно познакомиться и со второй, чуть более старой техникой. Скорее всего, вы встретите ее в более старом коде Python.

В этом уроке вы узнаете о:

  1. Метод string .format()
  2. Литерал форматированной строки, или f-строка

Вы подробно познакомитесь с этими техниками форматирования и добавите их в свой набор инструментов для форматирования строк в Python.

Примечание: Существует стандартный модуль string, содержащий класс Template, который также обеспечивает некоторое форматирование строк с помощью интерполяции. Родственная техника, оператор модуляции строк , обеспечивает примерно ту же функциональность. В этом уроке мы не будем рассматривать ни одну из этих техник, но вам стоит почитать об операторе string modulo operator, если вам придется работать с кодом, написанным на Python 2.

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

Должен быть один и, желательно, только один - очевидный способ сделать это. -Тим Питерс, The Zen of Python

За годы существования и развития Python различные методы форматирования строк развивались исторически. Старые методы сохраняются для обеспечения обратной совместимости, а некоторые даже имеют специфическое применение.

Если вы только начинаете изучать Python и ищете единственный способ форматирования строк, то остановитесь на f-stringsPython. Если вы столкнулись со странным кодом формата строк и хотите узнать больше, или вам нужно работать со старыми версиями Python, тогда вам стоит узнать и о других методах.

Метод Python String .format()

Метод .format() был представлен в версии 2.6. Он во многом похож на оператор модуляции строк, но .format() значительно превосходит его по универсальности. Общая форма вызова Python .format() показана ниже:

<template>.format(<positional_argument(s)>, <keyword_argument(s)>)

Обратите внимание, что это метод, а не оператор. Вы вызываете метод на <template>, который представляет собой строку, содержащую поля-заменители . В <positional_arguments> и <keyword_arguments> к методу указываются значения, которые вставляются в <template> вместо полей-заменителей. Полученная отформатированная строка является возвращаемым значением метода.

В строке <template> поля замены заключены в фигурные скобки ({}). Все, что не заключено в фигурные скобки, является буквальным текстом, который копируется непосредственно из шаблона в вывод. Если вам необходимо включить в строку шаблона буквальный символ фигурных скобок, например { или }, то вы можете избежать этого символа, удвоив его:

>>> '{{ {0} }}'.format('foo')
'{ foo }'

Теперь фигурные скобки включены в вывод.

Метод String .format(): Аргументы

Начнем с небольшого примера, чтобы познакомить вас с ним, прежде чем вы погрузитесь в более подробную информацию о том, как использовать этот метод в Python для форматирования строк. Если использовать старую технику форматирования с помощью оператора модуляции строки , то код будет немного трудночитаемым:

>>> print('%d %s cost $%.2f' % (6, 'bananas', 1.74))
6 bananas cost $1.74

В приведенном выше примере для форматирования строки использовался оператор модуляции строки. Для лучшей читабельности можно использовать метод Python .format() для получения того же результата:

>>> print('{0} {1} cost ${2}'.format(6, 'bananas', 1.74))
6 bananas cost $1.74

В данном примере <template> - это строка '{0} {1} cost ${2}'. Поля замены {0}, {1} и {2} содержат числа, соответствующие нулевым позиционным аргументам 6, 'bananas' и 1.74. Каждый позиционный аргумент вставляется в шаблон на место соответствующего поля замены:

Python string format, positional parameters

Использование метода String .format() в Python для форматирования строки с позиционными аргументами

В следующем примере вместо позиционных параметров используются аргументы ключевого слова, что приводит к тому же результату:

>>> print('{quantity} {item} cost ${price}'.format(
...     quantity=6,
...     item='bananas',
...     price=1.74))
6 bananas cost $1.74

В этом случае полями замены будут {quantity}, {item} и {price}. Эти поля задают ключевые слова, соответствующие аргументам ключевых слов quantity=6, item='bananas' и price=1.74. Значение каждого ключевого слова вставляется в шаблон на место соответствующего поля-заменителя:

Python string format and keyword parameters

Использование метода String .format() в Python для форматирования строки с аргументами в виде ключевых слов

Более подробно о позиционных и ключевых аргументах вы узнаете в следующем уроке из этой вводной серии, где рассматриваются функции и передача аргументов. Пока же в двух последующих разделах мы покажем вам, как они используются в методе Python .format().

Позиционные аргументы

Позиционные аргументы вставляются в шаблон вместо пронумерованных полей замены. Как и при индексации списка, нумерация полей замены основана на нулях. Первый позиционный аргумент нумеруется 0, второй - 1 и так далее:

>>> '{0}/{1}/{2}'.format('foo', 'bar', 'baz')
'foo/bar/baz'

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

>>> '{2}.{1}.{0}/{0}{0}.{1}{1}.{2}{2}'.format('foo', 'bar', 'baz')
'baz.bar.foo/foofoo.barbar.bazbaz'

При указании номера поля замены, выходящего за пределы диапазона, вы получите ошибку. В следующем примере позиционные аргументы пронумерованы 0, 1 и 2, но в шаблоне указано {3}:

>>> '{3}'.format('foo', 'bar', 'baz')
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    '{3}'.format('foo', 'bar', 'baz')
IndexError: tuple index out of range

При этом возникает IndexError исключение.

Начиная с Python 3.1, вы можете не указывать номера в полях замены, в этом случае интерпретатор будет считать, что они расположены в последовательном порядке. Это называется автоматической нумерацией полей:

>>> '{}/{}/{}'.format('foo', 'bar', 'baz')
'foo/bar/baz'

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

>>> '{}{}{}{}'.format('foo','bar','baz')
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    '{}{}{}{}'.format('foo','bar','baz')
IndexError: tuple index out of range

В данном случае в шаблоне четыре поля замены, но только три аргумента, поэтому возникает исключение IndexError. С другой стороны, нет ничего страшного в том, что аргументов больше, чем полей замены. Лишние аргументы просто не используются:

>>> '{}{}'.format('foo', 'bar', 'baz')
'foobar'

Здесь аргумент 'baz' игнорируется.

Обратите внимание, что нельзя смешивать эти две техники:

>>> '{1}{}{0}'.format('foo','bar','baz')
Traceback (most recent call last):
  File "<pyshell#28>", line 1, in <module>
    '{1}{}{0}'.format('foo','bar','baz')
ValueError: cannot switch from manual field specification to automatic field
 numbering

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

Ключевые слова-аргументы

Ключевые слова-аргументы вставляются в строку шаблона вместо полей замены ключевых слов с тем же именем :

>>> '{x}/{y}/{z}'.format(x='foo', y='bar', z='baz')
'foo/bar/baz'

В этом примере значения аргументов ключевых слов x, y и z занимают место полей замены {x}, {y} и {z}, соответственно.

Если вы ссылаетесь на аргумент ключевого слова, который отсутствует, то вы увидите ошибку:

>>> '{x}/{y}/{w}'.format(x='foo', y='bar', z='baz')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'w'

Здесь вы указываете поле замены {w}, но нет соответствующего аргумента ключевого слова w. Python вызывает исключение KeyError.

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

>>> '{0}/{1}/{2}'.format('foo', 'bar', 'baz')
'foo/bar/baz'
>>> '{0}/{1}/{2}'.format('bar', 'baz', 'foo')
'bar/baz/foo'

>>> '{x}/{y}/{z}'.format(x='foo', y='bar', z='baz')
'foo/bar/baz'
>>> '{x}/{y}/{z}'.format(y='bar', z='baz', x='foo')
'foo/bar/baz'

В одном вызове Python .format() можно указать как позиционные, так и ключевые аргументы. Только учтите, что в этом случае все позиционные аргументы должны появляться перед любым из аргументов ключевого слова:

>>> '{0}{x}{1}'.format('foo', 'bar', x='baz')
'foobazbar'
>>> '{0}{x}{1}'.format('foo', x='baz', 'bar')
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

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

Во всех примерах, показанных до сих пор, значения, которые вы передавали методу Python .format(), были буквальными значениями, но вы можете указать переменные также:

>>> x = 'foo'
>>> y = 'bar'
>>> z = 'baz'
>>> '{0}/{1}/{s}'.format(x, y, s=z)
'foo/bar/baz'

В этом случае вы передаете переменные x и y как значения позиционных параметров, а z как значение параметра ключевого слова.

Метод String .format(): Простая замена полей

Как вы видели, при вызове метода Python .format() строка <template> содержит поля-заменители. Они указывают, куда в шаблоне вставить аргументы метода. Поле замены состоит из трех компонентов:

{[<name>][!<conversion>][:<format_spec>]}

Эти компоненты интерпретируются следующим образом:

Component Meaning
<name> Указывает источник форматируемого значения
<conversion> Указывает, какую стандартную функцию Python следует использовать для выполнения преобразования
<format_spec> Указывает более подробную информацию о том, как должно быть преобразовано значение

Каждый компонент является необязательным и может быть опущен. Давайте рассмотрим каждый компонент более подробно.

Компонент <name>

Компонент <name> является первой частью поля замены:

{[<name>][!<conversion>][:<format_spec>]}

<name> указывает, какой аргумент из списка аргументов будет вставлен в строку формата Python в указанном месте. Это либо число для позиционного аргумента, либо ключевое слово для аргумента с ключевым словом. В следующем примере <name> компонентами полей замены являются 0, 1 и baz, соответственно:

>>> x, y, z = 1, 2, 3
>>> '{0}, {1}, {baz}'.format(x, y, baz=z)
'1, 2, 3'

Если аргумент представляет собой список, то для доступа к элементам списка можно использовать индексы с помощью <name>:

>>> a = ['foo', 'bar', 'baz']
>>> '{0[0]}, {0[2]}'.format(a)
'foo, baz'
>>> '{my_list[0]}, {my_list[2]}'.format(my_list=a)
'foo, baz'

Аналогично, вы можете использовать ссылку на ключ с помощью <name>, если соответствующий аргумент является словарем:

>>> d = {'key1': 'foo', 'key2': 'bar'}
>>> d['key1']
'foo'
>>> '{0[key1]}'.format(d)
'foo'
>>> d['key2']
'bar'
>>> '{my_dict[key2]}'.format(my_dict=d)
'bar'

Вы также можете ссылаться на атрибуты объекта внутри поля замены. В предыдущем уроке этой серии вы узнали, что практически каждый элемент данных в Python является объектом. Объекты могут иметь атрибуты, доступ к которым осуществляется с помощью точечной нотации:

obj.attr

Здесь obj - это объект с атрибутом attr. Для доступа к атрибуту объекта используется точечная нотация. Рассмотрим пример. Комплексные числа в Python имеют атрибуты .real и .imag, которые представляют действительную и мнимую части числа. Вы можете получить к ним доступ, используя точечную нотацию:

>>> z = 3+5j
>>> type(z)
<class 'complex'>
>>> z.real
3.0
>>> z.imag
5.0

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

Актуальность атрибутов объекта в данном контексте заключается в том, что вы можете указать их в поле замены Python .format():

>>> z
(3+5j)
>>> 'real = {0.real}, imag = {0.imag}'.format(z)
'real = 3.0, imag = 5.0'

Как видите, в Python относительно просто форматировать компоненты сложных объектов с помощью метода .format().

Компонент <conversion>

Компонент <conversion> является средней частью поля замены:

{[<name>][!<conversion>][:<format_spec>]}

Python может форматировать объект как строку с помощью трех различных встроенных функций:

  1. str()
  2. repr()
  3. ascii()

По умолчанию метод Python .format() использует str(), но в некоторых случаях вам может понадобиться заставить .format() использовать один из двух других. Это можно сделать с помощью компонента <conversion> в поле замены. Возможные значения для <conversion> приведены в таблице ниже:

Значение Смысл
!s Преобразование с помощью функции str()
!r Преобразование с помощью функции repr()
!a Преобразование с помощью функции ascii()

Следующие примеры заставляют Python выполнять преобразование строк с помощью str(), repr() и ascii(), соответственно:

>>> '{0!s}'.format(42)
'42'
>>> '{0!r}'.format(42)
'42'
>>> '{0!a}'.format(42)
'42'

Во многих случаях результат будет одинаковым независимо от того, какую функцию преобразования вы используете, как видно из примера выше. Учитывая это, компонент <conversion> понадобится вам нечасто, поэтому мы не будем тратить на него много времени. Однако бывают ситуации, когда он имеет значение, поэтому полезно знать, что у вас есть возможность принудительно использовать определенную функцию преобразования, если вам это необходимо.

Компонент <format_spec>

Компонент <format_spec> является последней частью поля замены:

{[<name>][!<conversion>][:<format_spec>]}

<format_spec> представляет собой внутреннюю часть функциональности метода Python .format(). Он содержит информацию, которая позволяет контролировать форматирование значений перед вставкой в строку шаблона. В общем виде это выглядит так:

:[[<fill>]<align>][<sign>][#][0][<width>][<group>][.<prec>][<type>]

Десять подкомпонентов <format_spec> задаются в указанном порядке. Они управляют форматированием, как описано в таблице ниже:

Субкомпонент Эффект
: Отделяет <format_spec> от остальной части поля замены
<fill> Указывает, как размещать значения, которые не занимают всю ширину поля.
<align> Указывает, как выравнивать значения, которые не занимают всю ширину поля
<sign> Контролирует, будет ли включен ведущий знак для числовых значений
# Выбор альтернативной формы вывода для некоторых типов презентаций
0 Заставляет значения заполняться слева нулями вместо символов пробела ASCII
<width> Указывает минимальную ширину вывода
<group> Specifies a grouping character for numeric output
.<prec> Указывает количество цифр после десятичной точки для типов представления с плавающей точкой и максимальную ширину вывода для типов представления со строками
<type> Указывает тип представления, который является типом преобразования, выполняемого для соответствующего аргумента

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

Подкомпонент <type>

Начнем с <type>, которая является заключительной частью <format_spec>. Подкомпонент <type> задает тип представления, то есть тип преобразования, которое выполняется над соответствующим значением для получения выходного результата. Возможные значения показаны ниже:

Значение Тип представления
b Двоичное целое число
c Одиночный символ
d Десятичное целое число
e or E Экспоненциальный
f or F Плавающая точка
g or G Плавающая точка или экспоненциальная
o Восьмеричное целое число
s Строка
x or X Шестнадцатеричное целое число
% Процент

Эти типы похожи на типы преобразования, используемые с оператором модуляции строк, и во многих случаях они работают одинаково. Следующие примеры демонстрируют это сходство:

>>> '%d' % 42
'42'
>>> '{:d}'.format(42)
'42'

>>> '%f' % 2.1
'2.100000'
>>> '{:f}'.format(2.1)
'2.100000'

>>> '%s' % 'foobar'
'foobar'
>>> '{:s}'.format('foobar')
'foobar'

>>> '%x' % 31
'1f'
>>> '{:x}'.format(31)
'1f'

Однако между некоторыми типами представления Python .format() и типами преобразования оператора модуляции строк есть небольшие различия:

Тип Метод .format() Строковый оператор модуло
b Обозначает преобразование двоичных целых чисел Не поддерживается
i, u Не поддерживается Обозначает преобразование целых чисел
c Обозначает преобразование символов, и соответствующее значение должно быть целым числом Обозначает преобразование символов, но соответствующее значение может быть как целым числом, так и односимвольной строкой
g, G Выбирает между плавающей точкой и экспоненциальным выводом, но правила, управляющие выбором, немного сложнее Выбирает между выводом с плавающей точкой и экспонентой, в зависимости от величины экспоненты и значения, указанного для <prec>.
r, a Не поддерживается (хотя функциональность обеспечивается компонентами преобразования !r и !a в поле замены). Обозначает преобразование с помощью repr() или ascii(), соответственно
% Преобразует числовой аргумент в проценты Вставляет в вывод литеральный символ '%'

Далее вы увидите несколько примеров, иллюстрирующих эти различия, а также некоторые дополнительные возможности типов представления методов Python .format(). Первый тип представления, который вы увидите, - b, обозначающий двоичное целочисленное преобразование:

>>> '{:b}'.format(257)
'100000001'

Оператор modulo вообще не поддерживает двоичный тип преобразования:

>>> '%b' % 257
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: unsupported format character 'b' (0x62) at index 1

Однако оператор modulo позволяет преобразовывать десятичные целые числа с любым из типов d, i и u. Только тип представления d указывает на десятичное преобразование целых чисел с помощью метода Python .format(). Типы представления i и u не поддерживаются и не являются необходимыми.

Далее следует преобразование одного символа. Оператор modulo позволяет использовать либо целое, либо односимвольное значение с типом преобразования c:

>>> '%c' % 35
'#'
>>> '%c' % '#'
'#'

С другой стороны, метод Python .format() требует, чтобы значение, соответствующее типу представления c, было целым числом:

>>> '{:c}'.format(35)
'#'
>>> '{:c}'.format('#')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Unknown format code 'c' for object of type 'str'

При попытке передать значение другого типа вы получите ошибку ValueError.

Как для оператора модуляции строки, так и для метода Python .format(), тип преобразования g выбирает либо плавающую точку, либо экспоненту, в зависимости от величины экспоненты и значения, указанного для <prec>:

>>> '{:g}'.format(3.14159)
'3.14159'

>>> '{:g}'.format(-123456789.8765)
'-1.23457e+08'

Точные правила , регулирующие выбор, несколько сложнее с .format(), чем с оператором modulo. В целом, вы можете быть уверены, что выбор будет иметь смысл.

G идентичен g, за исключением случаев, когда вывод экспоненциальный, в этом случае 'E' будет в верхнем регистре:

>>> '{:G}'.format(-123456789.8765)
'-1.23457E+08'

Результат тот же, что и в предыдущем примере, но на этот раз с прописной буквы 'E'.

Примечание: Есть еще несколько ситуаций, в которых вы увидите разницу между типами презентаций g и G.

При некоторых обстоятельствах операция с плавающей точкой может привести к значению, которое по сути бесконечно. Строковое представление такого числа в Python - 'inf'. Также может случиться, что в результате операции с плавающей точкой получается значение, которое нельзя представить в виде числа. В Python это обозначается строкой 'NaN', которая расшифровывается как Not a Number.

Когда вы передаете эти значения методу Python .format(), тип представления g выводит строчные буквы, а G выводит прописные:

>>> x = 1e300 * 1e300
>>> x
inf

>>> '{:g}'.format(x)
'inf'
>>> '{:g}'.format(x * 0)
'nan'

>>> '{:G}'.format(x)
'INF'
>>> '{:G}'.format(x * 0)
'NAN'

Вы увидите аналогичное поведение с типами презентаций f и F:

>>> '{:f}'.format(x)
'inf'
>>> '{:F}'.format(x)
'INF'

>>> '{:f}'.format(x * 0)
'nan'
>>> '{:F}'.format(x * 0)
'NAN'

Более подробную информацию о представлении с плавающей точкой, inf и NaN можно найти в Википедии на странице IEEE 754-1985.

Оператор modulo поддерживает типы преобразования r и a для принудительного преобразования по repr() и ascii(), соответственно. Метод Python .format() не поддерживает типы представления r и a, но вы можете сделать то же самое с помощью компонентов преобразования !r и !a в поле замены.

Наконец, вы можете использовать тип преобразования % с оператором modulo, чтобы вставить в вывод литеральный символ '%':

>>> '%f%%' % 65.0
'65.000000%'

Для вставки литерального символа '%' в вывод метода Python .format() не нужно ничего особенного, поэтому тип представления % служит другой удобной цели для вывода процентов. Он умножает указанное значение на 100 и добавляет знак процента:

>>> '{:%}'.format(0.65)
'65.000000%'

<<<Оставшиеся части

указывают, <format_spec> как форматируется выбранный тип представления, примерно так же, как спецификаторы ширины и точности и флаги преобразования оператора строковой модуляции . Более подробно они описаны в следующих разделах.

Состав <fill> и <align> Подкомпоненты

<fill> и <align> управляют тем, как форматированный вывод заполняется и позиционируется в пределах заданной ширины поля. Эти подкомпоненты имеют смысл только тогда, когда значение форматированного поля не занимает всю ширину поля, что может произойти только в том случае, если минимальная ширина поля задана с помощью <width>. Если <width> не указан, то <fill> и <align> фактически игнорируются. О <width> вы узнаете позже в этом учебнике.

Вот возможные значения для подкомпонента <align>:

  • <
  • >
  • ^
  • =

Значение с использованием знака "меньше, чем" (<) указывает на то, что вывод выравнивается по левому краю:


>>> '{0:<8s}'.format('foo')
'foo     '
>>> '{0:<8d}'.format(123)
'123     '

Это поведение по умолчанию для строковых значений.

Значение с использованием знака "больше чем" (>) указывает на то, что вывод должен быть выровнен по правому краю:

>>> '{0:>8s}'.format('foo')
'     foo'
>>> '{0:>8d}'.format(123)
'     123'

Это поведение по умолчанию для числовых значений.

Значение с использованием каретки (^) указывает на то, что вывод должен быть центрирован в поле вывода:

>>> '{0:^8s}'.format('foo')
'  foo   '
>>> '{0:^8d}'.format(123)
'  123   '

Наконец, вы также можете указать значение с помощью знака равенства (=) для подкомпонента <align>. Это имеет значение только для числовых значений и только в том случае, если знак включен в вывод.

Когда числовой вывод включает знак, он обычно располагается непосредственно слева от первой цифры числа, как показано выше. Если для <align> установлен знак равенства (=), то знак появляется у левого края поля вывода, а между знаком и числом помещается подложка:

>>> '{0:+8d}'.format(123)
'    +123'
>>> '{0:=+8d}'.format(123)
'+    123'

>>> '{0:+8d}'.format(-123)
'    -123'
>>> '{0:=+8d}'.format(-123)
'-    123'

Вы подробно рассмотрите компонент <sign> в следующем разделе.

<fill> указывает, как заполнить дополнительное пространство, если отформатированное значение не полностью заполняет ширину вывода. Это может быть любой символ, кроме фигурных скобок ({}). (Если вы действительно считаете необходимым заполнить поле фигурными скобками, то вам придется найти другой способ!)

Ниже приведены примеры использования <fill>:

>>> '{0:->8s}'.format('foo')
'-----foo'

>>> '{0:#<8d}'.format(123)
'123#####'

>>> '{0:*^8s}'.format('foo')
'**foo***'

Если вы указываете значение для <fill>, то вы должны также указать значение для <align>.

Подкомпонент <sign>

С помощью компонента <sign> можно контролировать появление знака в числовом выводе. Например, в следующем примере знак плюс (+), указанный в <format_spec>, указывает на то, что значение всегда должно отображаться с ведущим знаком:

>>> '{0:+6d}'.format(123)
'  +123'
>>> '{0:+6d}'.format(-123)
'  -123'

Здесь используется знак плюс (+), поэтому знак всегда будет включен как для положительных, так и для отрицательных значений. Если вы используете знак минус (-), то только отрицательные числовые значения будут содержать ведущий знак, а положительные - нет:

>>> '{0:-6d}'.format(123)
'   123'
>>> '{0:-6d}'.format(-123)
'  -123'

Когда вы используете одиночный пробел (' '), это означает, что для отрицательных значений используется знак, а для положительных - символ ASCII пробела:


>>> '{0:*> 6d}'.format(123)
'** 123'

>>> '{0:*>6d}'.format(123)
'***123'

>>> '{0:*> 6d}'.format(-123)
'**-123'

Поскольку символ пробела является символом заполнения по умолчанию, вы заметите его эффект только в том случае, если будет указан альтернативный символ <fill>.

Наконец, вспомните, что когда вы указываете знак равенства (=) для <align> и включаете спецификатор <sign>, прокладка идет между знаком и значением, а не слева от знака.

Подкомпонент #

Когда вы указываете хэш-символ (#) в <format_spec>, Python будет выбирать альтернативную форму вывода для определенных типов представления. Это аналогично флагу преобразования # для оператора модуляции строки. Для двоичного, восьмеричного и шестнадцатеричного типов представления символ хэша (#) вызывает включение явного индикатора основания слева от значения:


>>> '{0:b}, {0:#b}'.format(16)
'10000, 0b10000'
>>> '{0:o}, {0:#o}'.format(16)
'20, 0o20'
>>> '{0:x}, {0:#x}'.format(16)
'10, 0x10'

Как видите, базовый индикатор может быть 0b, 0o или 0x.

Для типов представления с плавающей точкой или экспоненциального типа символ хеша заставляет вывод содержать десятичную точку, даже если вывод состоит из целого числа:


>>> '{0:.0f}, {0:#.0f}'.format(123)
'123, 123.'
>>> '{0:.0e}, {0:#.0e}'.format(123)
'1e+02, 1.e+02'

Для любого типа представления, кроме показанных выше, символ хэша (#) не имеет значения.

Подкомпонент 0

Если вывод меньше указанной ширины поля и вы указали цифру ноль (0) в <format_spec>, то значения будут заполнены слева нулями вместо символов ASCII пробела:


>>> '{0:05d}'.format(123)
'00123'
>>> '{0:08.1f}'.format(12.3)
'000012.3'

Обычно вы используете это для числовых значений, как показано выше. Однако он работает и для строковых значений:


>>> '{0:>06s}'.format('foo')
'000foo'

Если вы указали и <fill>, и <align>, то <fill> отменяет действие 0:


>>> '{0:*>05d}'.format(123)
'**123'

<fill> и 0 по сути управляют одним и тем же, поэтому нет необходимости указывать оба варианта. На самом деле, 0 действительно лишний, и, вероятно, был включен для удобства разработчиков, которые знакомы с аналогичным флагом преобразования 0 в операторе модуляции строк.

Подкомпонент <width>

<width> задает минимальную ширину поля вывода:


>>> '{0:8s}'.format('foo')
'foo     '
>>> '{0:8d}'.format(123)
'     123'

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


>>> '{0:2s}'.format('foobar')
'foobar'

В этом случае <width> фактически игнорируется.

Подкомпонент <group>

<group> позволяет включить в числовой вывод символ-разделитель группировки. Для десятичного представления и представления с плавающей точкой <group> может быть указан как символ запятой (,) или символ подчеркивания (_). Этот символ разделяет каждую группу из трех цифр в выводе:


>>> '{0:,d}'.format(1234567)
'1,234,567'
>>> '{0:_d}'.format(1234567)
'1_234_567'

>>> '{0:,.2f}'.format(1234567.89)
'1,234,567.89'
>>> '{0:_.2f}'.format(1234567.89)
'1_234_567.89'

Значение <group> с использованием символа подчеркивания (_) также может быть указано в двоичном, восьмеричном и шестнадцатеричном типах представления. В этом случае каждая группа из четырех цифр разделяется символом подчеркивания в выводе:


>>> '{0:_b}'.format(0b111010100001)
'1110_1010_0001'
>>> '{0:#_b}'.format(0b111010100001)
'0b1110_1010_0001'

>>> '{0:_x}'.format(0xae123fcc8ab2)
'ae12_3fcc_8ab2'
>>> '{0:#_x}'.format(0xae123fcc8ab2)
'0xae12_3fcc_8ab2'

Если вы попытаетесь указать <group> с любым типом представления, кроме перечисленных выше, то ваш код вызовет исключение.

Подкомпонент .<prec>

.<prec> задает количество цифр после десятичной точки для типов представления с плавающей точкой:


>>> '{0:8.2f}'.format(1234.5678)
' 1234.57'
>>> '{0:8.4f}'.format(1.23)
'  1.2300'

>>> '{0:8.2e}'.format(1234.5678)
'1.23e+03'
>>> '{0:8.4e}'.format(1.23)
'1.2300e+00'

Для строковых типов .<prec> задает максимальную ширину преобразованного вывода:


>>> '{:.4s}'.format('foobar')
'foob'

Если вывод будет длиннее указанного значения, то он будет усечен.

Метод String .format(): Вложенные поля-заменители

Напомним, что звездочкой можно указать либо <width>, либо <prec> с помощью строкового оператора modulo:


>>> w = 10
>>> p = 2
>>> '%*.*f' % (w, p, 123.456)  # Width is 10, precision is 2
'    123.46'

Затем соответствующие значения берутся из списка аргументов. Это позволяет динамически оценивать <width> и <prec> во время выполнения, как показано в примере выше. Метод .format() в Python предоставляет аналогичные возможности, используя вложенные поля замены.

Внутри поля замены можно указать вложенный набор фигурных скобок ({}), который содержит имя или число, относящееся к одному из позиционных или ключевых аргументов метода. Эта часть поля замены будет оценена во время выполнения и заменена соответствующим аргументом. Того же эффекта, что и в приведенном выше примере с оператором модуляции строки, можно добиться с помощью вложенных полей замены:


>>> w = 10
>>> p = 2
>>> '{2:{0}.{1}f}'.format(w, p, 123.456)
'    123.46'

Здесь <name> компонентом поля замены является 2, что указывает на третий позиционный параметр, значение которого 123.456. Это и есть форматируемое значение. Вложенные поля замены {0} и {1} соответствуют первому и второму позиционным параметрам, w и p. Они занимают места <width> и <prec> в <format_spec> и позволяют оценивать ширину и точность поля во время выполнения.

Вы также можете использовать аргументы ключевых слов с вложенными полями замены. Этот пример функционально эквивалентен предыдущему:


>>> w = 10
>>> p = 2
>>> '{val:{wid}.{pr}f}'.format(wid=w, pr=p, val=123.456)
'    123.46'

В любом случае, значения w и p оцениваются во время выполнения и используются для модификации <format_spec>. Результат фактически аналогичен следующему:


>>> '{0:10.2f}'.format(123.456)
'    123.46'

Оператор модуляции строк позволяет только <width> и <prec> оценивать таким образом во время выполнения. В отличие от этого, в методе Python .format() вы можете указать любую часть <format_spec> с помощью вложенных полей замены.

В следующем примере тип представления <type> задается вложенным полем замены и определяется динамически:


>>> bin(10), oct(10), hex(10)
('0b1010', '0o12', '0xa')
>>> for t in ('b', 'o', 'x'):
...     print('{0:#{type}}'.format(10, type=t))
...
0b1010
0o12
0xa

Здесь группирующий символ <group> является вложенным:


>>> '{0:{grp}d}'.format(123456789, grp='_')
'123_456_789'
>>> '{0:{grp}d}'.format(123456789, grp=',')
'123,456,789'

Фух! Это было приключение. Спецификация строки шаблона - это практически отдельный язык!

Как видите, форматирование строк может быть очень тонко настроено при использовании метода Python .format(). Далее вы увидите еще одну технику форматирования строк и вывода, которая обладает всеми преимуществами .format(), но имеет более прямой синтаксис.

Букварь форматированной строки Python (f-String)

В версии 3.6 появился новый синтаксис форматирования строк Python, называемый форматированным строковым литералом. Их также неофициально называют f-строками, термин, который был первоначально введен в PEP 498, где они были впервые предложены.

f-String Syntax

Строка f выглядит очень похоже на типичную строку Python, за исключением того, что к ней добавляется символ f:

>>> f'foo bar baz'
'foo bar baz'

Вы также можете использовать заглавные буквы F:

>>> s = F'qux quux'
>>> s
'qux quux'

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

>>> f'foo'
'foo'
>>> f"bar"
'bar'
>>> f'''baz'''
'baz'

Магия f-строк заключается в том, что вы можете встраивать выражения Python непосредственно в них. Любая часть f-строки, заключенная в фигурные скобки ({}), рассматривается как выражение. Выражение оценивается и преобразуется в строковое представление, а результат интерполируется в исходную строку в этом месте:

>>> s = 'bar'
>>> print(f'foo.{s}.baz')
foo.bar.baz

Интерпретатор обрабатывает оставшуюся часть f-строки - все, что не находится внутри фигурных скобок, - так же, как и обычную строку. Например, экранирующие последовательности обрабатываются так, как и ожидалось:

>>> s = 'bar'
>>> print(f'foo\n{s}\nbaz')
foo
bar
baz

Вот пример, приведенный ранее, с использованием f-строки:

>>> quantity = 6
>>> item = 'bananas'
>>> price = 1.74
>>> print(f'{quantity} {item} cost ${price}')
6 bananas cost $1.74

Это эквивалентно следующему:

>>> quantity = 6
>>> item = 'bananas'
>>> price = 1.74
>>> print('{0} {1} cost ${2}'.format(quantity, item, price))
6 bananas cost $1.74

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

  • Переменные:

    >>> quantity, item, price = 6, 'bananas', 1.74
    >>> f'{quantity} {item} cost ${price}'
    '6 bananas cost $1.74'
    
  • Арифметические выражения:

    >>> quantity, item, price = 6, 'bananas', 1.74
    >>> print(f'Price per item is ${price/quantity}')
    Price per item is $0.29
    
    >>> x = 6
    >>> print(f'{x} cubed is {x**3}')
    6 cubed is 216
    
  • Объекты составных типов:

    >>> a = ['foo', 'bar', 'baz']
    >>> d = {'foo': 1, 'bar': 2}
    
    >>> print(f'a = {a} | d = {d}')
    a = ['foo', 'bar', 'baz'] | d = {'foo': 1, 'bar': 2}
    
  • Индексирование, нарезка и ключевые ссылки:

    >>> a = ['foo', 'bar', 'baz']
    >>> d = {'foo': 1, 'bar': 2}
    
    >>> print(f'First item in list a = {a[0]}')
    First item in list a = foo
    
    >>> print(f'Last two items in list a = {a[-2:]}')
    Last two items in list a = ['bar', 'baz']
    
    >>> print(f'List a reversed = {a[::-1]}')
    List a reversed = ['baz', 'bar', 'foo']
    
    >>> print(f"Dict value for key 'bar' is {d['bar']}")
    Dict value for key 'bar' is 2
    
  • Вызовы функций и методов:

    >>> a = ['foo', 'bar', 'baz', 'qux', 'quux']
    >>> print(f'List a has {len(a)} items')
    List a has 5 items
    
    >>> s = 'foobar'
    >>> f'--- {s.upper()} ---'
    '--- FOOBAR ---'
    
    >>> d = {'foo': 1, 'bar': 2}
    >>> print(f"Dict value for key 'bar' is {d.get('bar')}")
    Dict value for key 'bar' is 2
    
  • Условные выражения:

    >>> x = 3
    >>> y = 7
    >>> print(f'The larger of {x} and {y} is {x if x > y else y}')
    The larger of 3 and 7 is 7
    
    >>> age = 13
    >>> f'I am {"a minor" if age < 18 else "an adult"}.'
    'I am a minor.'
    
  • Атрибуты объекта:

    >>> z = 3+5j
    >>> z
    (3+5j)
    
    >>> print(f'real = {z.real}, imag = {z.imag}')
    real = 3.0, imag = 5.0
    

Чтобы включить литеральную фигурную скобку в f-строку, уберите ее, удвоив, так же, как это делается в строке шаблона для метода Python .format():

>>> z = 'foobar'
>>> f'{{ {z[::-1]} }}'
'{ raboof }'

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

>>> z = 'bar'
>>> print(f'foo\n{z}\nbaz')
foo
bar
baz
>>> print(rf'foo\n{z}\nbaz')
foo\nbar\nbaz
>>> print(fr'foo\n{z}\nbaz')
foo\nbar\nbaz

Обратите внимание, что вы можете сначала указать 'r', а затем 'f', или наоборот.

Ограничения выражения f-String

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

>>> f'foo{}bar'
  File "<stdin>", line 1
SyntaxError: f-string: empty expression not allowed

Не очевидно, зачем вам это нужно. Но если вы чувствуете, что вынуждены попробовать, то знайте, что это не сработает.

Дополнительно, выражение f-строки не может содержать символ обратной косой черты (\) . Кроме всего прочего, это означает, что в выражении f-строки нельзя использовать последовательность символов обратной косой черты:

>>> print(f'foo{\n}bar')
  File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash
>>> print(f'foo{\'}bar')
  File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash

Вы можете обойти это ограничение, создав временную переменную, содержащую последовательность символов, которую вы хотите вставить:

>>> nl = '\n'
>>> print(f'foo{nl}bar')
foo
bar
>>> quote = '\''
>>> print(f'foo{quote}bar')
foo'bar

И наконец, выражение в f-строке, заключенное в тройные кавычки , не может содержать комментариев:

>>> z = 'bar'

>>> print(f'''foo{
... z
... }baz''')
foobarbaz

>>> print(f'''foo{
... z    # Comment
... }''')
  File "<stdin>", line 3
SyntaxError: f-string expression part cannot include '#'

Обратите внимание, что многострочная f-строка может содержать встроенные новые строки.

f-String Formatting

Как и метод Python .format(), f-строки поддерживают множество модификаторов, которые управляют конечным видом выводимой строки. Есть и другие хорошие новости. Если вы освоили метод Python .format(), то вы уже знаете, как использовать Python для форматирования f-строк!

Выражения в f-строках могут быть изменены с помощью <conversion> или <format_spec>, как и поля замены, используемые в шаблоне .format(). Синтаксис идентичен. Фактически, в обоих случаях Python отформатирует поле замены с помощью одной и той же внутренней функции. В следующем примере !r задается как компонент <conversion> в строке шаблона .format():

>>> s = 'foo'
>>> '{0!r}'.format(s)
"'foo'"

Это заставляет выполнять преобразование с помощью repr(). Вы можете получить практически тот же код, используя вместо него f-строку:

>>> s = 'foo'
>>> f'{s!r}'
"'foo'"

Все компоненты <format_spec>, которые работают с .format(), также работают с f-строками:

>>> n = 123
>>> '{:=+8}'.format(n)
'+    123'
>>> f'{n:=+8}'
'+    123'

>>> s = 'foo'
>>> '{0:*^8}'.format(s)
'**foo***'
>>> f'{s:*^8}'
'**foo***'

>>> n = 0b111010100001
>>> '{0:#_b}'.format(n)
'0b1110_1010_0001'
>>> f'{n:#_b}'
'0b1110_1010_0001'

Вложенность также работает, как и вложенные поля замены с помощью метода .format() в Python:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux']
>>> w = 4
>>> f'{len(a):0{w}d}'
'0005'

>>> n = 123456789
>>> sep = '_'
>>> f'{(n * n):{sep}d}'
'15_241_578_750_190_521'

Это означает, что элементы форматирования могут быть оценены во время выполнения программы.

f-строки и метод Python .format() - это, более или менее, два разных способа сделать одно и то же, причем f-строки - это более краткое сокращение. Следующие выражения по сути одинаковы:

f'{<expr>!<conversion>:<format_spec>}'

'{0!<conversion>:<format_spec>}'.format(<expr>)

Если вы хотите глубже изучить f-строки, то посмотрите Python 3's f-Strings: Улучшенный синтаксис форматирования строк (курс).

Заключение

В этом уроке вы освоили две дополнительные техники, которые можно использовать в Python для форматирования строковых данных. Теперь у вас есть все необходимые инструменты для подготовки строковых данных к выводу или отображению!

Возможно, вам интересно, какую технику форматирования в Python следует использовать. При каких обстоятельствах вы бы выбрали .format(), а не f-string? См. раздел Python String Formatting Best Practices, чтобы узнать о некоторых соображениях, которые следует принять во внимание.

В следующем уроке вы узнаете больше о функциях в Python. На протяжении этой серии уроков вы видели множество примеров встроенных функций в Python. В Python, как и в большинстве языков программирования, вы можете определять свои собственные пользовательские функции. Если вам не терпится узнать, как это сделать, переходите к следующему уроку!

Вернуться на верх