2つのリストの連結-'+ ='とextend()の違い


243

Pythonでリストを連結する方法は実際には2つ(おそらくそれ以上)あることがわかりました。1つは、extend()メソッドを使用する方法です。

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

もう一方はplus(+)演算子を使用します。

b += a

さて、これらの2つのオプションのどちらがリスト連結を行う「pythonic」の方法であり、2つのオプションに違いがあります(公式のPythonチュートリアルを調べましたが、このトピックについて何も見つかりませんでした)。


1
多分違いはそれがダックタイピングに関して、そしてあなたの多分真にリストではないが、リストのようなリスト.__iadd__()/ .__add__()/ .__radd__()対をサポートする場合、より多くの意味を持っています.extend()
Nick T

回答:


214

バイトコードレベルの唯一の違いは、.extend方法には関数呼び出しが含まれることです。これは、Pythonではよりも少し高価INPLACE_ADDです。

この操作を何十億回も実行しているのでない限り、心配する必要はありません。ただし、ボトルネックは他の場所にある可能性があります。


16
多分違いは、ダックタイピングに関して、そしてあなたの多分-本当に-リストではないが、-のような-リスト.__iadd__()/ .__add__()/ .__radd__().extend()
Nick T

8
この回答では、重要なスコープの違いについては触れていません。
2017年

3
実は、extendsはINPLACE_ADD()よりも高速です。つまり、リストの連結です。gist.github.com/mekarpeles/3408081
Archit Kapoor

178

非ローカル変数(関数に対してローカルではなく、グローバルでもない変数)に+ =を使用することはできません

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*


4
「関数に対してローカルではなくグローバルでもない変数」の説明に問題があります。そのような変数の例を挙げていただけますか?
Stephane Rolland 2014

8
私の例の変数「l」はまさにそのようなものです。「foo」関数および「boo」関数(スコープ外)に対してローカルではありませんが、グローバル(モジュールレベルではなく「main」関数内で定義)ではありません
monitorius

3
私はこのエラーがpython 3.4.2でも発生することを確認できます(印刷するには括弧を追加する必要がありますが、他のすべては同じままでかまいません)。
trichoplax

7
そのとおり。しかし、少なくともPython3のboo非ローカルlステートメントを使用できます。
monitorius

コンパイラ->インタプリタ?
joelb

42

関数呼び出しを連鎖させることはできますが、関数呼び出しを直接+ =することはできません。

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

8

私はそれがnumpyに来るときいくつかの違いがあると思います(私はちょうど質問がnumpy配列ではなく2つのリストを連結することについて尋ねるのを見ましたが、それは私のような初心者にとって問題になるかもしれないので、これが誰かを助けることを願っていますこの投稿の解決策を模索している人)、例:

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

エラーで戻ります

ValueError:オペランドは形状(0、)(4,4,4)と一緒にブロードキャストできませんでした

b.extend(a) 完璧に動作します


5

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

4

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()はイテラブルで動作しますが、私が正しくない場合はコメントしてください


タプルは間違いなく反復可能ですが、extend()メソッドはありません。extend()メソッドは反復とは何の関係もありません。
wombatonfire 2019年

.extendはリストクラスのメソッドです。Pythonのドキュメントから:list.extend(iterable) Extend the list by appending all the items from the iterable. Equivalent to a[len(a):] = iterable.私は自分のアスタリスクに答えたと思います。
grofte

ああ、あなたはextender()に任意のイテラブルを渡すことができることを意味しました。「extend()はすべての反復可能に使用できる」と読みました。
wombatonfire

1
全体として、これは良い例ではありません。少なくとも、この質問のコンテキストではそうではありません。+=(質問のように2つのリストとは対照的に)異なるタイプのオブジェクトで演算子を使用する場合、オブジェクトの連結が得られるとは期待できません。そして、list返される型があることを期待することはできません。あなたのコードを見てください、あなたはnumpy.ndarray代わりにを取得しますlist
wombatonfire 2019年

2

実際には、そこに3つのオプションの違いは以下の通りですADDINPLACE_ADDextend。前者は常に低速ですが、他の2つはほぼ同じです。

この情報については、を使用したいと思います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

2
あなたは比較することはできませんADDINPLACE_ADDしてextend()ADD新しいリストを作成し、2つの元のリストの要素をそのリストにコピーします。確かに、INPLACE_ADDおよびのインプレース操作よりも遅くなりextend()ます。
wombatonfire

そんなこと知ってる。この例のポイントは、すべての要素を含むリストを作成するさまざまな方法を比較することです。もちろん、別のことを行うので時間がかかりますが、元のオブジェクトを変更せずに保持したい場合に備えておくとよいでしょう。
dalonsoa

1

私は公式のPythonチュートリアルを調べましたが、このトピックについて何も見つかりませんでした

この情報は、たまたまプログラミングFAQに埋め込まれています。

...リストの場合、__iadd__[ie +=]はextendリストを呼び出してリストを返すことと同じです。だからこそ、リストの場合+=は、list.extend

これは、CPythonソースコードでも確認できます。https//github.com/python/cpython/blob/v3.8.2/Objects/listobject.c#L1000-L1011


-1

Python for Data Analysisによると。

「追加によるリストの連結は、新しいリストを作成してオブジェクトをコピーする必要があるため、比較的コストのかかる操作です。特に大きなリストを作成している場合は、extendを使用して既存のリストに要素を追加することをお勧めします。」したがって、

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

連結代替よりも高速です:

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

ここに画像の説明を入力してください ここに画像の説明を入力してください


4
everything = everything + tempは必ずしもと同じ方法で実装されるとは限りませんeverything += temp
デビッドハリソン

1
あなたが正しいです。リマインダーありがとうございます。しかし、私のポイントは効率の違いについてです。:)
littlebear333 2018

6
@ littlebear333 everything += tempは、everythingコピーする必要がないように実装されています。これはほとんどあなたの答えを根拠のないものにします。
nog642 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.