формат строки в Python неиспользуемые именованные аргументы

Позвольте'ы сказать, что у меня:

action = '{bond}, {james} {bond}'.format(bond='bond', james='james')

этот Wil выход:

'bond, james bond' 

Далее мы имеем:

 action = '{bond}, {james} {bond}'.format(bond='bond')

это будет выход:

KeyError: 'james'

Есть какой-то обходной путь, чтобы предотвратить эту ошибку, чтобы произошло, что-то вроде:

  • если keyrror: игнорировать, оставить его в покое (но не разбираем др.)
  • сравниваем строку формата с доступными именованные аргументы, если их нет, то добавить
Комментарии к вопросу (6)
Решение

Если вы используете Python 3.2+, использовать можете использовать стр.format_map().

Для облигаций, облигаций:

>>> from collections import defaultdict
>>> '{bond}, {james} {bond}'.format_map(defaultdict(str, bond='bond'))
'bond,  bond'

Для облигаций, {Джеймс} облигаций:

>>> class SafeDict(dict):
...     def __missing__(self, key):
...         return '{' + key + '}'
...
>>> '{bond}, {james} {bond}'.format_map(SafeDict(bond='bond'))
'bond, {james} bond'

В Python 2.6/2.7

Для облигаций, облигаций:

>>> from collections import defaultdict
>>> import string
>>> string.Formatter().vformat('{bond}, {james} {bond}', (), defaultdict(str, bond='bond'))
'bond,  bond'

Для облигаций, {Джеймс} облигаций:

>>> from collections import defaultdict
>>> import string
>>>
>>> class SafeDict(dict):
...     def __missing__(self, key):
...         return '{' + key + '}'
...
>>> string.Formatter().vformat('{bond}, {james} {bond}', (), SafeDict(bond='bond'))
'bond, {james} bond'
Комментарии (9)

Вы могли бы использовать [строка] шаблон1 с safe_substitute метод.

from string import Template

tpl = Template('$bond, $james $bond')
action = tpl.safe_substitute({'bond': 'bond'})
Комментарии (0)

Вы можете следовать рекомендациям в [Пеп 3101][1] и подкласс форматирования:

from __future__ import print_function
import string

class MyFormatter(string.Formatter):
    def __init__(self, default='{{{0}}}'):
        self.default=default

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            return kwds.get(key, self.default.format(key))
        else:
            return string.Formatter.get_value(key, args, kwds)

Теперь попробуйте это:

>>> fmt=MyFormatter()
>>> fmt.format("{bond}, {james} {bond}", bond='bond', james='james')
'bond, james bond'
>>> fmt.format("{bond}, {james} {bond}", bond='bond')
'bond, {james} bond'

Вы можете изменить ключевые ошибки помечаются на изменение текста в собственной.по умолчанию на что вы хотели бы показать на KeyErrors:


>>> fmt=MyFormatter('">>{{{0}}} KeyError>{james} KeyError
Комментарии (3)

Можно также сделать простой и читаемый, хотя и несколько глупо:

'{bond}, {james} {bond}'.format(bond='bond', james='{james}')

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

Комментарии (0)

falsetru'ы ответ есть умное использование недобросовестный словарь с vformat (), и чувак'ы ответ, пожалуй, больше в линии с Python's документация, но ни обрабатывать сложные имена полей (например, с помощью явного преобразования () или формат спецификации (:+10г).

Например, используя falsetru'ы SafeDict:

>>> string.Formatter().vformat('{one} {one:x} {one:10f} {two!r} {two[0]}', (), SafeDict(one=215, two=['James', 'Bond']))
"215 d7 215.000000 ['James', 'Bond'] James"
>>> string.Formatter().vformat('{one} {one:x} {one:10f} {two!r} {two[0]}', (), SafeDict(one=215))
"215 d7 215.000000 '{two}' {"

И используя кореш'ы MyFormatter:

>>> MyFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215, two=['James', 'Bond'])
"215 d7 215.000000 ['James', 'Bond'] James"
>>> MyFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215)
"215 d7 215.000000 '{two}' {"

Ни хорошо работают во втором случае за искомое значение (в число()) уже раздели спецификации форматирования. Вместо этого, вы можете переопределить vformat () " или " метод Parse () таким образом, что эти спецификации доступны. Мое решение ниже делает это путем переопределенияvformat()таким образом, она выполняет поиск ключа и если ключ отсутствует, убегает формат строки с двойными скобками (например,{{две!р}}), а затем выполняет vformat нормального()`.

class SafeFormatter(string.Formatter):
    def vformat(self, format_string, args, kwargs):
        args_len = len(args)  # for checking IndexError
        tokens = []
        for (lit, name, spec, conv) in self.parse(format_string):
            # re-escape braces that parse() unescaped
            lit = lit.replace('{', '{{').replace('}', '}}')
            # only lit is non-None at the end of the string
            if name is None:
                tokens.append(lit)
            else:
                # but conv and spec are None if unused
                conv = '!' + conv if conv else ''
                spec = ':' + spec if spec else ''
                # name includes indexing ([blah]) and attributes (.blah)
                # so get just the first part
                fp = name.split('[')[0].split('.')[0]
                # treat as normal if fp is empty (an implicit
                # positional arg), a digit (an explicit positional
                # arg) or if it is in kwargs
                if not fp or fp.isdigit() or fp in kwargs:
                    tokens.extend([lit, '{', name, conv, spec, '}'])
                # otherwise escape the braces
                else:
                    tokens.extend([lit, '{{', name, conv, spec, '}}'])
        format_string = ''.join(tokens)  # put the string back together
        # finally call the default formatter
        return string.Formatter.vformat(self, format_string, args, kwargs)

Здесь's это в действии:

>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215, two=['James', 'Bond'])
"215 d7 215.000000 ['James', 'Bond'] James"
>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215)
'215 d7 215.000000 {two!r} {two[0]}'
>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}')
'{one} {one:x} {one:10f} {two!r} {two[0]}'
>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', two=['James', 'Bond'])
"{one} {one:x} {one:10f} ['James', 'Bond'] James"

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

Комментарии (0)

Нуждаясь в том, чтобы частично заполнить строки формата является общей проблемой, когда постепенно заполняя строки формата, например, для SQL-запросов.

format_partial (метод) использует форматирования от String и АСТ, чтобы разобрать строку формата, а также выяснить, есть ли им хэш-параметр имеет все необходимые параметры для того, чтобы частично оценить формат:

import ast
from collections import defaultdict
from itertools import chain, ifilter, imap
from operator import itemgetter
import re
from string import Formatter

def format_partial(fstr, **kwargs):
    def can_resolve(expr, **kwargs):
        walk = chain.from_iterable(imap(ast.iter_fields, ast.walk(ast.parse(expr))))
        return all(v in kwargs for k,v in ifilter(lambda (k,v): k=='id', walk))

    ostr = fstr
    fmtr = Formatter()
    dd = defaultdict(int)
    fmtr.get_field = lambda field_name, args, kwargs: (dd[field_name],field_name)
    fmtr.check_unused_args = lambda used_args, args, kwargs: all(v in dd for v in used_args)
    for t in ifilter(itemgetter(1), Formatter().parse(fstr)):
        f = '{'+t[1]+(':'+t[2] if t[2] else '')+'}'
        dd = defaultdict(int)
        fmtr.format(f,**kwargs)
        if all(can_resolve(e,**kwargs) for e in dd):
            ostr = re.sub(re.escape(f),Formatter().format(f, **kwargs),ostr,count=1)
    return ostr

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

goodmami's и чувак's отвечает кажутся чище, но они оба не могут передать в формате мини-язык полностью, как в {Х:>{х}}; format_partial не будет иметь никаких проблем решить любой формат строки, строки.формат()` постановляет:

from datetime import date
format_partial('{x} {} {y[1]:x} {x:>{x}} {z.year}', **{'x':30, 'y':[1,2], 'z':date.today()})

'30 {} 2                             30 2016'

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

Комментарии (2)

Здесь'ы другой способ сделать это, используя python27:

action = '{bond}, {james} {bond}'
d = dict((x[1], '') for x in action._formatter_parser())
# Now we have: `d = {'james': '', 'bond': ''}`.
d.update(bond='bond')
print action.format(**d)  # bond,  bond
Комментарии (1)

Исходя из других ответов, я расширил решения. Это будет обрабатывать строки с форматированием спецификаций " и{А:<10}"в.

Я обнаружил, что некоторые строки из журнала селен вызывая vformat (и format_map), чтобы поразить ограничение рекурсии. Я также хотел, чтобы я мог обрабатывать строки, где существуют пустые фигурные скобки, а также.

в


def partialformat(s: str, recursionlimit: int = 10, **kwargs):
    """
    vformat does the acutal work of formatting strings. _vformat is the 
    internal call to vformat and has the ability to alter the recursion 
    limit of how many embedded curly braces to handle. But for some reason 
    vformat does not.  vformat also sets the limit to 2!   

    The 2nd argument of _vformat 'args' allows us to pass in a string which 
    contains an empty curly brace set and ignore them.
    """

    class FormatPlaceholder:
        def __init__(self, key):
            self.key = key

        def __format__(self, spec):
            result = self.key
            if spec:
                result += ":" + spec
            return "{" + result + "}"

    class FormatDict(dict):
        def __missing__(self, key):
            return FormatPlaceholder(key)

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

                # loop through the rest of the field_name, doing
                #  getattr or getitem as needed
                for is_attr, i in rest:
                    if is_attr:
                        try:
                            obj = getattr(obj, i)
                        except AttributeError as exc:
                            pass
                    else:
                        obj = obj[i]

            return obj, first

    fmttr = string.Formatter()
    fs, _ = fmttr._vformat(s, ("{}",), FormatDict(**kwargs), set(), recursionlimit)
    return fs

class ColorObj(object):
    blue = "^BLUE^"
s = '{"a": {"b": {"c": {"d" : {} {foo:
Комментарии (0)

Для Python 3, принимая утвержденного ответа, это хороший, плотный, подходящие для Python реализации:

def safeformat(str, **kwargs):
    class SafeDict(dict):
        def __missing__(self, key):
            return '{' + key + '}'
    replacements = SafeDict(**kwargs)
    return str.format_map(replacements)

# In [1]: safeformat("a: {a}, b: {b}, c: {c}", a="A", c="C", d="D")
# Out[1]: 'a: A, b: {b}, c: C'
Комментарии (1)