问答中心分类: LISTPython 的列表方法 append 和 extend 有什么区别?
0
匿名用户 提问 6分钟 前

这个问题的答案是社区努力.编辑现有答案以改进这篇文章。它目前不接受新的答案或交互。

列表方法有什么区别append()extend()?

19 Answers
0
Harley Holcombe 回答 6分钟 前

append将元素添加到列表中。extend将第一个列表与另一个列表/可迭代连接。

>>> xs = ['A', 'B']
>>> xs
['A', 'B']

>>> xs.append("D")
>>> xs
['A', 'B', 'D']

>>> xs.append(["E", "F"])
>>> xs
['A', 'B', 'D', ['E', 'F']]

>>> xs.insert(2, "C")
>>> xs
['A', 'B', 'C', 'D', ['E', 'F']]

>>> xs.extend(["G", "H"])
>>> xs
['A', 'B', 'C', 'D', ['E', 'F'], 'G', 'H']
0
Russia Must Remove Putin 回答 6分钟 前

列表方法追加和扩展有什么区别?

  • append将其参数作为单个元素添加到列表的末尾。列表本身的长度将增加一。
  • extend迭代其参数,将每个元素添加到列表中,扩展列表。无论可迭代参数中有多少元素,列表的长度都会增加。

append
list.append方法将一个对象附加到列表的末尾。

my_list.append(object)

无论对象是什么,无论是数字、字符串、另一个列表还是其他东西,它都会被添加到my_list作为列表中的单个条目。

>>> my_list
['foo', 'bar']
>>> my_list.append('baz')
>>> my_list
['foo', 'bar', 'baz']

所以请记住,列表是一个对象。如果您将另一个列表附加到列表中,则第一个列表将是列表末尾的单个对象(这可能不是您想要的):

>>> another_list = [1, 2, 3]
>>> my_list.append(another_list)
>>> my_list
['foo', 'bar', 'baz', [1, 2, 3]]
                     #^^^^^^^^^--- single item at the end of the list.

extend
list.extend方法通过附加来自可迭代的元素来扩展列表:

my_list.extend(iterable)

因此,通过扩展,可迭代的每个元素都被附加到列表中。例如:

>>> my_list
['foo', 'bar']
>>> another_list = [1, 2, 3]
>>> my_list.extend(another_list)
>>> my_list
['foo', 'bar', 1, 2, 3]

请记住,字符串是可迭代的,因此如果您使用字符串扩展列表,您将在迭代字符串时附加每个字符(这可能不是您想要的):

>>> my_list.extend('baz')
>>> my_list
['foo', 'bar', 1, 2, 3, 'b', 'a', 'z']

运算符重载,__add__(+) 和__iadd__(+=)
两个都++=运算符定义为list.它们在语义上类似于扩展。
my_list + another_list在内存中创建第三个列表,因此您可以返回它的结果,但它要求第二个可迭代对象是一个列表。
my_list += another_list就地修改列表(它如我们所见,就地运算符和列表是可变对象),因此它不会创建新列表。它也像extend一样工作,因为第二个iterable可以是任何类型的iterable。
不要混淆-my_list = my_list + another_list不等于+=– 它为您提供分配给 my_list 的全新列表。
时间复杂度
追加有 (摊销)恒定时间复杂度, O(1)。
Extend 具有时间复杂度 O(k)。
遍历多次调用append增加了复杂性,使其等同于扩展,并且由于扩展的迭代是在 C 中实现的,如果您打算将可迭代的连续项目附加到列表中,它总是会更快。
关于“摊销”——从列表对象实现源

/* This over-allocates proportional to the list size, making room
     * for additional growth.  The over-allocation is mild, but is
     * enough to give linear-time amortized behavior over a long
     * sequence of appends() in the presence of a poorly-performing
     * system realloc().

这意味着我们预先获得了大于需要的内存重新分配的好处,但我们可能会在下一次边际重新分配时付出更大的代价。所有追加的总时间在 O(n) 处是线性的,并且每个追加分配的时间变为 O(1)。
Performance
您可能想知道什么更高效,因为 append 可用于实现与 extend 相同的结果。以下函数做同样的事情:

def append(alist, iterable):
    for item in iterable:
        alist.append(item)
        
def extend(alist, iterable):
    alist.extend(iterable)

所以让我们给他们计时:

import timeit

>>> min(timeit.repeat(lambda: append([], "abcdefghijklmnopqrstuvwxyz")))
2.867846965789795
>>> min(timeit.repeat(lambda: extend([], "abcdefghijklmnopqrstuvwxyz")))
0.8060121536254883

解决对时间的评论
一位评论者说:

完美的答案,我只是错过了比较只添加一个元素的时间

做语义上正确的事情。如果要在可迭代中追加所有元素,请使用extend.如果您只是添加一个元素,请使用append.
好的,让我们创建一个实验,看看它是如何及时完成的​​:

def append_one(a_list, element):
    a_list.append(element)

def extend_one(a_list, element):
    """creating a new list is semantically the most direct
    way to create an iterable to give to extend"""
    a_list.extend([element])

import timeit

而且我们看到,为了使用扩展而特意创建一个可迭代的对象是(轻微的)时间浪费:

>>> min(timeit.repeat(lambda: append_one([], 0)))
0.2082819009956438
>>> min(timeit.repeat(lambda: extend_one([], 0)))
0.2397019260097295

我们从中了解到,使用没有任何收获extend当我们只有要附加的元素。
此外,这些时间并不那么重要。我只是向他们表明,在 Python 中,做语义上正确的事情就是做事情正确的方式™。
可以想象,您可能会在两个可比较的操作上测试时序并得到模棱两可或相反的结果。只专注于做语义上正确的事情。
结论
我们看到extend在语义上更清晰,并且它可以运行得比append,当您打算将迭代中的每个元素附加到列表时。
如果您只有一个元素(不在迭代中)要添加到列表中,请使用append.

ilias iliadis 回复 6分钟 前

@Aaron Hall 关于计时算法的一个小评论。 “extend_one”可能会返回“稍微错误”的时间,因为还涉及到列表的创建。可能更好的方法是将项目创建为变量(ex1 = 0ex2 = [0]) 并传递这些变量,如果你想更严格的话。

Jean-Francois T. 回复 6分钟 前

确实是完美的答案。性能怎么样l1 += l2对比l1.extend(l2)?

ShadowRanger 回复 6分钟 前

@Jean-FrancoisT.:l1 += l2l1.extend(l2)最终执行相同的代码(list_extend作用于listobject.c)。唯一的区别是:1。+=重新分配l1(对自己来说lists,但重新分配支持之后不是同一个对象的不可变类型),这使得它非法如果l1实际上是不可变对象的一个​​属性;例如,t = ([],),t[0] += lst会失败,而t[0].extend(lst)会工作。 2.l1 += l2使用专用的字节码,而l1.extend(l2)使用广义方法调度;这使得+=比…快extend.

ShadowRanger 回复 6分钟 前

事实是+=必须重新分配l1确实意味着在某些情况下,较慢的调度extend通过不分配回左侧来部分或全部弥补。例如,如果list是对象的属性,self.l1 += l2self.l1.extend(l2)在我的 Python 3.6 安装上具有相同的性能,仅仅是因为实际操作更像self.l1 = self.l1.__iadd__(l2),这意味着它必须执行适度昂贵的STORE_ATTRself.l1.extend(l2)不必。

ShadowRanger 回复 6分钟 前

局部测试中的简单比较:对于局部变量(所以+=只是在使用STORE_FAST,这是超级便宜的),其中增加的价值是现有的list里面有一个项目,操作重复 1000 次,+=平均耗时约 33 ns,而extend耗时 78 ns,相差 45 ns。如果l1是全球性的(需要更昂贵的STORE_GLOBAL),差异缩小到 17 ns。如果l1实际上是local.l1(需要更贵的STORE_ATTR) 之间没有有意义的区别+=extend(时间大致相同;extend有时会赢)。

tparker 回复 6分钟 前

您能否澄清两者之间的行为差​​异my_list = my_list + another_listmy_list += another_list?修改现有列表和创建具有相同内容的新列表并重新绑定名称之间的实际区别是什么?

Tony Suffolk 66 回复 6分钟 前

@tparker – 如果你有另一个名字(foo)绑定到 my_list (假设你在一个函数中),那么使用 += 不会将 my_list 重新绑定到一个新对象,因此 foo 和 my_list 仍然是同一个列表。做显式的 + 和赋值(即不使用 +=)意味着 RHS 是一个新列表,并且名称 my_list 是重新绑定的,这意味着 my_list 和 foo 不再绑定到同一个对象。

Daniel 回复 6分钟 前

@Aaron Hall 你这么说append为了array a需要 O(1) 时间但是如何创建一个新的数组大小sizeof(a)+1具有所需的值? (如 a 中的第一个项目和最后一个新项目)。我真的在问,因为我不知道它在 python 中是如何工作的

Russia Must Remove Putin 回复 6分钟 前

@Daniel 我在该部分添加了一些信息 – 如果您有兴趣,这应该会引导您获得更多相关信息。

zk82 回复 6分钟 前

有点奇怪,“l1[0] += l2”失败了,但它确实扩展了列表(至少在我的 Python 版本中)@ShadowRanger >>> try: … l1 = ([], ) … l1 [0] += range(3) … except TypeError: … print(‘Exception got, but l1 is:’, l1) … Exception got, but l1 is: ([0, 1, 2] ,)

ShadowRanger 回复 6分钟 前

@zk82:是的。它完成了整个操作,然后重新分配失败。可能最好避免它;我将被迫对任何实际使用过的人进行焦油和羽毛处理+=就像那样,只是用了一个try:/except TypeError: pass使异常静音。 🙂

0
Greg Hewgill 回答 6分钟 前

append附加一个元素。extend附加元素列表。
请注意,如果您传递一个列表来追加,它仍然会添加一个元素:

>>> a = [1, 2, 3]
>>> a.append([4, 5, 6])
>>> a
[1, 2, 3, [4, 5, 6]]
0
PythonProgrammi 回答 6分钟 前

追加与扩展
在此处输入图像描述
使用 append,您可以附加一个将扩展列表的元素:

>>> a = [1,2]
>>> a.append(3)
>>> a
[1,2,3]

如果你想扩展多个元素,你应该使用extend,因为你只能附加一个元素或一个元素列表:

>>> a.append([4,5])
>>> a
>>> [1,2,3,[4,5]]

这样你就得到一个嵌套列表
而不是使用extend,您可以像这样扩展单个元素

>>> a = [1,2]
>>> a.extend([3])
>>> a
[1,2,3]

或者,与追加不同,一次扩展更多元素而不将列表嵌套到原始列表中(这就是名称扩展的原因)

>>> a.extend([4,5,6])
>>> a
[1,2,3,4,5,6]

使用两种方法添加一个元素
在此处输入图像描述
append 和 extend 都可以将一个元素添加到列表的末尾,尽管 append 更简单。
追加 1 个元素

>>> x = [1,2]
>>> x.append(3)
>>> x
[1,2,3]

扩展一个元素

>>> x = [1,2]
>>> x.extend([3])
>>> x
[1,2,3]

添加更多元素…具有不同的结果
如果您对多个元素使用 append,则必须将元素列表作为参数传递,您将获得一个 NESTED 列表!

>>> x = [1,2]
>>> x.append([3,4])
>>> x
[1,2,[3,4]]

相反,使用extend,您将一个列表作为参数传递,但您将获得一个包含未嵌套在旧元素中的新元素的列表。

>>> z = [1,2] 
>>> z.extend([3,4])
>>> z
[1,2,3,4]

因此,对于更多元素,您将使用 extend 来获取包含更多项目的列表。但是,追加列表不会向列表中添加更多元素,而是添加一个嵌套列表的元素,您可以在代码输出中清楚地看到。
在此处输入图像描述
在此处输入图像描述

0
Erik 回答 6分钟 前

以下两个片段在语义上是等效的:

for item in iterator:
    a_list.append(item)

a_list.extend(iterator)

后者可能会更快,因为循环是用 C 实现的。

Alex L 回复 6分钟 前

在我的机器上扩展比在循环中附加快约 4 倍(16us 对 4us 用于 100 个零循环)

Mad Physicist 回复 6分钟 前

extend()可能预分配,而append()可能没有。

Soren Bjornstad 回复 6分钟 前

@MadPhysicist:为了完整起见,有时会extend() 不能明智地预分配,因为一些迭代没有实现__len__(),但像你一样,如果它不尝试,我会感到惊讶。正如在亚伦的回答.