दो सूचियों को सम्‍मिलित करना - '+ =' और अंतर के बीच अंतर ()


243

मैंने देखा है कि वास्तव में पायथन में सूचियों को समेटने के दो (शायद अधिक) तरीके हैं: एक तरीका यह है कि विस्तार का उपयोग करें) ()

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

प्लस (+) ऑपरेटर का उपयोग करने के लिए अन्य:

b += a

अब मुझे आश्चर्य है कि उन दो विकल्पों में से कौन सा 'पाइथोनिक' तरीका है लिस्ट कॉनटेनटेशन करने का और दोनों के बीच अंतर है (मैंने आधिकारिक पायथन ट्यूटोरियल को देखा है लेकिन इस विषय के बारे में कुछ भी पता नहीं लगा सका)।


1
हो सकता है कि अंतर अधिक प्रभाव जब यह ducktyping की बात आती है और अगर आपके शायद-नहीं-वास्तव में एक सूची बल्कि तरह-ए-सूची का समर्थन करता है .__iadd__()/ .__add__()/ .__radd__()बनाम.extend()
निक टी

जवाबों:


214

एक बाइटकोड स्तर पर एकमात्र अंतर यह है कि जिस .extendतरह से एक फ़ंक्शन कॉल शामिल है, जो कि पायथन से थोड़ा अधिक महंगा है INPLACE_ADD

यह वास्तव में कुछ भी नहीं है जिसके बारे में आपको चिंता होनी चाहिए, जब तक कि आप इस ऑपरेशन को अरबों बार नहीं कर रहे हैं। हालाँकि, यह संभावना है कि अड़चन कुछ और जगह होगी।


16
हो सकता है कि अंतर के अधिक प्रभाव पड़ते हैं जब यह ducktyping की बात आती है और यदि आपकी शायद-नहीं-एक-सूची-लेकिन-जैसे-एक-सूची का समर्थन करता है .__iadd__()/ .__add__()/ .__radd__()बनाम.extend()
निक टी

8
यह उत्तर महत्वपूर्ण स्कूपिंग अंतरों का उल्लेख करने में विफल रहता है।
विम

3
वास्तव में, विस्तार INPLACE_ADD () की तुलना में अधिक है, यानी सूची का स्थान। gist.github.com/mekarpeles/3408081
अर्चित कपूर

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
मुझे आपके स्पष्टीकरण से कठिनाई है "चर जो फ़ंक्शन के लिए स्थानीय नहीं है और वैश्विक भी नहीं है " क्या आप इस तरह के चर का उदाहरण दे सकते हैं?
स्टीफन रोलैंड

8
मेरे उदाहरण में चर 'l' ठीक उसी तरह का है। यह foo 'और' बू 'कार्य (उनके कार्यक्षेत्र के बाहर) के लिए स्थानीय नहीं है, लेकिन यह (परिभाषित के अंदर' मुख्य 'समारोह, मॉड्यूल स्तर पर नहीं) वैश्विक नहीं है
monitorius

3
मैं पुष्टि कर सकता हूं कि यह त्रुटि अभी भी अजगर 3.4.2 के साथ होती है (आपको प्रिंट करने के लिए कोष्ठक जोड़ने की आवश्यकता होगी लेकिन बाकी सब वही रह सकता है)।
ट्राइकोप्लाक्स

7
ये सही है। लेकिन कम से कम आप Python3 में boo में nonlocal l स्टेटमेंट का उपयोग कर सकते हैं ।
monitorius

संकलक -> दुभाषिया?
जोएलब

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

मैं कहूंगा कि जब यह सुन्न के साथ आता है तो कुछ अंतर होता है (मैंने अभी देखा कि प्रश्न दो सूचियों को संक्षिप्त करने के बारे में पूछता है, न कि खस्ता सरणी के बारे में, लेकिन चूंकि यह शुरुआती के लिए एक मुद्दा हो सकता है, जैसे कि मुझे, मुझे उम्मीद है कि यह किसी की मदद कर सकता है जो इस पद का समाधान चाहते हैं), पूर्व के लिए।

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

विस्तार () किसी भी चलने योग्य * के साथ काम करता है, + = कुछ के साथ काम करता है लेकिन कायरता प्राप्त कर सकता है।

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]

अजगर 3.6
* बहुत यकीन है कि। कस्टम () किसी भी चलने के साथ काम करता है, लेकिन कृपया टिप्पणी करें कि क्या मैं गलत हूं


ट्यूपल निश्चित रूप से एक चलने योग्य है, लेकिन इसकी कोई विस्तारित () विधि नहीं है। विस्तार () विधि का पुनरावृत्ति से कोई लेना-देना नहीं है।
wombatonfire

.extend सूची वर्ग की एक विधि है। पायथन प्रलेखन से: list.extend(iterable) Extend the list by appending all the items from the iterable. Equivalent to a[len(a):] = iterable.लगता है कि मैंने अपने खुद के तार का जवाब दिया।
ग्रोफर्ट

ओह, आपका मतलब था कि आप विस्तार करने के लिए किसी भी चलने योग्य पारित कर सकते हैं ()। मैंने इसे "विस्तार" के रूप में पढ़ा है जो किसी भी चलने योग्य के लिए उपलब्ध है ":) मेरा बुरा है, लेकिन यह थोड़ा अस्पष्ट लगता है।
१२:०२ पर wombatonfire

1
कुल मिलाकर, यह एक अच्छा उदाहरण नहीं है, कम से कम इस प्रश्न के संदर्भ में नहीं। जब आप +=विभिन्न प्रकार की वस्तुओं के साथ एक ऑपरेटर का उपयोग करते हैं (दो सूचियों के विपरीत, सवाल के रूप में), तो आप उम्मीद नहीं कर सकते कि आपको वस्तुओं का एक संयोजन मिलेगा। और आप यह उम्मीद नहीं कर सकते हैं कि एक listप्रकार का रिटर्न होगा। अपने कोड पर एक नज़र है, आप के numpy.ndarrayबजाय मिलता है list
wombatonfire

2

वास्तव में, तीन विकल्प के बीच अंतर हैं: ADD, INPLACE_ADDऔरextend । पूर्व हमेशा धीमा होता है, जबकि अन्य दो लगभग समान होते हैं।

इस जानकारी के साथ, मैं इसका उपयोग करूंगा extend, जो कि तेजी से है ADD, और मुझे लगता है कि आप जो कर रहे हैं उससे अधिक स्पष्ट है INPLACE_ADD

निम्न कोड को कुछ बार आज़माएं (पायथन 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
आप के ADDसाथ INPLACE_ADDऔर तुलना नहीं कर सकते extend()ADDएक नई सूची तैयार करता है और दो मूल सूचियों के तत्वों की प्रतिलिपि बनाता है। यकीन के लिए यह की inplace आपरेशन की तुलना में धीमी हो जाएगा INPLACE_ADDऔर extend()
२३:१

मुझे पता है। इस उदाहरण का बिंदु सभी तत्वों के साथ एक सूची होने के विभिन्न तरीकों की तुलना कर रहा है। यकीन है कि यह अधिक समय लेता है क्योंकि यह अलग-अलग चीजें करता है, लेकिन फिर भी यह जानना अच्छा है कि आप मूल वस्तुओं को संरक्षित करने में रुचि रखते हैं।
डलोनसोआ

1

मैंने आधिकारिक पायथन ट्यूटोरियल देखा है, लेकिन इस विषय के बारे में कुछ भी नहीं पा सका

यह जानकारी प्रोग्रामिंग FAQ में दफन होने के लिए होती है :

... सूचियों के लिए, __iadd__[यानी +=] extendसूची में कॉल करने और सूची वापस करने के बराबर है । इसलिए हम कहते हैं कि सूचियों के लिए, +="शॉर्टहैंड" हैlist.extend

आप CPython स्रोत कोड में अपने लिए भी इसे देख सकते हैं: https://github.com/python/cpython/blob/v3.8.2/Objects/listobject.c#L1000-L1011


-1

डेटा विश्लेषण के लिए अजगर के अनुसार।

"ध्यान दें कि सूची को जोड़ना इसके अलावा एक तुलनात्मक रूप से महंगा ऑपरेशन है क्योंकि एक नई सूची बनाई जानी चाहिए और वस्तुओं को कॉपी किया जाना चाहिए। मौजूदा सूची में तत्वों को बढ़ाने के लिए उपयोग करना, खासकर यदि आप एक बड़ी सूची का निर्माण कर रहे हैं, तो आमतौर पर बेहतर होता है। इस प्रकार,

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

6
@ Littlebear333 everything += tempको इस तरह से लागू किया गया है कि everythingइसे कॉपी करने की आवश्यकता नहीं है। यह बहुत ज्यादा आपके जवाब को एक मूक बिंदु बनाता है।
nog642
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.