连接两个列表- '+='和扩展()

我看到在Python中实际上有两种(可能更多)方法来连接列表:

一种方法是使用extend()方法:

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

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

b += a

现在我想知道:这两个选项中哪一个是“python”的方式来做列表连接,两者之间有区别吗?(我已经查阅了官方的Python教程,但找不到任何关于这个主题的任何东西)。

96328 次浏览

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

这真的没什么好担心的,除非你做了几十亿次这个操作。然而,瓶颈很可能存在于其他地方。

对于非局部变量(对于函数来说不是局部变量,也不是全局变量)不能使用+=

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


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


def boo():
l += [5]


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


main()

这是因为对于扩展情况编译器将使用LOAD_DEREF指令加载变量l,但对于+=它将使用LOAD_FAST -并且你得到*UnboundLocalError: local variable 'l' referenced before assignment*

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

class A:
def __init__(self):
self.listFoo = [1, 2]
self.listBar = [3, 4]


def get_list(self, which):
if which == "Foo":
return self.listFoo
return self.listBar


a = A()
other_list = [5, 6]


a.get_list("Foo").extend(other_list)
a.get_list("Foo") += other_list  #SyntaxError: can't assign to function call

我想说,有一些不同,当它与numpy(我刚刚看到的问题是关于连接两个列表,而不是numpy数组,但由于它可能是初学者的问题,如我,我希望这可以帮助谁寻求这篇文章的解决方案),对于ex。

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

它将返回错误

ValueError:操作数不能与形状(0,)(4,4,4)一起广播

b.extend(a)工作完美

CPython 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;
}

根据Python进行数据分析。

注意,通过加法连接列表是一个相对昂贵的操作,因为必须创建一个新的列表并复制对象。使用extend将元素附加到现有列表,特别是如果您正在构建一个大型列表,通常是更可取的。” 因此,< / p >
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 < / p >

列表上的.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()适用于任何可迭代对象,但如果我不正确请评论

< p >编辑:“()”;更改为“列表上的.extend()方法”; 注:David M. Helmuth下面的评论很清晰

实际上,这三个选项是有区别的: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

我查了官方的Python教程,但找不到任何关于这个主题的东西

这个信息恰好被隐藏在编程的常见问题中:

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

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

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

这是可行的

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

而这个不会

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

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

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

你可以看到这会改变元组中的对象,这是不可能的。使用.extend()修改元组中的对象,这是允许的。

ary += ext创建一个新的List对象,然后从列表中复制数据。和“;ext"进去。

Ary.extend (ext)只是增加了对“;ext”的引用;列表的结尾"ary"列表,从而减少内存事务。

因此,.extend的工作速度快了几个数量级,并且不使用被扩展的列表和被扩展的列表之外的任何额外内存。

╰─➤ time ./list_plus.py
./list_plus.py  36.03s user 6.39s system 99% cpu 42.558 total
╰─➤ time ./list_extend.py
./list_extend.py  0.03s user 0.01s system 92% cpu 0.040 total

第一个脚本也使用了超过200MB的内存,而第二个脚本只使用了一个“裸”python3进程的内存。

话虽如此,原地添加似乎与.extend做的事情是一样的。