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

hel*_*hod 219 python list

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

a = [1, 2]
b = [2, 3]
b.extend(a)
Run Code Online (Sandbox Code Playgroud)

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

b += a
Run Code Online (Sandbox Code Playgroud)

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

Sil*_*ost 198

字节码级别的唯一区别在于,这种.extend方式涉及函数调用,这在Python中略高于INPLACE_ADD.

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

  • 也许这种差异对于鸭子类型有更多影响,如果你的*也许不是真正的列表但是像列表*支持`.__ iadd __()`/`.__ add __()`/`. __radd __()`与`.extend()` (12认同)
  • `.extend` 比 `+` 更快。与有额外函数调用的扩展无关。`+` 是一个运算符,它也会导致函数调用。`.extend` 更快的原因是它所做的工作少得多。`+` 将 (1) 创建一个列表,复制该列表中的所有元素(引用),然后它将获取第二个列表并添加引用。`.extend` 不会创建新列表,也不会从该列表复制引用元素。扩展相当于“a[len(a):] = iterable”。扩展将在您正在执行操作的列表上进行操作,并且应该使用它而不是`L = L + iterable` (7认同)
  • 这个答案没有提到重要的范围界定差异. (6认同)
  • 实际上,扩展比INPLACE_ADD()(即列表连接)要快。https://gist.github.com/mekarpeles/3408081 (2认同)
  • 对我来说,这个答案并没有真正帮助我决定应该使用哪一个作为一般原则。我认为一致性很重要,并且了解诸如它如何不能与非本地人一起使用以及不能链接(来自其他答案)之类的事情提供了一个更实用的、功能性的理由来使用“extend()”运营商,即使有选择。“数十亿次操作”用例是一个有效的观点,但在我的职业生涯中我遇到的次数并不多。 (2认同)

mon*_*ius 163

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

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

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

    def boo():
        l += [5]

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

main()
Run Code Online (Sandbox Code Playgroud)

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

  • 我的例子中的变量'l'就是那种.它不是'foo'和'boo'函数的本地(在它们的作用域之外),但它不是全局的(在'main'函数内定义,而不是在模块级别定义) (7认同)
  • 那就对了.但至少你可以在Python3中的*boo*中使用*nonlocal l*语句. (6认同)
  • 我对你的解释有困难"对于函数而言**不是本地的**而且**不是全局的**"你能给出这样一个变量的例子吗? (4认同)
  • 我可以确认python 3.4.2仍然会发生此错误(您需要添加括号才能打印,但其他所有内容都可以保持不变)。 (2认同)

isa*_*ndi 31

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

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
Run Code Online (Sandbox Code Playgroud)


dal*_*soa 11

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()
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

  • 我知道。这个例子的重点是比较将所有元素放在一起的列表的不同方式。当然,它需要更长的时间,因为它做了不同的事情,但如果您有兴趣保留原始对象不变,那么了解一下仍然是件好事。 (5认同)
  • 您不能将“ADD”与“INPLACE_ADD”和“extend()”进行比较。`ADD` 生成一个新列表并将两个原始列表的元素复制到其中。当然它会比“INPLACE_ADD”和“extend()”的就地操作慢。 (3认同)

Lan*_*ang 7

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

import numpy as np
a = np.zeros((4,4,4))
b = []
b += a
Run Code Online (Sandbox Code Playgroud)

它将返回错误

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

b.extend(a) 工作得很好


小智 7

ary += ext 创建一个新的 List 对象,然后将列表“ary”和“ext”中的数据复制到其中。

\n

ary.extend(ext) 只是将对“ext”列表的引用添加到“ary”列表的末尾,从而减少内存事务。

\n

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

\n
\xe2\x95\xb0\xe2\x94\x80\xe2\x9e\xa4 time ./list_plus.py\n./list_plus.py  36.03s user 6.39s system 99% cpu 42.558 total\n\xe2\x95\xb0\xe2\x94\x80\xe2\x9e\xa4 time ./list_extend.py\n./list_extend.py  0.03s user 0.01s system 92% cpu 0.040 total\n
Run Code Online (Sandbox Code Playgroud)\n

第一个脚本还使用超过 200MB 的内存,而第二个脚本使用的内存并不比“裸”python3 进程多。

\n

话虽如此,就地添加似乎确实与 .extend 做了同样的事情。

\n

  • 您可以在此处添加`/list_plus.py`和`/list_extend.py`的内容吗? (2认同)

Vic*_*icX 5

来自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;
}
Run Code Online (Sandbox Code Playgroud)


gro*_*fte 5

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]
Run Code Online (Sandbox Code Playgroud)

Python 3.6
*很确定 .extend() 适用于任何可迭代对象,但如果我不正确,请发表评论