строка формата питона неиспользованные параметры, передаваемые по имени

Скажем, я имею:

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

этот wil произвел:

'bond, james bond' 

Затем мы имеем:

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

это произведет:

KeyError: 'james'

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

  • , если keyrror: проигнорируйте, оставьте его в покое (но действительно разберите других)
  • сравнивает строку формата с доступными параметрами, передаваемыми по имени, если пропавшие без вести тогда добавляют
38
Второй лучше, я думаю. Сначала можно создать странное содержание.. второй заставляет людей думать "эй, что-то неправильно здесь" который в этом случае в хорошей вещи
добавлено автор nelsonvarela, источник
добавлено автор dreftymac, источник
Какой, вы хотите связь, связь / связь, {james}, связь ?
добавлено автор falsetru, источник
Я обновил ответ для обоих случаев.
добавлено автор falsetru, источник
stackoverflow.com/questions/5466451/…
добавлено автор Qlimax, источник

9 ответы

При использовании Пайтона 3.2 +, использование может использовать str.format_map ().

Для связь, связь :

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

Для связь, {james} связь :

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

У питона 2.6/2.7

Для связь, связь :

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

Для связь, {james} связь :

>>> 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'
60
добавлено
I' m использование 2.7.. Спасибо! I' m собирающийся проверять это
добавлено автор nelsonvarela, источник
Большой! Принятый!
добавлено автор nelsonvarela, источник
Достаточно странно в python3 формат дает тот же самый результат как format_map в этом примере.
добавлено автор frnhr, источник
Здесь это: ' {связь}, {james} {связь} ' .format (** SafeDict (связь =' bond')) прибыль ' связь, {james} bond' . Отметьте ** . Я на самом деле didn' t замечают его до сих пор:) Питон 3.4.3
добавлено автор frnhr, источник
@frnhr, вы могли показать конкретный пример, который вы попробовали?
добавлено автор falsetru, источник
@frnhr, я вижу. Спасибо за вашу обратную связь.
добавлено автор falsetru, источник
@frnhr, Что вы имеете в виду? ' {связь}, {james} {связь} ' .format (defaultdict (str, связь =' bond')) дает мне KeyError .
добавлено автор falsetru, источник

Вы могли использовать a последовательность шаблона с safe_substitute метод.

from string import Template

tpl = Template('$bond, $james $bond')
action = tpl.safe_substitute({'bond': 'bond'})
17
добавлено

Можно следовать рекомендации в БОДРОСТЬ ДУХА 3101 и подкласс Средство форматирования:

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:
            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'

Можно измениться, как ключевые ошибки сигнализируются, изменяя текст в self.default к тому, что требуется показать для KeyErrors:

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

Кодовые работы, неизменные на Пайтоне 2.6, 2.7, и 3.0 +

9
добавлено
Я думаю Средство форматирования get_value (ключ, args, kwds) должен быть возвращаемая строка. Средство форматирования get_value (сам, ключ, args, kwds) в вашем коде
добавлено автор Grijesh Chauhan, источник
Из ответов, если здесь я думаю, что это является лучшим с точки зрения мобильности/элегантности +1
добавлено автор Ajay, источник
@GrijeshChauhan я не уверен … функция, называют рекурсивно, единственное реальное возвращение происходит в последнем, предельном, звоните так … Так или иначе, мне не удалось получить его работа. Я закончил с более простым кодом, которые делают то, что я хочу. класс URLFormatter (последовательность. Средство форматирования): определение __ init __ (сам, дефолт =' {} '): сам default=default определение get_value (сам, ключ, args, kwds): возвратите kwds.get (ключ, self.default.format (ключ))
добавлено автор Stéphane, источник

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

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

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

6
добавлено

falsetru's answer has a clever use of a defaulting dictionary with vformat(), and dawg's answer is perhaps more in-line with Python's documentation, but neither handle compound field names (e.g., with explicit conversion (!r) or format specs (:+10g).

Например, использование SafeDict falsetru:

>>> 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 dawg:

>>> 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}' {"

Никакая работа хорошо во втором случае, потому что поиск стоимости (в get_value() ) уже раздел технические требования форматирования. Вместо этого можно пересмотреть vformat() или разбор() , таким образом, эти технические требования доступны. Мое решение ниже делает это, пересматривая vformat() , таким образом, это выполняет ключевой поиск и, если ключ отсутствует, избегает строки формата с двойными фигурными скобками (например, {{ два! r }} ), и затем выполняет нормальное 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)

Вот это в действии:

>>> 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"

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

5
добавлено

Вот другой способ сделать это, используя 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
2
добавлено
Некоторые могут передумать относительно использования _formatter_parser , но мне это - большая часть подхода pythonic: простой, легкий постигать, использование функциональности коробки, и если вы изменяете вторую линию к d = dict ((x [1], ' {' +str (x [1]) + '} ') для x в действии. _ formatter_parser ()) можно получить связь, {james} связь формат так же легко как связь, связь формат.
добавлено автор hlongmore, источник

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

format_partial() method uses the Formatter from string and ast to parse the format string and also find out whether the named parameter hash has all the values needed to partially evaluate the format:

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 will leave the unresolved portion of the format string, so subsequent calls can be used to resolve those parts as the data is available.

goodmami's and dawg's answers seem cleaner, but they both fail to capture the format mini-language completely as in {x:>{x}}; format_partial will have no problem resolving any format string that string.format() resolves:

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'

Еще легче расширить функциональность на старые строки формата стиля, используя regex вместо средства форматирования последовательности, поскольку старые подстроки формата стиля были обычны (т.е. никакие вложенные маркеры).

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

Для Питона 3, беря одобренный ответ, это - хорошее, трудное, внедрение Pythonic:

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'
0
добавлено
К сожалению, этот won' t обращаются со случаем последовательности " {a: <10} ".
добавлено автор Marcel Wilson, источник

Based on some of the other answers, I expanded the solutions. This will handle strings with formatting spec "{a:<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:<12} & {foo!r} {arg} {color.blue:<10} {color.pink} {blah.atr} }}}}'
print(partialformat(s, foo="Fooolery", arg="ARRrrrrrg!", color=ColorObj))

продукция:

{"a": {"b": {"c": {"d" : {} Fooolery             & 'Fooolery' Fooolery ARRrrrrrg! ^BLUE^ {color.pink} {blah.atr} }}}}
0
добавлено
Python
Python
7 654 участник(ов)

Уютный чат для профессионалов, занимающихся поиском питоньих мудростей. Как не получить бан: https://t.me/ru_python/577926

Python beginners
Python beginners
4 449 участник(ов)

Вопросы про Python для чайников. Cпам и троллинг неприемлем. Не злоупотребляйте стикерами. Частозадаваемые вопросы: https://github.com/ru-python-beginners/faq/blob/master/README.md Статистика тут: https://grstats.me/chat/x4qym2k5uvfkr3al6at7

pro.python
pro.python
1 090 участник(ов)

Сообщество разработчиков под Python Создатель: @rodgelius

Rude Python
Rude Python
971 участник(ов)

Python без „девочек”, здесь матерятся и унижают Django. Not gay friendly. Правила: t.me/rudepython/114107 @rudepython | t.me/rudepython

rupython
rupython
509 участник(ов)

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

Python-programming
Python-programming
266 участник(ов)

Чат группы вконтакте https://vk.com/python_community