python格式字符串未使用的命名参数

nel*_*ela 48 python string string-formatting missing-data defaultdict

比方说我有:

action = '{bond}, {james} {bond}'.format(bond='bond', james='james')
Run Code Online (Sandbox Code Playgroud)

这个输出:

'bond, james bond' 
Run Code Online (Sandbox Code Playgroud)

接下来我们有:

 action = '{bond}, {james} {bond}'.format(bond='bond')
Run Code Online (Sandbox Code Playgroud)

这将输出:

KeyError: 'james'
Run Code Online (Sandbox Code Playgroud)

是否有一些解决方法可以防止此错误发生,例如:

  • 如果keyrror:忽略,别管它(但要解析别人)
  • 比较格式字符串和可用的命名参数,如果缺少则添加

fal*_*tru 80

如果您使用的是Python 3.2+,请使用str.format_map().

用于bond, bond:

>>> from collections import defaultdict
>>> '{bond}, {james} {bond}'.format_map(defaultdict(str, bond='bond'))
'bond,  bond'
Run Code Online (Sandbox Code Playgroud)

用于bond, {james} bond:

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

在Python 2.6/2.7中

用于bond, bond:

>>> from collections import defaultdict
>>> import string
>>> string.Formatter().vformat('{bond}, {james} {bond}', (), defaultdict(str, bond='bond'))
'bond,  bond'
Run Code Online (Sandbox Code Playgroud)

用于bond, {james} 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'
Run Code Online (Sandbox Code Playgroud)

  • 这会丢失任何格式,例如`"{bond:<5}"'`。 (2认同)

Mar*_*ard 22

您可以在方法中使用模板字符串safe_substitute.

from string import Template

tpl = Template('$bond, $james $bond')
action = tpl.safe_substitute({'bond': 'bond'})
Run Code Online (Sandbox Code Playgroud)


daw*_*awg 10

您可以按照PEP 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:
            return string.Formatter.get_value(key, args, kwds)
Run Code Online (Sandbox Code Playgroud)

现在尝试一下:

>>> fmt=MyFormatter()
>>> fmt.format("{bond}, {james} {bond}", bond='bond', james='james')
'bond, james bond'
>>> fmt.format("{bond}, {james} {bond}", bond='bond')
'bond, {james} bond'
Run Code Online (Sandbox Code Playgroud)

您可以通过将文本更改为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'
Run Code Online (Sandbox Code Playgroud)

代码在Python 2.6,2.7和3.0+上保持不变


Ioa*_*dis 8

人们也可以做到简单易读,尽管有点傻:

'{bond}, {james} {bond}'.format(bond='bond', james='{james}')
Run Code Online (Sandbox Code Playgroud)

我知道这个答案需要知道预期的键,但我正在寻找一个简单的两步替换(首先说问题名称,然后是循环中的问题索引),创建一个完整的类或不可读的代码比需要的更复杂.


goo*_*ami 6

falsetru的答案巧妙地使用了一个默认字典vformat(),而dawg的答案可能更符合Python的文档,但是它们都没有处理复合字段名称(例如,使用显式转换(!r)或格式规范(:+10g).

例如,使用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}' {"
Run Code Online (Sandbox Code Playgroud)

并使用dawg的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}' {"
Run Code Online (Sandbox Code Playgroud)

在第二种情况下都不能正常工作,因为值查找(in get_value())已经删除了格式规范.相反,你可以重新定义vformat()parse()使这些规格可供选择.我的解决方案通过重新定义vformat()来执行此操作,因此它执行密钥查找,如果缺少密钥,则使用双括号(例如{{two!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)
Run Code Online (Sandbox Code Playgroud)

这是它的实际应用:

>>> 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"
Run Code Online (Sandbox Code Playgroud)

这个解决方案有点太hacky(可能重新定义parse()会有更少的kludges),但应该适用于更多的格式化字符串.