Немножко о форматировании строк в питоне

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Как всем известно, в питоне есть четыре способа форматирования строк:

  1. string.Template

  2. сишный стиль

  3. f-строки

  4. str.format()

Первый, string.Template, я последний раз видел в проектах на версии питона не то 2.4, не то 2.5. Очень давно.

Сишный стиль, конечно же, самый няшный. К тому же в питоне можно давать подстановкам имена и после % указывать dict вместо tuple или одиночного значения, если оно не tuple. Этот стиль используется в модуле logging — см. описание класса LogRecord: The main information passed in is in msg and args, which are combined using str(msg) % args to create the message field of the record. — вы наверняка везде видели рекомендации использовать отложенное форматирование log.debug('%s, %s', 'hello', 'world) вместо log.debug('%s, %s' % ('hello', 'world)). Но можно и так: log.debug('%(greet)s, %(me)s', dict(greet='hello', me='world))

f-строки это не совсем данные, это часть кода и для их обработки задействуется компилятор. Но они сделаны по образу и подобию str.format(), который описан в https://peps.python.org/pep-3101/

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

class MyObject:

    def __format__(self, format_spec):
        print(format_spec)
        return "Here we go."

print(f"{MyObject():Format spec with {{ braces }} }")

выдаёт ошибку

NameError: name 'braces' is not defined

Потому что { braces } интерпретируется как set, состоящий из одного элемента braces, который мы не определяли. Но str.format() отрабатывает без ошибок:

print('{my_obj:Format spec with {{ braces }} }'.format(my_obj=MyObject()))
Format spec with { braces }
Here we go.

Потому что двойные скобки интерпретируются как экранирование одиночных. Можно считать это фичей.

Итак, format_spec допускает рекурсию, но она ограничивается двойкой:

x=1
y=2
z=3
print('x={x:{y:{z}}}'.format_map(locals()))
ValueError: Max string recursion exceeded

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

Как видим, format_spec может содержать что угодно, но стандартное форматирование реализовано в классе Formatter модуля string. Однако ничто не мешает написать свой formatter. Зачем? — Ну, например, чтобы найти имена всех подстановок в строке:

import string
import _string

class SubstCollector(string.Formatter):

    def format(self, format_string, /, *args, **kwargs):
        '''
        Return names of all substitutions in format_string.
        '''
        self._substs = set()
        self.vformat(format_string, args, kwargs)
        return self._substs

    def get_field(self, field_name, args, kwargs):
        '''
        Collect substitution names
        i.e. `arg_name` according to
        https://docs.python.org/3/library/string.html#formatspec.
        '''
        first, rest = _string.formatter_field_name_split(field_name)
        self._substs.add(first)
        return '', first

    def convert_field(self, value, conversion):
        '''
        Do nothing.
        '''
        return ''

    def format_field(self, value, format_spec):
        '''
        Do nothing.
        '''
        return ''

print(SubstCollector().format(
    'a={a[s].d:Format spec with b={b.c[d]} and {{braces}}} plus {e}'
))
{'a', 'e', 'b'}

Или сделать форматирование с поддержкой отступов. Или, что гораздо прикольнее, не ограничивать рекурсию и замутить свой мини-язык. Но это я всецело оставляю на ваше усмотрение.

Источник: https://habr.com/ru/articles/759250/


Интересные статьи

Интересные статьи

Однажды на работе у нас возникла проблема с производительностью одной из наших основных Python-библиотек. Эта библиотека формирует фундамент нашего конвейера 3D-обработки. Это довольно большая и ...
Уже давно прошли те времена, когда текстовые строки в языках программирования были исключительно байтовыми без поддержки символов национальных алфавитов, а в некоторых случаях еще и ограничены размеро...
Привет, Хабр!Если ваш проект вырос, в нем  бэкенд с фронтендом, различные точки входа API, интеграции с внешними системами, сложные алгоритмы, проверки введенных данных пользователем на валидност...
В предыдущей статье я описал векторные языки и их ключевые отличия от обычных языков. На коротких примерах я постарался показать, как эти особенности позволяют реализовыв...
Аналитическая СУБД ClickHouse обрабатывает множество разных строк, потребляя ресурсы. Для ускорения работы системы постоянно добавляются новые оптимизации. Разработчик ClickHouse Николай Кочетов ...