जब बी एक सूची है, तो b + = (4,) काम क्यों नहीं करता है और b = b + (4,) काम नहीं करता है?


77

अगर हम लेते हैं b = [1,2,3]और अगर हम करने की कोशिश करते हैं:b+=(4,)

यह लौटता है b = [1,2,3,4], लेकिन अगर हम कोशिश करते हैं तो b = b + (4,)यह काम नहीं करता है।

b = [1,2,3]
b+=(4,) # Prints out b = [1,2,3,4]
b = b + (4,) # Gives an error saying you can't add tuples and lists

मैं b+=(4,)असफल होने की उम्मीद करता हूं क्योंकि आप एक सूची और टपल नहीं जोड़ सकते, लेकिन यह काम किया। इसलिए मैंने b = b + (4,)उसी परिणाम की उम्मीद करने की कोशिश की, लेकिन यह काम नहीं किया।


4
मेरा मानना ​​है कि एक उत्तर यहां मिल सकता है
जॉचेन


पहले तो मैंने इसे गलत बताया और इसे बहुत व्यापक के रूप में बंद करने की कोशिश की, फिर इसे वापस ले लिया। फिर मैंने सोचा कि यह एक डुप्लिकेट होना था, लेकिन न केवल मैं एक वोट फिर से नहीं डाल सका, मैंने अपने बालों को उन जैसे अन्य उत्तर खोजने की कोशिश करते हुए बाहर निकाला। : /
कार्ल Knechtel

बहुत ही समान प्रश्न: stackoverflow.com/questions/58048664/…
sanyash

जवाबों:


70

"क्यों" प्रश्नों के साथ समस्या यह है कि आमतौर पर वे कई अलग-अलग चीजों का मतलब कर सकते हैं। मैं हर एक का जवाब देने की कोशिश करूंगा जो मुझे लगता है कि आपके मन में हो सकता है।

"इसके लिए अलग से काम करना क्यों संभव है?" जिसका उत्तर इस प्रकार है । मूल रूप से, +=ऑब्जेक्ट के विभिन्न तरीकों का उपयोग करने की कोशिश करता है: __iadd__(जो केवल बाएं-हाथ की तरफ जाँच की जाती है), बनाम __add__और __radd__("रिवर्स ऐड", दाईं ओर की जाँच की जाती है, अगर बाएं-हाथ की तरफ नहीं है __add__) के लिए +

"वास्तव में प्रत्येक संस्करण क्या करता है?" संक्षेप में, list.__iadd__विधि वही काम करती है जो list.extend(लेकिन भाषा डिजाइन के कारण, अभी भी एक असाइनमेंट बैक है)।

यह भी उदाहरण के लिए इसका मतलब है कि

>>> a = [1,2,3]
>>> b = a
>>> a += [4] # uses the .extend logic, so it is still the same object
>>> b # therefore a and b are still the same list, and b has the `4` added
[1, 2, 3, 4]
>>> b = b + [5] # makes a new list and assigns back to b
>>> a # so now a is a separate list and does not have the `5`
[1, 2, 3, 4]

+बेशक, एक नई वस्तु बनाता है, लेकिन तत्वों को एक अलग अनुक्रम से बाहर खींचने की कोशिश करने के बजाय स्पष्ट रूप से एक और सूची की आवश्यकता होती है।

"ऐसा करने के लिए + = क्यों उपयोगी है? यह अधिक कुशल है; extendविधि को एक नई वस्तु बनाने की ज़रूरत नहीं है। बेशक, यह कुछ आश्चर्यजनक प्रभाव कभी-कभी (जैसे ऊपर), और आमतौर पर पायथन वास्तव में दक्षता के बारे में नहीं है। , लेकिन ये निर्णय बहुत समय पहले किए गए थे।

"सूची और ट्यूपल्स को + के साथ जोड़ने की अनुमति नहीं देने का क्या कारण है?" यहाँ देखें (धन्यवाद, @ splash58); एक विचार यह है कि (tuple + list) उसी प्रकार का उत्पादन करना चाहिए जैसे (list + tuple), और यह स्पष्ट नहीं है कि परिणाम किस प्रकार का होना चाहिए। +=यह समस्या नहीं है, क्योंकि a += bस्पष्ट रूप से इसके प्रकार को नहीं बदलना चाहिए a


2
उफ़, काफी सही। और सूचियों का उपयोग नहीं करते हैं |, ताकि थोड़े मेरे उदाहरण को बर्बाद कर दें। अगर मैं एक स्पष्ट उदाहरण के बारे में सोचता हूं तो बाद में मैं इसे स्वैप करूंगा।
कार्ल Knechtel

1
|सेट के लिए Btw एक आने वाला ऑपरेटर है लेकिन +सूचियों के लिए नहीं है। इस कारण से मुझे नहीं लगता कि टाइप अस्पष्टता के बारे में तर्क विशेष रूप से मजबूत है। चूंकि ऑपरेटर यह नहीं मानता है कि प्रकारों के लिए समान की आवश्यकता क्यों है? कोई भी इस बात से सहमत हो सकता है कि परिणाम में lhs का प्रकार है। दूसरी ओर, प्रतिबंधित करके list + iterator, डेवलपर को उनके इरादे के बारे में अधिक स्पष्ट होने के लिए प्रोत्साहित किया जाता है। यदि आप एक नई सूची बनाना चाहते हैं, जिसमें सामान से aबढ़ा हुआ सामान शामिल है, bतो पहले से ही ऐसा करने का एक तरीका है new = a.copy(); new += b:। यह एक और पंक्ति है लेकिन क्रिस्टल स्पष्ट है।
a_guest

कारण a += bअलग-अलग व्यवहार करता a = a + bहै दक्षता नहीं है। यह वास्तव में है कि गुइडो ने चुने हुए व्यवहार को कम भ्रामक समझा । किसी फ़ंक्शन aको तर्क के रूप में सूची प्राप्त करने और फिर करने की कल्पना करें a += [1, 2, 3]। यह वाक्यविन्यास निश्चित रूप से ऐसा लगता है कि यह नई सूची बनाने के बजाय सूची में संशोधन कर रहा है , इसलिए यह निर्णय लिया गया था कि यह अपेक्षित परिणाम के बारे में अधिकांश लोगों के अंतर्ज्ञान के अनुसार व्यवहार करे। हालांकि, तंत्र को भी intएस जैसे अपरिवर्तनीय प्रकारों के लिए काम करना पड़ता था , जिसके कारण वर्तमान डिजाइन को बढ़ावा मिला।
स्वेन मार्नाच

मुझे व्यक्तिगत रूप से लगता है कि डिज़ाइन वास्तव में केवल एक शॉर्टहैंड बनाने की तुलना में अधिक भ्रमित है , जैसा कि रूबी ने किया था, लेकिन मैं समझ सकता हूं कि हम वहां कैसे पहुंचे। a += ba = a + b
स्वेन मार्नाच

21

वे समतुल्य नहीं हैं:

b += (4,)

के लिए आशुलिपि है:

b.extend((4,))

जबकि +सूचियाँ एकत्र करती हैं, इसलिए:

b = b + (4,)

आप एक सूची में एक tuple को संक्षिप्त करने की कोशिश कर रहे हैं


14

जब आप ऐसा करते हैं:

b += (4,)

यह करने के लिए परिवर्तित है:

b.__iadd__((4,)) 

हुड के तहत यह कॉल करता है b.extend((4,)), extendएक पुनरावृत्ति स्वीकार करता है और यही कारण है कि यह भी काम करता है:

b = [1,2,3]
b += range(2)  # prints [1, 2, 3, 0, 1]

लेकिन जब आप ऐसा करते हैं:

b = b + (4,)

यह करने के लिए परिवर्तित है:

b = b.__add__((4,)) 

केवल सूची ऑब्जेक्ट स्वीकार करें।


4

दोनों प्रकार के परिवर्तनशील अनुक्रम प्रकारों के लिए आधिकारिक डॉक्स से :

s += t
s.extend(t)

के रूप में परिभाषित कर रहे हैं:

sकी सामग्री के साथ फैली हुई हैt

जो कि इस प्रकार परिभाषित किया गया है:

s = s + t    # not equivalent in Python!

इसका मतलब यह भी है कि किसी भी प्रकार का अनुक्रमt आपके उदाहरण में एक टपल सहित काम करेगा

लेकिन यह रेंज और जनरेटर के लिए भी काम करता है! उदाहरण के लिए, आप यह भी कर सकते हैं:

s += range(3)

3

"संवर्धित" असाइनमेंट ऑपरेटरों की तरह +=पायथन 2.0 में पेश किए गए थे, जो अक्टूबर 2000 में जारी किया गया था। डिजाइन और औचित्य पीईपी 203 में वर्णित हैं । इन ऑपरेटरों के घोषित लक्ष्यों में से एक इन-प्लेस ऑपरेशन का समर्थन था। लिख रहे हैं

a = [1, 2, 3]
a += [4, 5, 6]

जगह में सूची को अद्यतन करने के लिए माना जाता a है । यह तब मायने रखता है aजब सूची के अन्य संदर्भ होते हैं , उदाहरण के लिए जब aफ़ंक्शन तर्क के रूप में प्राप्त किया गया था।

हालाँकि, ऑपरेशन हमेशा नहीं हो सकता है, क्योंकि कई अजगर प्रकार, जिसमें पूर्णांक और तार शामिल हैं, अपरिवर्तनीय हैं , इसलिए i += 1एक पूर्णांक के लिए जैसेi संभवतः जगह में काम नहीं कर सकता है।

सारांश में, संवर्धित असाइनमेंट ऑपरेटरों को संभव होने पर काम करना चाहिए, और अन्यथा एक नई वस्तु तैयार करनी चाहिए। इन डिजाइन लक्ष्यों को सुविधाजनक बनाने के लिए, अभिव्यक्तिx += y को निम्नानुसार व्यवहार करने के लिए निर्दिष्ट किया गया था:

  • अगर x.__iadd__ परिभाषित किया गया है, x.__iadd__(y)तो मूल्यांकन किया जाता है।
  • अन्यथा, यदि x.__add__ लागू किया x.__add__(y)जाता है तो मूल्यांकन किया जाता है।
  • अन्यथा, यदि y.__radd__ लागू किया y.__radd__(x)जाता है तो मूल्यांकन किया जाता है।
  • अन्यथा एक त्रुटि बढ़ा।

इस प्रक्रिया द्वारा प्राप्त पहला परिणाम वापस सौंपा जाएगा x (जब तक कि यह परिणाम NotImplementedसिंगलटन न हो, जिस स्थिति में अगले चरण के साथ लुकअप जारी रहता है)।

यह प्रक्रिया उन प्रकारों को अनुमति देती है जो लागू करने के लिए इन-प्लेस संशोधन का समर्थन करते हैं __iadd__()। इन-प्लेस संशोधन का समर्थन नहीं करने वाले प्रकारों को किसी नए जादू के तरीकों को जोड़ने की आवश्यकता नहीं है, क्योंकि पायथन स्वचालित रूप से अनिवार्य रूप से वापस आ जाएगाx = x + y

तो चलिए अंत में आपके वास्तविक प्रश्न पर आते हैं - आप एक संवर्धित असाइनमेंट ऑपरेटर के साथ सूची में टपल को क्यों जोड़ सकते हैं। स्मृति से, इस का इतिहास लगभग इसी तरह था: पायथन 2.0 में list.__iadd__()पहले से मौजूद list.extend()विधि को कॉल करने के लिए यह विधि लागू की गई थी । जब पायथन 2.1 में पुनरावृत्तियों को पेश किया list.extend()गया था , तो विधि को पुनरावृत्तियों को स्वीकार करने के लिए अद्यतन किया गया था। इन परिवर्तनों का अंतिम परिणाम यही थाmy_list += my_tuple पायथन 2.1 से काम करना शुरू किया। list.__add__()विधि, तथापि, दाएँ हाथ के तर्क के रूप में मनमाने ढंग से iterators का समर्थन करने वाला कभी नहीं था - यह एक जोरदार टाइप किया भाषा के लिए अनुपयुक्त माना जाता था।

मुझे व्यक्तिगत रूप से लगता है कि संवर्धित परिचालकों का कार्यान्वयन पायथन में थोड़ा बहुत जटिल हो गया था। इसके कई आश्चर्यजनक दुष्प्रभाव हैं, जैसे यह कोड:

t = ([42], [43])
t[0] += [44]

दूसरी पंक्ति उठती है TypeError: 'tuple' object does not support item assignment, लेकिन ऑपरेशन सफलतापूर्वक वैसे भी किया जाता है - त्रुटि को उठाने वाली रेखा को निष्पादित करने के बाद tहोगा ([42, 44], [43])


वाहवाही! पीईपी का संदर्भ होना विशेष रूप से उपयोगी है। मैंने सूची के इन-टूपल व्यवहार के बारे में पिछले SO प्रश्न के दूसरे छोर पर एक लिंक जोड़ा। जब मैं पीछे देखता हूं कि पाइथन 2.3 या इससे पहले क्या था, तो यह आज की तुलना में व्यावहारिक रूप से अनुपयोगी लगता है ... (और मेरे पास अभी भी बहुत पुराने मैक पर कुछ भी करने के लिए 1.5 पाने की कोशिश करने और असफल होने की अस्पष्ट याद है)
कार्ल केनचेल

2

ज्यादातर लोग उम्मीद करेंगे कि X + = Y, X = X + Y के समतुल्य होगा। वास्तव में, मार्क लुट्ज़ द्वारा पायथन पॉकेट रेफरेंस (4th एड) पेज 57 पर कहता है "निम्नलिखित दो प्रारूप लगभग बराबर हैं: X = X + Y, एक्स + = वाई ”। हालांकि, पायथन निर्दिष्ट करने वाले लोग उन्हें समकक्ष नहीं बनाते थे। संभवतः यह एक गलती थी जिसके परिणामस्वरूप निराश प्रोग्रामर द्वारा समय के लिए डिबगिंग के समय के रूप में लंबे समय तक पायथन उपयोग में रहेगा, लेकिन अब यह सिर्फ पायथन है। यदि X एक उत्परिवर्ती अनुक्रम प्रकार है, तो X + = Y X.extend (Y) के बराबर है और X = X + X के लिए नहीं है।


> संभवतः यह एक गलती थी जिसके परिणामस्वरूप निराश प्रोग्रामर द्वारा समय के साथ डिबगिंग के समय का परिणाम होगा जब तक कि पायथन उपयोग में रहता है <- क्या आप वास्तव में इस वजह से पीड़ित थे? आप अनुभव से बात कर रहे हैं। मुझे आपकी कहानी सुनना बहुत पसंद है।
विक्की

1

जैसा कि यहां बताया गया है , यदि arrayयह __iadd__विधि लागू नहीं करता है , तो यह b+=(4,)केवल एक छोटा होगा, b = b + (4,)लेकिन जाहिर है कि ऐसा नहीं है, इसलिए इसे arrayलागू करने की __iadd__विधि है। जाहिरा तौर पर __iadd__विधि का कार्यान्वयन कुछ इस तरह है:

def __iadd__(self, x):
    self.extend(x)

हालाँकि हम जानते हैं कि उपरोक्त कोड __iadd__विधि का वास्तविक कार्यान्वयन नहीं है, लेकिन हम यह मान सकते हैं और स्वीकार कर सकते हैं कि extendविधि की तरह कुछ है , जो tuppleइनपुट्स को स्वीकार करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.