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)
是否有一些解决方法可以防止此错误发生,例如:
fal*_*tru 80
用于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)
用于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)
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+上保持不变
人们也可以做到简单易读,尽管有点傻:
'{bond}, {james} {bond}'.format(bond='bond', james='{james}')
Run Code Online (Sandbox Code Playgroud)
我知道这个答案需要知道预期的键,但我正在寻找一个简单的两步替换(首先说问题名称,然后是循环中的问题索引),创建一个完整的类或不可读的代码比需要的更复杂.
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),但应该适用于更多的格式化字符串.