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()метод.
спасибо Янтарькомментарий, я придумал это:
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