连接两个列表 - '+ ='和extend()之间的区别

时间:2010-09-06 17:35:51

标签: list python

我已经看到实际上有两种(可能更多)方法在Python中连接列表: 一种方法是使用extend()方法:

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

另一个使用加号(+)运算符:

b += a

现在我想知道:这两个选项中的哪一个是'pythonic'方式进行列表连接,两者之间是否存在差异(我已查阅官方Python教程但未找到任何关于此主题的内容)。

10 个答案:

答案 0 :(得分:181)

字节码级别的唯一区别是.extend方式涉及函数调用,这在Python中比INPLACE_ADD略贵。

除非你执行这项行动数十亿次,否则你应该担心的事情真的没什么。但是,瓶颈可能会出现在其他地方。

答案 1 :(得分:153)

你不能将+ =用于非局部变量(对于函数而言不是局部的变量,也不是全局变量)

def main():
    l = [1, 2, 3]

    def foo():
        l.extend([4])

    def boo():
        l += [5]

    foo()
    print l
    boo()  # this will fail

main()

这是因为对于 extend 案例编译器将使用l指令加载变量LOAD_DEREF,但对于+ =它将使用LOAD_FAST - 并且您得到{ {1}}

答案 2 :(得分:28)

你可以链接函数调用,但你不能直接+ =函数调用:

{{1}}

答案 3 :(得分:7)

我会说numpy有一些区别(我刚看到问题是关于连接两个列表,而不是numpy数组,但是因为它可能是初学者的问题,比如我,我希望这个可以帮助那些寻求解决这个问题的人,例如

import numpy as np
a = np.zeros((4,4,4))
b = []
b += a

它将返回错误

ValueError:操作数无法与形状(0,)(4,4,4)

一起广播

b.extend(a)完美无缺

答案 4 :(得分:4)

从python 3.5.2源代码: 没什么大不同。

static PyObject *
list_inplace_concat(PyListObject *self, PyObject *other)
{
    PyObject *result;

    result = listextend(self, other);
    if (result == NULL)
        return result;
    Py_DECREF(result);
    Py_INCREF(self);
    return (PyObject *)self;
}

答案 5 :(得分:2)

实际上,三个选项之间存在差异:ADDINPLACE_ADDextend。前者总是较慢,而另两个大致相同。

有了这些信息,我宁愿使用extend,它比ADD快,并且在我看来比INPLACE_ADD更清楚您在做什么。

几次尝试以下代码(对于Python 3):

import time

def test():
    x = list(range(10000000))
    y = list(range(10000000))
    z = list(range(10000000))

    # INPLACE_ADD
    t0 = time.process_time()
    z += x
    t_inplace_add = time.process_time() - t0

    # ADD
    t0 = time.process_time()
    w = x + y
    t_add = time.process_time() - t0

    # Extend
    t0 = time.process_time()
    x.extend(y)
    t_extend = time.process_time() - t0

    print('ADD {} s'.format(t_add))
    print('INPLACE_ADD {} s'.format(t_inplace_add))
    print('extend {} s'.format(t_extend))
    print()

for i in range(10):
    test()
ADD 0.3540440000000018 s
INPLACE_ADD 0.10896000000000328 s
extend 0.08370399999999734 s

ADD 0.2024550000000005 s
INPLACE_ADD 0.0972940000000051 s
extend 0.09610200000000191 s

ADD 0.1680199999999985 s
INPLACE_ADD 0.08162199999999586 s
extend 0.0815160000000077 s

ADD 0.16708400000000267 s
INPLACE_ADD 0.0797719999999913 s
extend 0.0801490000000058 s

ADD 0.1681250000000034 s
INPLACE_ADD 0.08324399999999343 s
extend 0.08062700000000689 s

ADD 0.1707760000000036 s
INPLACE_ADD 0.08071900000000198 s
extend 0.09226200000000517 s

ADD 0.1668420000000026 s
INPLACE_ADD 0.08047300000001201 s
extend 0.0848089999999928 s

ADD 0.16659500000000094 s
INPLACE_ADD 0.08019399999999166 s
extend 0.07981599999999389 s

ADD 0.1710910000000041 s
INPLACE_ADD 0.0783479999999912 s
extend 0.07987599999999873 s

ADD 0.16435900000000458 s
INPLACE_ADD 0.08131200000001115 s
extend 0.0818660000000051 s

答案 6 :(得分:2)

我已经查看了Python的官方教程,但是找不到关于此主题的任何内容

此信息恰好埋在Programming FAQ中:

...用于列表,__iadd__ [即+=]等效于在列表上调用extend并返回列表。这就是为什么我们说对于列表,+=list.extend的“简写”

您还可以在CPython源代码中https://github.com/python/cpython/blob/v3.8.2/Objects/listobject.c#L1000-L1011

亲眼看到这一点。

答案 7 :(得分:0)

extend()适用于任何可迭代的*,+ =适用于某些但可以变得时髦。

import numpy as np

l = [2, 3, 4]
t = (5, 6, 7)
l += t
l
[2, 3, 4, 5, 6, 7]

l = [2, 3, 4]
t = np.array((5, 6, 7))
l += t
l
array([ 7,  9, 11])

l = [2, 3, 4]
t = np.array((5, 6, 7))
l.extend(t)
l
[2, 3, 4, 5, 6, 7]

Python 3.6
*非常确定.extend()可以与任何可迭代对象一起使用,但是如果我不正确,请发表评论

答案 8 :(得分:0)

当列表在元组中时,只能使用 .extend()

这会起作用

t = ([],[])
t[0].extend([1,2])

虽然这不会

t = ([],[])
t[0] += [1,2]

原因是 += 生成了一个新对象。如果你看长版:

t[0] = t[0] + [1,2]

您可以看到这将如何改变元组中的哪个对象,这是不可能的。使用 .extend() 修改元组中的对象,这是允许的。

答案 9 :(得分:-1)

根据Python进行数据分析。

“请注意,通过加法进行列表连接是一项相对昂贵的操作,因为必须创建新列表并复制对象。通常最好使用extend将元素追加到现有列表中,特别是在构建大型列表时。 ” 因此,

everything = []
for chunk in list_of_lists:
    everything.extend(chunk)

比串联的速度快:

everything = []
for chunk in list_of_lists:
    everything = everything + chunk

enter image description here enter image description here