частичное форматирование строк



можно ли сделать частичное форматирование строк с помощью расширенных методов форматирования строк, подобных шаблону строки

626   13  

13 ответов:

Вы можете обмануть его в частичное форматирование путем перезаписи отображение:

import string

class FormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"

s = '{foo} {bar}'
formatter = string.Formatter()
mapping = FormatDict(foo='FOO')
print(formatter.vformat(s, (), mapping))

печати

FOO {bar}

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

если вы знаете, в каком порядке вы форматируете вещи:

s = '{foo} {{bar}}'

используйте его так:

ss = s.format(foo='FOO') 
print ss 
>>> 'FOO {bar}'

print ss.format(bar='BAR')
>>> 'FOO BAR'

вы не можете указать foo и bar в то же время - вы должны сделать это последовательно.

ограничение .format() - невозможность делать частичные замены-меня беспокоит.

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

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

import string
template = string.Template('${x} ${y}')
try:
  template.substitute({'x':1}) # raises KeyError
except KeyError:
  pass

# but the following raises no error
partial_str = template.safe_substitute({'x':1}) # no error

# partial_str now contains a string with partial substitution
partial_template = string.Template(partial_str)
substituted_str = partial_template.safe_substitute({'y':2}) # no error
print substituted_str # prints '12'

сформировал удобную обертку на основе этого:

class StringTemplate(object):
    def __init__(self, template):
        self.template = string.Template(template)
        self.partial_substituted_str = None

    def __repr__(self):
        return self.template.safe_substitute()

    def format(self, *args, **kws):
        self.partial_substituted_str = self.template.safe_substitute(*args, **kws)
        self.template = string.Template(self.partial_substituted_str)
        return self.__repr__()


>>> s = StringTemplate('${x}${y}')
>>> s
'${x}${y}'
>>> s.format(x=1)
'1${y}'
>>> s.format({'y':2})
'12'
>>> print s
12

аналогично оболочка на основе ответа Свена, который использует форматирование строки по умолчанию:

class StringTemplate(object):
    class FormatDict(dict):
        def __missing__(self, key):
            return "{" + key + "}"

    def __init__(self, template):
        self.substituted_str = template
        self.formatter = string.Formatter()

    def __repr__(self):
        return self.substituted_str

    def format(self, *args, **kwargs):
        mapping = StringTemplate.FormatDict(*args, **kwargs)
        self.substituted_str = self.formatter.vformat(self.substituted_str, (), mapping)

Не уверен, что это нормально, как быстрый обходной путь, но как насчет

s = '{foo} {bar}'
s.format(foo='FOO', bar='{bar}')

? :)

можно использовать

если вы определяете свой собственный Formatter, который переопределяет get_value метод, вы можете использовать это для сопоставления неопределенных имен полей с тем, что вы хотите:

http://docs.python.org/library/string.html#string.Formatter.get_value

например, вы могли бы сопоставить bar до "{bar}" Если bar не в kwargs.

однако, это требует использования format() метод вашего объекта форматирования, а не строки format() метод.

>>> 'fd:{uid}:{{topic_id}}'.format(uid=123)
'fd:123:{topic_id}'

попробуйте это.

спасибо Янтарькомментарий, я придумал это:

import string

try:
    # Python 3
    from _string import formatter_field_name_split
except ImportError:
    formatter_field_name_split = str._formatter_field_name_split


class PartialFormatter(string.Formatter):
    def get_field(self, field_name, args, kwargs):
        try:
            val = super(PartialFormatter, self).get_field(field_name, args, kwargs)
        except (IndexError, KeyError, AttributeError):
            first, _ = formatter_field_name_split(field_name)
            val = '{' + field_name + '}', first
        return val

для меня этого было достаточно:

>>> ss = 'dfassf {} dfasfae efaef {} fds'
>>> nn = ss.format('f1', '{}')
>>> nn
'dfassf f1 dfasfae efaef {} fds'
>>> n2 = nn.format('whoa')
>>> n2
'dfassf f1 dfasfae efaef whoa fds'

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

class IncrementalFormatting:
    def __init__(self, string):
        self._args = []
        self._kwargs = {}
        self._string = string

    def add(self, *args, **kwargs):
        self._args.extend(args)
        self._kwargs.update(kwargs)

    def get(self):
        return self._string.format(*self._args, **self._kwargs)

пример:

template = '#{a}:{}/{}?{c}'
message = IncrementalFormatting(template)
message.add('abc')
message.add('xyz', a=24)
message.add(c='lmno')
assert message.get() == '#24:abc/xyz?lmno'

есть еще один способ добиться этого, т. е. с помощью format и % для замены переменных. Например:

>>> s = '{foo} %(bar)s'
>>> s = s.format(foo='my_foo')
>>> s
'my_foo %(bar)s'
>>> s % {'bar': 'my_bar'}
'my_foo my_bar'

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

tmpl = '{foo}, {bar}'
tmpl.replace('{bar}', 'BAR')
Out[3]: '{foo}, BAR'

таким образом, вы все еще можете использовать tmpl как обычный шаблон и выполнять частичное форматирование только при необходимости. Я нахожу эту проблему слишком тривиальной, чтобы использовать чрезмерное решение, такое как Мохан Радж.

вы могли бы обернуть его в функцию, которая принимает аргументы по умолчанию:

def print_foo_bar(foo='', bar=''):
    s = '{foo} {bar}'
    return s.format(foo=foo, bar=bar)

print_foo_bar(bar='BAR') # ' BAR'

Comments

    Ничего не найдено.