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}' {"
使用 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}' {"
由于值查找(在get_value()
中)已经剥离了格式规范,因此两者都不适用于第二种情况。相反,您可以重新定义vformat()
或parse()
,以便这些规范可用。我的解决方案通过重新定义vformat()
以执行关键字查找,并且如果键缺失,则使用双括号转义格式字符串(例如{{two!r}}
),然后执行正常的vformat()
。
class SafeFormatter(string.Formatter):
def vformat(self, format_string, args, kwargs):
args_len = len(args)
tokens = []
for (lit, name, spec, conv) in self.parse(format_string):
lit = lit.replace('{', '{{').replace('}', '}}')
if name is None:
tokens.append(lit)
else:
conv = '!' + conv if conv else ''
spec = ':' + spec if spec else ''
fp = name.split('[')[0].split('.')[0]
if not fp or fp.isdigit() or fp in kwargs:
tokens.extend([lit, '{', name, conv, spec, '}'])
else:
tokens.extend([lit, '{{', name, conv, spec, '}}'])
format_string = ''.join(tokens)
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(也许重新定义parse()
会减少一些修补),但应该适用于更多的格式化字符串。
bond, bond
/bond, {james}, bond
? - falsetru