让我说我有:
action = '{bond}, {james} {bond}'.format(bond='bond', james='james')
这个输出:
'bond, james bond'
接下来我们有:
action = '{bond}, {james} {bond}'.format(bond='bond')
这将输出:
KeyError: 'james'
是否有一些解决方法可以防止此错误发生,例如:
答案 0 :(得分:69)
bond, bond
:
>>> from collections import defaultdict
>>> '{bond}, {james} {bond}'.format_map(defaultdict(str, bond='bond'))
'bond, bond'
bond, {james} bond
:
>>> class SafeDict(dict):
... def __missing__(self, key):
... return '{' + key + '}'
...
>>> '{bond}, {james} {bond}'.format_map(SafeDict(bond='bond'))
'bond, {james} bond'
bond, bond
:
>>> from collections import defaultdict
>>> import string
>>> string.Formatter().vformat('{bond}, {james} {bond}', (), defaultdict(str, bond='bond'))
'bond, bond'
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'
答案 1 :(得分:19)
您可以使用safe_substitute
方法使用template string。
from string import Template
tpl = Template('$bond, $james $bond')
action = tpl.safe_substitute({'bond': 'bond'})
答案 2 :(得分:10)
您可以按照PEP 3101中的建议和子类格式化程序:
Dim lines As New List(Of String)
For Each row As DataGridViewRow In myDataGridView.SelectedRows
lines.Add(row.Cells(1).Value.ToString())
Next
myTextBox.Lines = lines.ToArray()
现在尝试一下:
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显示的内容来更改标记错误的方式:
self.default
代码在Python 2.6,2.7和3.0 +上保持不变
答案 3 :(得分:8)
人们也可以做到简单易读,虽然有些愚蠢:
'{bond}, {james} {bond}'.format(bond='bond', james='{james}')
我知道这个答案需要知道预期的密钥, 但我正在寻找一个简单的两步替换(首先说问题名称,然后是循环中的问题索引),创建一个完整的类或不可读的代码比需要的更复杂。
答案 4 :(得分:6)
falsetru's answer巧妙地使用vformat()
的默认字典,而dawg's answer可能更符合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}}
)转义格式字符串,然后执行正常{{1} }。
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)
这个解决方案有点太hacky(可能重新定义>>> 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"
会有更少的kludges),但应该适用于更多格式化字符串。
答案 5 :(得分: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
答案 6 :(得分:1)
当逐步填充格式字符串时,需要部分填充格式字符串是常见问题,例如,用于SQL查询。
format_partial()
方法使用Formatter
和string
中的ast
来解析格式字符串,并查看命名参数哈希是否具有部分评估所需的所有值格式:
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和dawg的答案似乎更清晰,但它们都无法像{x:>{x}}
那样完全捕捉格式迷你语言;解析format_partial
解析的任何格式字符串时,string.format()
都没有问题:
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'
使用正则表达式而不是字符串格式化程序将功能扩展到旧样式格式字符串更加容易,因为旧样式格式子字符串是常规的(即没有嵌套标记)。
答案 7 :(得分:0)
对于Python 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'
答案 8 :(得分:0)
基于其他一些答案,我扩展了解决方案。
这将处理格式为"{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} }}}}