Форматирование строк с помощью класса шаблона Python

Введение Шаблоны Python используются для замены данных в строки. С помощью шаблонов мы получаем сильно настраиваемый интерфейс для подстановки строк (или интерполяции строк). Python уже предлагает множество способов замены строк [/ formatting-strings-with-python /], включая недавно представленные f-Strings [/ string-formatting-with-python-3s-f-strings /]. Хотя замена строк шаблонами является менее распространенной, ее сила заключается в том, как мы можем настроить наши правила форматирования строк. В th

Вступление

Шаблоны Python используются для замены данных в строки. С помощью шаблонов мы получаем сильно настраиваемый интерфейс для подстановки строк (или интерполяции строк).

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

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

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

Понимание класса шаблона Python

Класс Python Template был добавлен в string модуль, начиная с версии Python 2.4. Этот класс предназначен для использования в качестве альтернативы встроенным параметрам подстановки (в основном на % ) для создания сложных строковых шаблонов и для удобной для пользователя обработки.

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

  • Символ $
  • Действительный идентификатор Python. Идентификатор - это любая последовательность букв верхнего и нижнего регистра от A до Z, подчеркивания ( _ ) и цифр от 0 до 9. Идентификатор не может начинаться с цифр и не может быть ключевым словом Python.

В строке шаблона допустимыми заполнителями будут считаться $name и $age

Чтобы использовать Template в нашем коде, нам необходимо:

  1. Импортировать Template из string модуля
  2. Создайте допустимую строку шаблона
  3. Создание экземпляра Template с использованием строки шаблона в качестве аргумента
  4. Выполните замену с помощью метода подстановки

Вот базовый пример того, как мы можем использовать Template в нашем коде:

 >>> from string import Template 
 >>> temp_str = 'Hi $name, welcome to $site' 
 >>> temp_obj = Template(temp_str) 
 >>> temp_obj.substitute(name='John Doe', site='StackAbuse.com') 
 'Hi John Doe, welcome to StackAbuse.com' 

Мы замечаем, что при создании строки шаблона temp_str мы используем два заполнителя: $name и $site . Знак $ выполняет фактическую замену, а идентификаторы ( name и site ) используются для сопоставления заполнителей с конкретными объектами, которые нам нужно вставить в строку шаблона.

Магия завершается, когда мы используем метод substitute () для выполнения подстановки и построения желаемой строки. Подумайте о substitute() как если бы мы говорили Python, просмотрите эту строку и, если вы найдете $name , замените его на John Doe . Продолжайте поиск по строке и, если вы найдете идентификатор $site , превратите его в StackAbuse.com .

Имена аргументов, которые мы передаем в .substitute() должны совпадать с идентификаторами, которые мы использовали в заполнителях нашей строки шаблона.

Наиболее важное различие между Template и остальными инструментами подстановки строк, доступными в Python, заключается в том, что тип аргумента не принимается во внимание. Мы можем передать любой тип объекта, который можно преобразовать в действительную строку Python. Класс Template автоматически преобразует эти объекты в строки, а затем вставит их в окончательную строку.

Теперь, когда мы знаем основы использования Template , давайте углубимся в детали его реализации, чтобы лучше понять, как этот класс работает внутри. Обладая этими знаниями, мы сможем эффективно использовать класс в нашем коде.

Строка шаблона

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

Согласно PEP 292A - Simpler String Substitutions , следующие правила применяются для использования $ в заполнителях:

  1. $$ - это побег; он заменяется одним $
  2. $identifier заполнитель подстановки, соответствующий ключу сопоставления "идентификатор". По умолчанию «идентификатор» должен соответствовать идентификатору Python, как определено в http://docs.python.org/reference/lexical_analysis.html#identifiers-and-keywords . Первый неидентификационный символ после $ завершает эту спецификацию заполнителя.
  3. ${identifier} эквивалентен $identifier . Это необходимо, когда за заполнителем следуют действительные символы идентификатора, но не являются его частью, например "${noun}ification" . ( Источник )

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

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

 >>> budget = Template('The $time budget for investment is $$$amount') 
 >>> budget.substitute(time='monthly', amount='1,000.00') 
 'The monthly budget for investment is $1,000.00' 

Обратите внимание, что нет необходимости добавлять дополнительный пробел между экранированным знаком и следующим заполнителем, как мы это сделали в случае с $$$amount . Шаблоны достаточно умны, чтобы правильно избегать знака $

Второе правило устанавливает основы создания действительного заполнителя в наших строках шаблона. Каждый заполнитель должен быть построен с использованием $ за которым следует действительный идентификатор Python. Взгляните на следующий пример:

 >>> template = Template('$what, $who!') 
 >>> template.substitute(what='Hello', who='World') 
 'Hello, World!' 

Здесь оба заполнителя формируются с использованием действительных идентификаторов Python ( what и who ). Также обратите внимание, что, как указано во втором правиле, первый неидентификационный символ завершает заполнитель, как вы можете видеть в $who! где персонаж ! не является частью заполнителя, а является последней строкой.

Могут возникнуть ситуации, когда нам нужно частично подставить слово в строке. По этой причине у нас есть второй вариант создания заполнителя. Третье правило гласит, что ${identifier} эквивалентен $identifier и должен использоваться, когда действительные символы идентификатора следуют за заполнителем, но не являются частью самого заполнителя.

Допустим, нам нужно автоматизировать создание файлов, содержащих коммерческую информацию о продукции нашей компании. Файлы названы в соответствии с шаблоном, который включает код продукта, имя и производственную партию, все они разделены _ ). Рассмотрим следующий пример:

 >>> filename_temp = Template('$code_$product_$batch.xlsx') 
 >>> filename_temp.substitute(code='001', product='Apple_Juice', batch='zx.001.2020') 
 Traceback (most recent call last): 
 ... 
 KeyError: 'code_' 

Поскольку _ является допустимым символом идентификатора Python, наша строка шаблона работает не так, как ожидалось, и Template вызывает KeyError . Чтобы исправить эту проблему, мы можем использовать обозначение в фигурных скобках ( ${identifier} ) и построить наши заполнители следующим образом:

 >>> filename_temp = Template('${code}_${product}_$batch.xlsx') 
 >>> filename_temp.substitute(code='001', product='Apple_Juice', batch='zx.001.2020') 
 '001_Apple_Juice_zx.001.2020.xlsx' 

Теперь шаблон работает правильно! Это потому, что фигурные скобки правильно отделяют наши идентификаторы от символа _ Стоит отметить, что нам нужно использовать обозначения в фигурных скобках только для code и product а не для batch потому что . Символ, следующий за batch , не является допустимым символом идентификатора в Python.

Наконец, строка шаблона сохраняется в template экземпляра. Давайте вернемся к Hello, World! пример, но на этот раз мы собираемся немного template

 >>> template = Template('$what, $who!') # Original template 
 >>> template.template = 'My $what, $who template' # Modified template 
 >>> template.template 
 'My $what, $who template' 
 >>> template.substitute(what='Hello', who='World') 
 'My Hello, World template' 

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

Лучше всего создавать новые экземпляры Template для каждой отдельной строки шаблона, которую мы используем в нашем коде. Таким образом, мы избежим некоторых тонких и трудно обнаруживаемых ошибок, связанных с использованием неопределенных строк шаблона.

Метод replace ()

До сих пор мы использовали метод substitute() в Template для выполнения подстановки строк. Этот метод заменяет заполнители в строке шаблона, используя аргументы ключевого слова или используя сопоставление, содержащее пары "идентификатор-значение".

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

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

 >>> template = Template('Hi $name, welcome to $site') 
 >>> mapping = {'name': 'John Doe', 'site': 'StackAbuse.com'} 
 >>> template.substitute(**mapping) 
 'Hi John Doe, welcome to StackAbuse.com' 

Когда мы используем словари в качестве аргументов с substitute() , нам нужно использовать оператор распаковки словаря: ** . Этот оператор распакует пары ключ-значение в аргументы ключевого слова, которые будут использоваться для замены соответствующих заполнителей в строке шаблона.

Распространенные ошибки шаблонов

Есть несколько распространенных ошибок, которые мы можем непреднамеренно ввести при использовании класса Template

Например, KeyError возникает всякий раз, когда мы предоставляем неполный набор аргументов для substitute() . Рассмотрим следующий код, в котором используется неполный набор аргументов:

 >>> template = Template('Hi $name, welcome to $site') 
 >>> template.substitute(name='Jane Doe') 
 Traceback (most recent call last): 
 ... 
 KeyError: 'site' 

Если мы вызовем substitute() с набором аргументов, который не соответствует всем заполнителям в нашей строке шаблона, мы получим KeyError .

Если мы используем недопустимый идентификатор Python в некоторых из наших заполнителей, мы получим ValueError сообщающую нам, что заполнитель неверен.

Возьмем этот пример, в котором мы используем недопустимый идентификатор, $0name в качестве заполнителя вместо $name .

 >>> template = Template('Hi $0name, welcome to $site') 
 >>> template.substitute(name='Jane Doe', site='StackAbuse.com') 
 Traceback (most recent call last): 
 ... 
 ValueError: Invalid placeholder in string: line 1, col 4 

Только когда Template считывает строку шаблона для выполнения подстановки, он обнаруживает недопустимый идентификатор. Он немедленно вызывает ValueError . Обратите внимание, что 0name не является допустимым идентификатором или именем Python, потому что оно начинается с цифры.

Метод safe_substitute ()

У Template есть второй метод, который мы можем использовать для выполнения подстановки строк. Метод называется safe_substitute() . Он работает аналогично substitute() но когда мы используем неполный или несовпадающий набор аргументов, метод не вызывает KeyError .

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

Вот как работает safe_substitute() с неполным набором аргументов ( site будет отсутствовать):

 >>> template = Template('Hi $name, welcome to $site') 
 >>> template.safe_substitute(name='John Doe') 
 'Hi John Doe, welcome to $site' 

Здесь мы safe_substitute() используя неполный набор аргументов. Результирующая строка содержит исходный заполнитель $site , но исключение KeyError не возникает.

Настройка класса шаблона Python

Класс Python Template предназначен для создания подклассов и настройки. Это позволяет нам изменять шаблоны регулярных выражений и другие атрибуты класса в соответствии с нашими конкретными потребностями.

В этом разделе мы расскажем, как настроить некоторые из наиболее важных атрибутов класса и как это влияет на общее поведение наших объектов Template Начнем с атрибута класса .delimiter .

Использование другого разделителя

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

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

 from string import Template 
 class MyTemplate(Template): 
 delimiter = '#' 
 
 template = MyTemplate('Hi #name, welcome to #site') 
 print(template.substitute(name='Jane Doe', site='StackAbuse.com')) 
 
 # Output: 
 # 'Hi Jane Doe, welcome to StackAbuse.com' 
 
 # Escape operations also work 
 tag = MyTemplate('This is a Twitter hashtag: ###hashtag') 
 print(tag.substitute(hashtag='Python')) 
 
 # Output: 
 # 'This is a Twitter hashtag: #Python' 

Мы можем использовать наш MyTemplate же, как обычный класс Template Однако теперь мы должны использовать # вместо $ для создания наших заполнителей. Это может быть удобно, когда мы работаем со строками, которые обрабатывают множество знаков доллара, например, когда мы имеем дело с валютами.

Примечание: Не заменить delimiter с регулярным выражением. Класс шаблона автоматически избегает разделителя. Следовательно, если мы используем регулярное выражение в качестве delimiter , весьма вероятно, что наш настраиваемый Template будет работать некорректно.

Изменение того, что считается идентификатором

idpattern класса idpattern содержит регулярное выражение, которое используется для проверки второй половины заполнителя в строке шаблона. Другими словами, idpattern проверяет, что идентификаторы, которые мы используем в наших заполнителях, являются действительными идентификаторами Python. Значение idpattern по умолчанию - r'(?-i:[_a-zA-Z][_a-zA-Z0-9]*)' .

Мы можем idpattern Template и использовать наш собственный шаблон регулярного выражения для idpattern. Предположим, что нам нужно ограничить идентификаторы именами, которые не содержат подчеркивания ( _ ) или цифр ( [0-9] ). Для этого мы можем переопределить idpattern и удалить эти символы из шаблона следующим образом:

 from string import Template 
 class MyTemplate(Template): 
 idpattern = r'(?-i:[a-zA-Z][a-zA-Z]*)' 
 
 # Underscores are not allowed 
 template = MyTemplate('$name_underscore not allowed') 
 print(template.substitute(name_underscore='Jane Doe')) 

Если мы запустим этот код, мы получим эту ошибку:

 Traceback (most recent call last): 
 ... 
 KeyError: 'name' 

Мы можем подтвердить, что цифры также не допускаются:

 template = MyTemplate('$python3 digits not allowed') 
 print(template.substitute(python3='Python version 3.x')) 

Ошибка будет:

 Traceback (most recent call last): 
 ... 
 KeyError: 'python' 

Поскольку подчеркивание и цифры не включены в наш настраиваемый idpattern , Template применяет второе правило и разбивает местозаполнитель первым неидентификационным символом после $ . Вот почему мы получаем KeyError в каждом случае.

Создание расширенных подклассов шаблонов

Могут возникнуть ситуации, когда нам нужно изменить поведение Template Python, но переопределения delimiter , idpattern или того и другого недостаточно. В этих случаях мы можем пойти дальше и переопределить pattern чтобы определить совершенно новое регулярное выражение для наших пользовательских подклассов Template

Если вы решите использовать совершенно новое регулярное выражение для pattern , вам необходимо предоставить регулярное выражение с четырьмя именованными группами:

  1. escaped соответствует escape-последовательности для разделителя, как в $$
  2. named соответствует разделителю и действительному идентификатору Python, например, $identifier
  3. braced соответствует разделитель и действительный идентификатор Python с помощью фигурных скобок, как в ${identifier}
  4. invalid совпадения с другими неправильно сформированными разделителями, например на сайте $0site

Свойство pattern содержит скомпилированный объект регулярного выражения. Тем не менее, это возможно , чтобы проверить оригинальную регулярную строку выражения, обращаясь к pattern атрибута pattern свойства. Посмотрите следующий код:

 >>> template = Template('$name') 
 >>> print(template.pattern.pattern) 
 \$(?: 
 (?P<escaped>\$) | # Escape sequence of two delimiters 
 (?P<named>(?-i:[_a-zA-Z][_a-zA-Z0-9]*)) | # delimiter and a Python identifier 
 {(?P<braced>(?-i:[_a-zA-Z][_a-zA-Z0-9]*))} | # delimiter and a braced identifier 
 (?P<invalid>) # Other ill-formed delimiter exprs 
 ) 

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

Запуск кода с eval () и exec ()

Примечание . Встроенные функции eval() и exec() могут иметь важные последствия для безопасности при использовании со злонамеренным вводом. Используйте с осторожностью!

Этот последний раздел призван открыть вам глаза на то, насколько мощным Template , если мы будем использовать его вместе с некоторыми встроенными функциями Python, такими как eval() и exec() .

Функция eval() выполняет одно выражение Python и возвращает его результат. Функция exec() также выполняет выражение Python, но никогда не возвращает его значение. Обычно вы используете exec() когда вас интересует только побочный эффект выражения, например, измененное значение переменной.

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

В первом примере мы собираемся использовать шаблон вместе с eval() для динамического создания списков через понимание списка:

 >>> template = Template('[$exp for item in $coll]') 
 >>> eval(template.substitute(exp='item ** 2', coll='[1, 2, 3, 4]')) 
 [1, 4, 9, 16] 
 >>> eval(template.substitute(exp='2 ** item', coll='[3, 4, 5, 6, 7, 8]')) 
 [8, 16, 32, 64, 128, 256] 
 >>> import math 
 >>> eval(template.substitute(expression='math.sqrt(item)', collection='[9, 16, 25]')) 
 [3.0, 4.0, 5.0] 

Наш объект шаблона в этом примере содержит базовый синтаксис понимания списка. Начиная с этого шаблона, мы можем динамически создавать списки, заменяя заполнители допустимыми выражениями ( exp ) и коллекциями ( coll ). В качестве последнего шага мы запускаем понимание с помощью eval() .

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

 from string import Template 
 
 _class_template = """ 
 class ${klass}: 
 def __init__(self, name): 
 self.name = name 
 
 def ${method}(self): 
 print('Hi', self.name + ',', 'welcome to', '$site') 
 """ 
 
 template = Template(_class_template) 
 exec(template.substitute(klass='MyClass', 
 method='greet', 
 site='StackAbuse.com')) 
 
 obj = MyClass("John Doe") 
 obj.greet() 

Здесь мы создаем строку шаблона для хранения полнофункционального класса Python. Позже мы можем использовать этот шаблон для создания разных классов, но с разными именами в соответствии с нашими потребностями.

В этом случае exec() создает настоящий класс и переносит его в наше текущее пространство имен. С этого момента мы можем свободно использовать этот класс, как и любой обычный класс Python.

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

Заключение

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

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

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

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus