Python 中奇怪的切片行为

Strange slicing behavior in Python

提问人:mrwonderfulness 提问时间:8/19/2022 最后编辑:Rodrigo Rodriguesmrwonderfulness 更新时间:8/20/2022 访问量:146

问:

我觉得这有点奇怪。谁能向我解释一下:为什么,如果我有这样的列表:

l = [1, 2, 3, 4, 5]

我做这个重新分配:

l[-1:0] = [99,]

然后它将 99 插入 5 的左侧,如下所示:

[1, 2, 3, 4, 99, 5]

我们必须赋值给一个可迭代对象,只赋值整数会给出一个错误,这可能是这里发生的事情的线索......我想?[99,]99

列表 可变

评论

0赞 h0r53 8/19/2022
Python 的列表数据结构是使用动态数组实现的。我相信这里发生的事情是您正在尝试插入列表的“切片”,这基本上是数组的子数组。底层动态数组说:“哦,你想在索引处插入一些指向数组最后一个元素的东西吗?好的,我会将当前元素移到右侧,然后插入您的数据”
0赞 h0r53 8/19/2022
为什么有效而无效?因为要为其赋值的对象是 type 而不是 type 。因此,从技术上讲,您正在做的是在原始列表的特定位置执行列表连接。[99,]99listint

答:

0赞 Pepe Salad 8/19/2022 #1

首先,您需要了解切片是如何完全工作的。可以有 3 个值:[start:end:step]

在代码中,使用 as 值将瞄准列表中的最后一个值,但由于设置了值,它也会尝试查找值列表。这就是为什么您必须使用列表来更改值的原因。l[-1:0]-1startend

若要解决此问题,请将列表的最后一个值更改为 (假设这是预期结果)l[-1] = 9999

评论

3赞 h0r53 8/19/2022
[99,]不是元组。我不认为 OP 是询问如何更改列表的最终值。我认为他们要求澄清为什么他们会看到他们分享的行为。
0赞 Pepe Salad 8/19/2022
我在词汇上犯了错误,但我的回答确实解决了为什么他们会看到他们分享的行为,至少在我理解的范围内是这样。
1赞 h0r53 8/20/2022 #2

当你用一个 Python 列表切片时,你最终会得到另一个列表。示例中新列表的开头是原始列表中最后一个元素的位置。仅仅因为您的示例中的值为 它并不意味着这只是一个空列表。它的值是一个空列表,但对的引用是现有列表中的切片列表。[start:end]l[-1:0][]l[-1:0]

所以在你的例子中

l = [1,2,3,4,5]
#            ^       l[-1:0] starts here but the size is 0

将一个列表分配给另一个列表的切片位置时,您实际上是在执行列表连接。 这相当于你正在做的事情。

l=[1,2,3,4,5]
l[4:0] = [99] # concatenate a new list at the position of the final element in the original list

# A different way to look at it
l[0:4] + [99] + l[4:]

结果再次有效地将两个列表连接起来。更具体地说,您正在将新列表的元素插入到原始列表中的指定位置。使用文本整数(如 )将导致错误,因为您无法直接连接 int 和 list。然而,正如其他人所指出的,切片赋值可以使用任何可迭代的,因此工作原理相同。99l[-1,0] = (99,)

评论

1赞 slothrop 8/20/2022
请注意,切片分配可以使用任何可迭代,而列表串联则不能。所以很好(元组是可迭代的),但给出一个 TypeError。l[4:0] = (99,)l[0:4] + (99,) + l[4:]
0赞 h0r53 8/20/2022
@slothrop感谢您提供更多详细信息。我已经更新了答案以包含此信息。
1赞 Ignatius Reilly 8/20/2022
我喜欢这种解释,这种务实的思考方式是将问题视为串联。但我与“在你的例子中并不意味着这只是一个空列表”的说法非常矛盾。这是现有列表位置的空范围“。 produce ,而不是 ,它产生 和 它实际上是一个“空”范围(例如[]type(l[-1:0])listtype(range(0))rangefor i in range(0): print(i))
0赞 h0r53 8/20/2022
@IgnatiusReilly是的,我同意我本可以用更好的措辞。 确实是一个空列表,但不仅仅是一个空列表,它还是对现有列表中特定切片的引用。正如你所指出的,范围在 Python 中确实意味着其他东西。我会更新得更简洁一些。[]l[-1:0]
3赞 Rodrigo Rodrigues 8/20/2022 #3

根据官方文档

在表中,s 是可变序列类型的实例,t 是任何可迭代对象,x 是满足 s 施加的任何类型和值限制的任意对象

enter image description here

注(1):t的长度必须与它所替换的切片相同。

因此,如果您这样做,则将位置 -1(这意味着比最后一个少一个)中的项目替换为值 99。l[-1] = 99

另一方面,如果你这样做,你把索引 -1 到 0 的元素(这意味着,在位置 -1 处的长度为 0 的切片),并用 [99,] 的元素替换它们(这就是为什么它必须是可迭代的),这实际上是一个语义插入。l[-1:0] = [99,]

评论

0赞 mrwonderfulness 8/20/2022
啊,是的......明白了。既然 Python 知道既然我使用 -1 作为索引,那么它就不能是切片的开始......所以它(正确地)假设这是结束,因为我们不能向后切片(除非我们使用负步进)。因此,假设 中的 0 是步进,而不是要切片的索引。明白了。谢谢。l[-1:0]