在Python中追加列表的解释和首选方法

3

我正在学习Python,以前是做C#/Java的开发工作,最近在研究列表的行为。我已经阅读了一些文档,但我不明白为什么在长度-1之外的索引上使用切片会导致可以添加项目。

ls = ["a", "b", "c", "d"]
n = len(ls)  # n = 4

letter = ls[4]  # index out of range (list indexes are 0 - 3)

# the following all do the same thing

ls += ["echo"]
ls.append("foxtrot")
ls[len(ls):] = ["golf"]  # equivalent to ls[len(ls): len(ls)]

print(ls)

尽管对我来说看起来很奇怪,但我理解切片如何修改它们操作的列表。我不明白的是为什么 list[len(list)] 会导致预期的越界错误,而 list[len(list):] 却不会。我了解切片与索引根本不同,只是不知道当切片从列表值之外的索引开始时内部会发生什么。
为什么我可以从以不存在元素 (len(list)) 开头的列表返回一个切片?这为什么允许我扩展列表?
此外,在上述三种附加项目的方法中,哪一种从约定或性能的角度更受欢迎?它们中是否存在性能缺陷?

当你将某个东西分配给一个列表切片时,我无法解释会发生什么(我甚至不知道为什么允许这样做)。首选形式通常是Python中最易读的形式,即ls += ["alpha"]ls.append("tango")。从性能角度来看,这取决于您的Python解释器。 - DainDwarf
请将您的问题缩小范围;目前来看,它不清楚是重复了 https://dev59.com/Ymkx5IYBdhLWcg3wCP2Y 还是 https://dev59.com/ymkv5IYBdhLWcg3wZwC- 或其他内容。您也可以自己进一步研究以完善它,因为已经有很多相关信息可供参考。 - jonrsharpe
请注意,ls += 要求 ls 在本地作用域中(或在函数中给予global声明),因为它执行的是赋值操作,但 ls.append 可以作用于全局变量,因为它仅仅是对现有可变对象的方法调用,而不是赋值。 - PM 2Ring
2个回答

1
虽然 a.append(x)a[len(a):] = [x] 的作用相同,但前者更清晰,几乎应该总是使用。切片替换可能有其用处,但不应该寻找理由去使用它。如果情况需要,何时使用应该是显而易见的。
从索引返回列表元素,切片返回列表的“子列表”这一事实开始。虽然特定位置上的元素存在或不存在,但在数学上方便将每个列表都视为空列表,在任意两个相邻位置之间。考虑以下例子:
>>> a = [1, 2, 3]
>>> a[2:2]
[]

现在考虑下面的序列。每一步,我们增加切片的起始索引。
>>> a[0:]   # A copy of the list
[1, 2, 3]
>>> a[1:]
[2, 3]
>>> a[2:]
[3]

每次这样做时,我们从结果切片的开头移除一个元素。那么当起始索引最终等于最终索引(隐含为列表长度)时,应该发生什么?
>>> a[3:]
[]

您得到了一个空列表,这与以下等式一致。
a + [] = a
a[:k] + a[k:] = a

(第一个是第二个的特殊情况,其中 len(a)。)
现在,一旦我们看到为什么切片存在是有意义的,那么用另一个列表替换该切片意味着什么?换句话说,如果我们用列表 [4] 替换 [1,2,3] 末尾的空列表,我们得到什么列表? 您将获得 [1,2,3,4] ,这与原来的列表是一致的
[1, 2, 3] + [4] = [1, 2, 3, 4]

考虑另一个序列,这次涉及到切片替换。
>>> a = [1,2,3]; a[0:] = ['a']; a
['a']
>>> a = [1,2,3]; a[1:] = ['a']; a
[1, 'a']
>>> a = [1,2,3]; a[2:] = ['a']; a
[1, 2, 'a']
>>> a = [1,2,3]; a[3:] = ['a']; a
[1, 2, 3, 'a']

另一种看待切片替换的方式是将其视为将列表添加到原始子列表中。当该子列表即为列表本身时,则相当于将其添加到原始列表中。

很好的解释。将切片视为发生在元素之间(或元素和列表末尾之间)使得操作对我来说更易理解。 - Brian H.

0
为什么我可以从一个以不存在元素开头的列表返回一个切片?
我不能确定,但我的猜测是因为可以返回一个合理的值(没有元素与之对应,所以返回一个空列表)。
那么为什么这样可以扩展列表呢?
你的切片选择了列表的最后0个元素。然后你用另一个列表的元素替换了这些元素。
此外,在前三种添加项目的方法中,哪一种更好?
如果你只添加一个元素,使用`.append()`,如果你有另一个列表中的元素,则使用`.extend()`。
我不确定性能如何,但我的猜测是它们都被很好地优化了。

你的切片选择了列表的最后0个元素。然后,你用另一个列表的元素替换了这些元素。我猜这就是我无法理解的部分。选择“什么都没有”并用“某物”替换它。我知道你可能会选择最后一个元素并用[last_elem,new_elem]替换它,但事实并非如此。可能有点超自然。行为很奇怪,但我可以解决。我只是不喜欢不知道它是如何工作的。 - Brian H.

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