Python中使用带有整数键的字典进行字符串格式化(format())

18

我想使用Python字符串的format()作为快速且简单的模板。但是,我想要使用的dict具有其键是整数的字符串表示形式。以下是一个简化的示例:

s = 'hello there {5}'
d = {'5': 'you'}
s.format(**d)

上述代码会抛出以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range

能否做到上述要求?


5
注意,不要使用 str 作为变量名,因为这会覆盖内置的 str 类。 - Volatility
1
如果键实际上是整数,{[5]} 将起作用。 - Blender
6个回答

28

我们已确定它不起作用,但有没有解决方案:

尽管在这种情况下 str.format 无法使用,有趣的是旧的格式化将可行。这并不推荐,但您确实要求一个快速而简单的模板。

>>> 'hello there %(5)s' % {'5': 'you'}
'hello there you'

请注意,这并不适用于整数键。

>>> 'hello there %(5)s' % {5: 'you'}

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    'hello there %(5)s' % {5: 'you'}
KeyError: '5'

8
我喜欢扩展Formatter的想法,使其允许任意字段名称(整数,带有冒号的字段名称等)。实现可能如下所示:

我喜欢扩展Formatter的想法,使其允许任意字段名称(整数,带有冒号的字段名称等)。实现可能如下所示:

import string, re

class QuFormatter(string.Formatter):
    def _quote(self, m):
        if not hasattr(self, 'quoted'):
            self.quoted = {}
        key = '__q__' + str(len(self.quoted))
        self.quoted[key] = m.group(2)
        return '{' + m.group(1) + key + m.group(3) + '}'

    def parse(self, format_string):
        return string.Formatter.parse(self,
            re.sub(r'{([^}`]*)`([^}`]*)`([^}]*)}', self._quote, format_string))

    def get_value(self, key, args, kwargs):
        if key.startswith('__q__'):
            key = self.quoted[key]
        return string.Formatter.get_value(self, key, args, kwargs)

使用方法:

d = {'5': 'you', '6': 'me', "okay":1, "weird:thing!": 123456}
print QuFormatter().format(
     'hello there {`5`} {`6`:20s}--{okay}--{`weird:thing!`:20,d}', 
     **d)

所以用反引号包裹的字段会被视为文字字面量。

7
请参考此文获取解决方案。似乎在格式化字符串(文档链接)中,不能使用由数字组成的字符串作为字典键。
如果您可以使用除了5之外的键,则可以解决问题:
my_string='hello there {spam:s}'
d={'spam': 'you'}
print my_string.format(**d) # Returns "hello there you"

3
链接答案中最重要的部分是来自文档的引用:“因为arg_name没有引号定界,所以无法在格式字符串中指定任意字典键(例如字符串'10'或':-]')。 - Blckknght

3

来自PEP 3101

内置的字符串类(以及2.6中的unicode类)将会增加一个新的方法,’format‘,该方法接受任意数量的位置参数和关键字参数:

"The story of {0}, {1}, and {c}".format(a, b, c=d)

在格式字符串内,每个位置参数都有一个编号,从零开始计数,因此在上面的示例中,“a”是参数0,“b”是参数1。每个关键字参数都由其关键字名称标识,因此在上面的示例中,“c”用于引用第三个参数。
str.format中使用的数字值为位置参数。因此你不能这样做。
你可以从这里访问PEP 3101。相关部分位于“字符串方法”下面。
正如@Volatility所提到的,你可以使用%格式化器来完成这个任务。

1
您可以在自定义的 string.Formatter 中使用 get_value 进行处理,在尝试使用字典键替换字段之前,回退到索引 arg 键 - 注意这里可能存在优先级和意图的冲突... 所以并不完全建议使用,但这是一个可行的想法:
import string

class MyFormatter(string.Formatter):
    def get_value(self, key, args, kwargs):
        try:
            return kwargs[str(key)]
        except KeyError:
            return super(MyFormatter, self).get_value(key, args, kwargs)

s = 'hello there {5} - you are number {0}'
d = {'5': 'you'}
print MyFormatter().format(s, 1, 2, 3, **d)
# hello there you - you are number 1

0

实际上,使用事实{k}寻找第(k+1)个位置参数是可能的。

def populate_list(d):
   """ Return a list l verifying l[k] = d[str(k)] for each natural k """
   return [d.get(str(k)) for k in range(1 + max(map(int, d)))] if d else []

def format_with_int_keys(s,d):
   """ Replace each {k} in s by d[str(k)] """
   return s.format(*populate_list(d))

s = 'hello there {5}'
d = {'5': 'you'}
print (format_with_int_keys(s,d))

编辑:这实际上是@wim方案的详细版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接