Answers:
字节码级别的唯一区别在于,该.extend
方式涉及函数调用,在Python中,该函数的调用成本比INPLACE_ADD
。
除非您要执行数十亿次此操作,否则实际上不必担心。但是,瓶颈可能在其他地方。
.__iadd__()
/ .__add__()
/ .__radd__()
对.extend()
您不能将+ =用于非局部变量(该变量对于函数而言不是局部变量,也不是全局变量)
def main():
l = [1, 2, 3]
def foo():
l.extend([4])
def boo():
l += [5]
foo()
print l
boo() # this will fail
main()
这是因为对于扩展情况,编译器将l
使用LOAD_DEREF
指令加载变量,而对于+ =,它将使用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
从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;
}
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()可与任何迭代器一起使用,但是如果我不正确,请发表评论
list.extend(iterable) Extend the list by appending all the items from the iterable. Equivalent to a[len(a):] = iterable.
猜猜我回答了我自己的星号。
+=
运算符与不同类型的对象一起使用时(与问题中的两个列表相反),您不能指望会得到对象的串联。您不能期望会list
返回一个类型。看一下您的代码,您得到了numpy.ndarray
而不是list
。
其实,有三个选项之间的差异:ADD
,INPLACE_ADD
和extend
。前者总是较慢,而另两个大致相同。
有了这些信息,我宁愿使用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
ADD
与INPLACE_ADD
和进行比较extend()
。ADD
生成一个新列表,并将两个原始列表的元素复制到该列表。可以肯定这将是比就地操作慢INPLACE_ADD
和extend()
。
我已经查阅了官方的Python教程,但是找不到关于此主题的任何内容
这些信息恰好埋在“ 编程常见问题”中:
...对于列表,
__iadd__
[ie+=
]等效于调用extend
列表并返回列表。这就是为什么我们说列表+=
是“简写”的原因list.extend
您还可以在CPython源代码中亲自查看以下内容:https : //github.com/python/cpython/blob/v3.8.2/Objects/listobject.c#L1000-L1011
根据Python进行数据分析。
“请注意,通过添加进行列表连接是一项相对昂贵的操作,因为必须创建新列表并复制对象。通常最好使用extend将元素追加到现有列表中,特别是在构建大型列表时。”因此,
everything = []
for chunk in list_of_lists:
everything.extend(chunk)
比串联的替代方法更快:
everything = []
for chunk in list_of_lists:
everything = everything + chunk
everything = everything + temp
不一定以与相同的方式实现everything += temp
。
everything += temp
的实现方式everything
无需复制。这几乎可以使您的回答变得毫无根据。
.__iadd__()
/.__add__()
/.__radd__()
对.extend()