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

Posted

技术标签:

【中文标题】带有负索引的 str.format(list) 在 Python 中不起作用【英文标题】:str.format(list) with negative index doesn't work in Python 【发布时间】:2021-06-14 13:46:08 【问题描述】:

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

>>> a=[1,2,3] >>> 一个[2] 3 >>> 一个[-1] 3 >>> '最后一个:0[2]'.format(a) '最后一个:3' >>> '最后一个:0[-1]'.format(a) 回溯(最近一次通话最后): 文件“”,第 1 行,在 TypeError:列表索引必须是整数,而不是 str

【问题讨论】:

听起来像是对格式函数定义的疏忽 Python 错误库中的相关错误 - bugs.python.org/issue7951。简而言之,由于实施此问题的副作用以及它可能导致错误代码的事实,该问题被视为文档错误。 【参考方案1】:

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

我刚刚编写了这个技巧来使负索引起作用:

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 将如何解释它们的方式。

【讨论】:

【参考方案2】:

这就是我所说的格式字符串规范中的设计故障。每the docs,

element_index     ::=  integer | index_string

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

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

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

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

【讨论】:

有趣!至于 -2**2 等于 -4,我认为这是一件非常好的事情,因为这是数学中使用的约定。像 element_index ::= "signed_integer" 这样的东西有意义吗?【参考方案3】:

这里有几个问题,一旦你开始挖掘:

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

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

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

这是一个整数,对吧?没那么快……后来文档说“'[index]' 形式的表达式使用__getitem__() 进行索引查找”

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

问题 4:不是每个人都知道__getitem__() 做了什么。需要更清晰的文档。

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

element_index 是一个整数?是的,这适用于 dict:

>>> "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”或“:-]”)。 """

【讨论】:

【参考方案4】:

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

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

【讨论】:

这引出了一个问题:既然可以在 args 中完成所有这些以及更多工作,为什么还要费心在格式字符串中设计受限序列索引、受限 dict 查找和属性查找工具?

以上是关于带有负索引的 str.format(list) 在 Python 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

C++ 不允许带有负索引的向量?

使用 ix() 方法对带有负索引的 pandas DataFrame 进行切片

Python中str.format()字典及list传入详解

如何用str.format()批量生成网址以豆瓣读书为例

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

使用负索引调整大小的列表切片分配[重复]