在使用kwargs时,如何在Python format()中转义冒号?

在使用kwargs时,如何在Python format()中转义冒号?,python,string-formatting,Python,String Formatting,我想打印一本带冒号的字典。不幸的是冒号字符用于格式化,所以我需要以某种方式对其进行转义 例如: >>> d = {'hello': 'world', 'with:colon': 'moo'} >>> '{hello}'.format(**d) 'world' >>> '{with:colon}'.format(**d) KeyError: 'with' >>> '{with\:colon}'.format(**d) K

我想打印一本带冒号的字典。不幸的是冒号字符用于格式化,所以我需要以某种方式对其进行转义

例如:

>>> d = {'hello': 'world', 'with:colon': 'moo'}

>>> '{hello}'.format(**d)
'world'

>>> '{with:colon}'.format(**d)
KeyError: 'with'

>>> '{with\:colon}'.format(**d)
KeyError: 'with\\'

>>> '{with::colon}'.format(**d)
KeyError: 'with'
据我所知,你所要求的是根本不可能的。具体来说,

由于arg_name不以引号分隔,因此无法在格式字符串中指定任意字典键(例如字符串
'10'
':-]'


不能这样做-键必须在语法上等同于Python标识符。请参见文档中的:

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
arg_name          ::=  [identifier | integer]
attribute_name    ::=  identifier
class QuotableFormatter(string.Formatter):
    def __init__(self):
        self.super = super(QuotableFormatter, self)
        self.super.__init__()
        self.quotes = {}

    def parse(self, format_string):
        fs = ''
        for p in re.findall(r'(?:".+?")|(?:[^"]+)', format_string):
            if p[0] == '"':
                key = '_q_' + str(len(self.quotes))
                self.quotes[key] = p[1:-1]
                fs += key
            else:
                fs += p
        return self.super.parse(fs)

    def get_field(self, field_name, args, kwargs):
        if field_name.startswith('_q_'):
            field_name = self.quotes[field_name]
        return self.super.get_field(field_name, args, kwargs)
正如他在回答中指出的那样,这是不可能的

解决方法是用有效的密钥替换密钥:

d_sanitised = {key.replace(":", "-"): value for key, value in d.items()}
当然,如果可能与其他键发生冲突,您可能需要小心

>>> d = {'hello': 'world', 'with:colon': 'moo'}
>>> d_sanitised = {key.replace(":", "-"): value for key, value in d.items()}
>>> '{with-colon}'.format(**d_sanitised)
'moo'
显然,这假设您可以修改格式字符串以适应。理想情况下,只需修改两端以避免在一起使用冒号。

作为一种解决方法:

>>> d = {'hello': 'world', 'with:colon': 'moo'}
>>> '{hello} {}'.format(d['with:colon'],**d)
'world moo'
>>> '{hello} {0}'.format(d['with:colon'],**d)
'world moo'

遗憾的是,内置格式化程序不允许这样做。一个明显的语法扩展是允许在必要时引用键。您的格式字符串如下所示:

format('{"with:colon"} and {hello}'
幸运的是,扩展格式化程序以提供这种语法似乎很容易,下面是一个POC实现:

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
arg_name          ::=  [identifier | integer]
attribute_name    ::=  identifier
class QuotableFormatter(string.Formatter):
    def __init__(self):
        self.super = super(QuotableFormatter, self)
        self.super.__init__()
        self.quotes = {}

    def parse(self, format_string):
        fs = ''
        for p in re.findall(r'(?:".+?")|(?:[^"]+)', format_string):
            if p[0] == '"':
                key = '_q_' + str(len(self.quotes))
                self.quotes[key] = p[1:-1]
                fs += key
            else:
                fs += p
        return self.super.parse(fs)

    def get_field(self, field_name, args, kwargs):
        if field_name.startswith('_q_'):
            field_name = self.quotes[field_name]
        return self.super.get_field(field_name, args, kwargs)
用法:

d = {'hello': 'world', 'with:colon': 'moo', "weird!r:~^20": 'hi'}
print QuotableFormatter().format('{"with:colon":*>20} and {hello} and {"weird!r:~^20"}', **d)
# *****************moo and world and hi

从python 3.6开始,您可以使用新的f字符串格式解决此问题:

>>> d = {'hello': 'world', 'with:colon': 'moo'}
>>> print(f"with:colon is equal to {d['with:colon']}")
with:colon is equal to moo

{'with!惊呼:某物}
无法取消显示机器人笑脸
:-]
+1,很好的解决方案-不幸的是,它使语法变得笨拙,但如果这是OP代码中的常见问题,这是迄今为止最好的解决方案。@Lattyware:是的,也许转义会更好,如
{with\:colon}