带有负索引的str.format(list)在Python中不起作用

问题描述 投票:0回答:5

我在替换字段中使用负索引来输出格式化列表,但它引发了 TypeError。代码如下:

>>> a=[1,2,3]
>>> 一个[2]
3
>>> 一个[-1]
3
>>> '最后一个:{0[2]}'.format(a)
'最后:3'
>>> '最后一个:{0[-1]}'.format(a)
回溯(最近一次调用最后一次):
  文件“”,第 1 行,位于
类型错误:列表索引必须是整数,而不是 str
python string formatting
5个回答
16
投票

这就是我所说的格式字符串规范中的设计故障。根据 文档

element_index     ::=  integer | index_string

但是,唉,

-1
不是“整数”——它是一个表达式。一元减运算符甚至没有特别高的优先级,因此,例如
print(-2**2)
发出
-4
——另一个常见问题,可以说是一个设计故障(
**
运算符具有更高的优先级,因此 raise-to -power 首先发生,然后是较低优先级一元请求的更改符号
-
)。

格式字符串中该位置的任何非整数(例如表达式)都被视为字符串,以索引 dict 参数 - 例如:

$ python3 -c "print('The last:{0[2+2]}'.format({'2+2': 23}))"
The last:23

不确定这是否值得在 Python trac 中提出问题,但这确实是一个有点令人惊讶的行为:-(。


4
投票

一旦你开始挖掘,这里就会出现一些问题:

有问题的项目称为“element_index”,它被定义为一个整数。

问题1:除非用户点击“integer”链接到语言参考手册,否则他们不会知道-1被认为是一个表达式,而不是一个整数。顺便说一句,任何想说“按照文档记录工作”的人都应该首先看到问题 7 :-)

首选解决方案:更改定义,以便“element_index”在整数之前可以有一个可选的“-”。

它是一个整数,对吗?没那么快......后来文档说“'[index]'形式的表达式使用

__getitem__()
进行索引查找”

问题 3:应该说“[element_index]”(索引未定义)。

问题 4:并不是每个人都清楚

__getitem__()
的作用。需要更清晰的文档。

所以我们可以在这里使用字典和整数,可以吗?是的,有一两个问题:

element_index 是整数吗?是的,这适用于字典:

>>> "{0[2]}".format({2: 'int2'})
'int2'

看来我们也可以使用非整数字符串,但这需要更明确的文档(问题5):

>>> "{0[foo]}".format({'foo': 'bar'})
'bar'

但是我们不能使用带有“2”这样的键的字典(问题 6):

>>> "{0[2]}".format({'2': 'str2'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 2
>>> "{0['2']}".format({'2': 'str2'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: "'2'"

问题 7:“整数”实际上应该记录为“十进制整数”...0x22 和 0b11 被视为 str,010(“八进制整数”)被视为 10,而不是 8:

>>> "{0[010]}".format('0123456789abcdef')
'a'

更新: PEP 3101讲述真实的故事:
”“”
解析项目键的规则非常简单。如果以数字开头,则将其视为数字,否则将其视为字符串。

由于键不是用引号分隔的,因此无法从格式字符串中指定任意字典键(例如,字符串“10”或“:-]”)。
“”“


1
投票

正确,它不起作用。解决方案:

>>> 'The last:{0}'.format(a[-1])
'The last:3'

0
投票

我经常使用 Python 格式字符串作为配置选项 - 格式字符串提供特定的、已知的关键字参数列表。因此,在格式字符串中向前或向后寻址可变长度列表的索引正是我最终需要的。

我刚刚编写了这个 hack 来使负索引工作:

string_to_tokenise = "Hello_world"
tokens = re.split(r"[^A-Z\d]+", string_to_tokenise, flags=re.I)
token_dict = {str(i) if i < 0 else i: tokens[i] for i in range(-len(tokens) + 1, len(tokens))}
print "{thing[0]} {thing[-1]}".format(thing=token_dict)

结果:

Hello world

因此,为了解释一下,我没有传入标记列表,而是创建了一个字典,其中包含用于索引从 0 到 len(..)-1 的列表所需的所有整数键,并且还添加了用于索引的负整数键结尾从 -1 到 -(len(..)-1),但是这些键从整数转换为字符串,因为这就是 format 解释它们的方式。


0
投票

扩展内置

string.Formatter
来允许这种情况相对简单。下面的代码在现有实现中添加了 2 行,以将负索引解析为整数。

from _string import formatter_field_name_split
from string import Formatter as BuiltinFormatter


class Formatter(BuiltinFormatter):
    """
    Implements negative indexing for format fields.
    """

    def get_field(self, field_name, args, kws):
        # eg: field_name = '0[name]' or 'label.title' or 'some_keyword'
        # OR 'key[-1]' implimented here
        first, rest = formatter_field_name_split(field_name)
        obj = self.get_value(first, args, kws)

        # loop through the rest of the field_name, doing
        #  getattr or getitem as needed
        for is_attr, i in rest:
            if is_attr:
                obj = getattr(obj, i)
            else:
                # fix to allow negative indexing next two lines :)
                if i.startswith('-') and i[1:].isdigit():
                    i = int(i)
                obj = obj[i]

        return obj, first


# test
if __name__ == '__main__':
    formatter = Formatter()
    print(formatter.format('hello {worlds[-1]}!', 
                            worlds=('earth', 'mars', ..., 'world')))

哪个输出

hello world!
根据需要。

© www.soinside.com 2019 - 2024. All rights reserved.