İki listeyi birleştirmek - '+ =' ve expand () arasındaki fark


243

Ben aslında Python listeleri birleştirmek için iki (belki daha fazla) yolu olduğunu gördüm: Bir yolu, ext () yöntemini kullanmaktır:

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

diğeri artı (+) operatörünü kullanmak için:

b += a

Şimdi merak ediyorum: Bu iki seçenekten hangisinin liste birleştirmesini yapmanın 'pythonic' yolu olduğu ve ikisi arasında bir fark var (Resmi Python öğreticisine baktım ancak bu konu hakkında hiçbir şey bulamadım).


1
O ducktyping gelir ve Belki fark daha etkileri vardır eğer belki-not-gerçekten-a-list-ama-like-a-listesinin destekleri .__iadd__()/ .__add__()/ .__radd__()karşı.extend()
Nick T

Yanıtlar:


214

Bir bayt kodu düzeyindeki tek fark, .extendyolun Python'da olandan biraz daha pahalı olan bir işlev çağrısı içermesidir INPLACE_ADD.

Bu işlemi milyarlarca kez yapmadıkça, endişelenmeniz gereken hiçbir şey yok. Bununla birlikte, darboğazın başka bir yerde yatması muhtemeldir.


16
O ducktyping gelir ve Belki fark daha etkileri vardır eğer belki-not-gerçekten-a-list-ama-like-a-listesinin destekleri .__iadd__()/ .__add__()/ .__radd__()karşı.extend()
Nick T

8
Bu cevap önemli kapsam belirleme farklılıklarından bahsetmemektedir.
wim

3
Aslında, genişletme INPLACE_ADD () yani liste birleştirme işleminden daha hızlıdır. gist.github.com/mekarpeles/3408081
Kapoor

178

Yerel olmayan değişken için + = kullanamazsınız (işlev için yerel olmayan ve global olmayan değişken)

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

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

    def boo():
        l += [5]

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

main()

Çünkü case genişletmek için derleyici komutu lkullanarak değişkeni yükleyecektir LOAD_DEREF, ancak + = için kullanacaktır LOAD_FAST- ve*UnboundLocalError: local variable 'l' referenced before assignment*


4
Ben " işlev için yerel ve aynı zamanda küresel değil " değişken ile sorun yaşıyorsanız böyle bir değişken örnek verebilir misiniz?
Stephane Rolland

8
Örneğimdeki 'l' değişkeni tam olarak bu tür. 'Foo' ve 'boo' işlevleri için yerel değildir (kapsamlarının dışında), ancak küresel değildir ('ana' fonk içinde tanımlanmıştır, modül düzeyinde değil)
monitorius

3
Bu hatanın hala python 3.4.2 ile oluştuğunu onaylayabilirim (yazdırmak için parantez eklemeniz gerekir, ancak diğer her şey aynı kalabilir).
trichoplax

7
Doğru. Ama en azından kullanabileceğiniz yerel olmayan l içinde açıklama boo Python3 içinde.
monitorius

derleyici -> tercüman?
Mart'ta

42

İşlev çağrılarını zincirleyebilirsiniz, ancak doğrudan bir işlev çağrısını + = yapamazsınız:

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 ile geldiğinde bazı farklar olduğunu söyleyebilirim (sadece sorunun numpy dizisini değil, iki listeyi birleştirmeyi sormasını gördüm, ama benim gibi yeni başlayanlar için bir sorun olabileceğinden, umarım bu birine yardımcı olabilir bu gönderiye çözüm arayan), örn.

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

hata ile geri dönecek

ValueError: işlenenler şekillerle (0,) (4,4,4) birlikte yayınlanamadı

b.extend(a) Mükemmel çalışıyor


5

Gönderen CPython 3.5.2 kaynak kod Hayır büyük farkı:.

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

expand () herhangi bir yinelenebilir * ile çalışır, + = bazılarıyla çalışır ancak korkak olabilir.

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
* oldukça emin .extend () herhangi bir yinelenebilir ile çalışır ama eğer yanlışsam lütfen yorum


Tuple kesinlikle tekrarlanabilir bir yöntemdir, ancak expand () yöntemi yoktur. expand () yönteminin yinelemeyle ilgisi yoktur.
wombatonfire

.extend liste sınıfının bir yöntemidir. Python belgelerinden: list.extend(iterable) Extend the list by appending all the items from the iterable. Equivalent to a[len(a):] = iterable.Sanırım kendi yıldızımı yanıtladım.
grofte

Oh, eğer uzatmak için herhangi bir yinelenebilir geçebilirsiniz demek istediniz. "Extend () herhangi bir yinelenebilir için kullanılabilir" olarak okudum :) Benim kötü, ama biraz belirsiz geliyor.
wombatonfire

1
Sonuçta, bu iyi bir örnek değil, en azından bu soru bağlamında değil. +=Farklı türde nesnelerle (soruda olduğu gibi iki listenin aksine) bir işleç kullandığınızda , nesnelerin bir araya gelmesini bekleyemezsiniz. Ve listdöndürülen bir tür olacağını bekleyemezsiniz . Kodunuza bir göz atın, numpy.ndarrayyerine bir tane olsun list.
wombatonfire

2

Aslında, orada üç seçenekler arasında farklar şunlardır: ADD, INPLACE_ADDve extend. Birincisi her zaman daha yavaşken, diğer ikisi kabaca aynıdır.

Bu bilgilerle, kullanmaktan extenddaha hızlı olan ADDve bana ne yaptığınızdan daha açık gibi görünmeyi tercih ederim INPLACE_ADD.

Aşağıdaki kodu birkaç kez deneyin (Python 3 için):

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
Karşılaştırmak edemez ADDile INPLACE_ADDve extend(). ADDyeni bir liste oluşturur ve iki orijinal listenin öğelerini bu listeye kopyalar. Elbette bu INPLACE çalışması daha yavaş olacaktır INPLACE_ADDve extend().
wombatonfire

Bunu biliyorum. Bu örneğin amacı, bir listeye sahip olmanın farklı yollarını tüm öğelerle karşılaştırmaktır. Elbette daha uzun sürer, çünkü farklı şeyler yapar, ancak orijinal nesneleri değiştirmeden korumakla ilgileniyorsanız hala bilmek iyidir.
dalonsoa


-1

Veri Analizi için Python'a göre.

“Ekleme yoluyla liste birleştirmenin nispeten pahalı bir işlem olduğunu ve yeni bir liste oluşturulması ve nesnelerin kopyalanması gerektiğine dikkat edin. Varolan bir listeye öğe eklemek için genişlet özelliğini kullanmak, özellikle de büyük bir liste oluşturuyorsanız, genellikle tercih edilir. " Böylece,

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

birleştirme alternatifinden daha hızlıdır:

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

resim açıklamasını buraya girin resim açıklamasını buraya girin


4
everything = everything + tempile aynı şekilde uygulanması gerekmez everything += temp.
David Harrison

1
Haklısın. Hatırlatıcınız için teşekkürler. Ama benim açımdan verimlilik farkı hakkında. :)
littlebear333

6
@ littlebear333 everything += temp, everythingkopyalanması gerekmeyecek şekilde uygulanır . Bu, cevabınızı tartışmalı bir nokta haline getiriyor.
nog642
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.