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

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

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

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

b += a

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


当前回答

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

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

其他回答

字节码级别上的唯一区别是.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:局部变量'l'在赋值之前引用*

ary += ext创建一个新的List对象,然后将列表“ary”和“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做的事情是一样的。

根据Python进行数据分析。

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

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

比连接替代更快:

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

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

这个信息恰好隐藏在编程FAQ中:

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

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