На этом шаге мы рассмотрим различные способы решения этой задачи.
Вы хотите создать строку, в которой на место переменных будут подставляться строковые представления значений этих переменных.
В Python нет прямой поддержки простой подстановки значений переменных в строках. Однако строковый метод format() предоставляет приближенную по смыслу возможность. Например:
>>> s = '{name} has {n} messages.' >>> s.format(name='Guido', n=37) 'Guido has 37 messages.' >>>
Если значения, которые должны быть подставлены, на самом деле находятся в переменных, вы можете использовать сочетание format_map() и vars(), как показано тут:
>>> name = 'Guido' >>> n = 37 >>> s.format_map(vars()) 'Guido has 37 messages.' >>>
Стоит отметить, что vars() также работает с экземплярами. Например:
>>> class Info: def __init__(self, name, n): self.name = name self.n = n >>> a = Info('Guido', 37) >>> s.format_map(vars(a)) 'Guido has 37 messages.' >>>
Недостаток format() и format_map() в том, что они не могут аккуратно справиться с отсутствующими значениями. Например:
>>> s.format(name='Guido') Traceback (most recent call last): . . . . KeyError: 'n' >>>
Этого можно избежать путем определения альтернативного класса словаря с методом __missing__(), как показано ниже:
class safesub(dict): def __missing__(self, key): return '{' + key + '}' >>>
Теперь этот класс можно использовать, чтобы обернуть значения, которые подаются на вход в format_map():
>>> del n # Убедимся, что n не определена >>> s.format_map(safesub(vars())) 'Guido has {n} messages.' >>>
Если вы обнаружите, что часто делаете такие вещи в своей программе, то можете спрятать процесс подстановки переменных в небольшую функцию, которая использует так называемый фреймхак (frame hack). Например:
>>> import sys >>> def sub(text): return text.format_map(safesub(sys._getframe(1).f_locals)) >>>
Теперь вы можете делать вот так:
>>> name = 'Guido' >>> n = 37 >>> print(sub('Hello {name}')) Hello Guido >>> print(sub('You have {n} messages.')) You have 37 messages. >>> print(sub('Your favorite color is {color}')) Your favorite color is {color} >>>
Отсутствие настоящей интерполяции переменных в Python привело к созданию разнообразных решений. В качестве альтернативы описанным выше вы иногда можете увидеть такой подход к форматированию строк:
>>> name = 'Guido' >>> n = 37 >>> '%(name) has %(n) messages.' % vars() 'Guido has 37 messages.' >>>
Или же вам могут попасться шаблонные строки:
>>> import string >>> s = string.Template('$name has $n messages.') >>> s.substitute(vars()) 'Guido has 37 messages.' >>>
Однако методы format() и format_map() являются более современными, нежели эти альтернативы, и отдавать предпочтение нужно им. Преимущество использования format() заключается в том, что вы также получаете все возможности форматирования строк (выравнивание, отступы, нумерацию и т. п.), что недоступно для альтернативных решений, таких как строковые объекты Template.
В этом рецепте нам также удалось показать несколько интересных продвинутых возможностей. Малоизвестный метод классов словарей и отображений missing() позволяет вам определить подход для работы с отсутствующими значениями. В классе safesub этот метод был определен таким образом, чтобы возвращать отсутствующие значения в форме заглушки (placeholder). Вместо того чтобы получить исключение KeyException, вы увидите отсутствующие значения появляющимися в возвращаемой строке (что может оказаться полезным для отладки).
Функция sub() использует sys._getframe(1), чтобы вернуть фрейм стека вызовов. Отсюда атрибут f_locals доступен, чтобы получить локальные переменные. Стоит отметить, что заигрывания с фреймами стека стоит избегать. Однако для вспомогательных функций типа строковой подстановки это может оказаться полезным. Отдельно заметим, что f_locals - это словарь, который является копией локальных переменных в вызывающей функции. Хотя вы можете изменить содержимое f_locals, эти изменения не станут постоянными. Поэтому, хотя доступ другому фрейму стека и может показаться адским злом, невозможно случайно переписать переменные или изменить локальное окружение вызывающей функции.
На следующем шаге мы рассмотрим разбивку текста на фиксированное количество колонок.