पायथन में "i = i + x" से "i + = x" अलग कब है?


212

मुझे बताया गया था कि +=मानक संकेतन से अलग प्रभाव हो सकते हैं i = i +। क्या कोई ऐसा मामला है जिसमें i += 1से अलग होगा i = i + 1?


7
+=extend()सूचियों के मामले में कार्य करता है।
अश्विनी चौधरी

12
@ अश्विनीचौधरी यह विचार करते हुए एक बहुत ही सूक्ष्म अंतर i=[1,2,3];i=i+[4,5,6];i==[1,2,3,4,5,6]है True। कई डेवलपर्स नोटिस नहीं कर सकते हैं कि id(i)एक ऑपरेशन के लिए बदलता है, लेकिन दूसरे को नहीं।
कोजीरो

1
@ कोकोजी - जबकि यह एक सूक्ष्म अंतर है, मुझे लगता है कि यह एक महत्वपूर्ण है।
एमजीसिलसन

@mgilson यह महत्वपूर्ण है, और इसलिए मुझे लगा कि इसे स्पष्टीकरण की आवश्यकता है। :)
कोजीरो

1
जावा में दोनों के बीच मतभेद के बारे में संबंधित प्रश्न: stackoverflow.com/a/7456548/245966
jakub.g

जवाबों:


317

यह पूरी तरह से वस्तु पर निर्भर करता है i

+=__iadd__विधि को कॉल करता है (यदि यह मौजूद है - यदि यह मौजूद __add__नहीं है तो वापस गिरता है) जबकि विधि 1 या विधि को कुछ मामलों में 2+ कहता है । __add____radd__

एपीआई के नजरिए से, स्थान में__iadd__ उत्परिवर्तित वस्तुओं को संशोधित करने के लिए इस्तेमाल किया जाना चाहिए (जो वस्तु को उत्परिवर्तित किया गया था) जबकि कुछ का नया उदाहरण__add__ वापस करना चाहिए । के लिए अपरिवर्तनीय वस्तुओं, दोनों तरीकों एक नया उदाहरण लौटने के लिए, लेकिन एक ही नाम है कि पुराने उदाहरण था के साथ वर्तमान नाम स्थान में नया उदाहरण डाल देंगे। इसलिए__iadd__

i = 1
i += 1

वेतन वृद्धि लगती है i। वास्तव में, आप एक नया पूर्णांक प्राप्त करते हैं और इसे "शीर्ष पर" असाइन करते हैं i- पुराने पूर्णांक के लिए एक संदर्भ खो देते हैं। इस मामले में, i += 1बिल्कुल वैसा ही है i = i + 1। लेकिन, अधिकांश परिवर्तनशील वस्तुओं के साथ, यह एक अलग कहानी है:

एक ठोस उदाहरण के रूप में:

a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a  #[1, 2, 3, 1, 2, 3]
print b  #[1, 2, 3, 1, 2, 3]

की तुलना में:

a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]

नोटिस कैसे पहले उदाहरण में, के बाद से bऔर aसंदर्भ एक ही वस्तु है, जब मैं का उपयोग +=पर b, यह वास्तव में बदल जाता है b(और aभी है कि परिवर्तन देखता है - आखिरकार, यह एक ही सूची को संदर्भित है)। हालांकि, दूसरे मामले में, जब मैं ऐसा करता हूं b = b + [1, 2, 3], तो यह सूची लेती है जो bसंदर्भित है और इसे एक नई सूची के साथ समेटती है [1, 2, 3]। इसके बाद वर्तमान नाम स्थान में समवर्ती सूची को संग्रहीत करता है b- bइससे पहले कि रेखा क्या थी, इसके लिए कोई संबंध नहीं है ।


1 अभिव्यक्ति में x + y, अगर x.__add__लागू नहीं है या यदि x.__add__(y)रिटर्न NotImplemented और xऔर yविभिन्न प्रकार है, तो x + yकॉल करने की कोशिश करता y.__radd__(x)। तो, उस मामले में जहां आपके पास है

foo_instance += bar_instance

यदि Fooलागू नहीं होता है __add__या __iadd__फिर यहां परिणाम समान है

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

2 अभिव्यक्ति में foo_instance + bar_instance, bar_instance.__radd__पहले की कोशिश की जाएगी foo_instance.__add__ यदि प्रकार के प्रकार का bar_instanceउपवर्ग है foo_instance(जैसे issubclass(Bar, Foo))। इसके लिए तर्कसंगत है क्योंकि Barकुछ अर्थों में एक "उच्च-स्तरीय" वस्तु है, Fooजिससे Barओवरराइडिंग Fooके व्यवहार का विकल्प मिलना चाहिए ।


18
ठीक है, +=कॉल __iadd__ अगर यह मौजूद है , और अन्यथा जोड़ने और रिबंड करने के लिए वापस गिर जाता है। यही कारण है कि i = 1; i += 1काम करता है, भले ही वहाँ नहीं है int.__iadd__। लेकिन उस मामूली नट के अलावा, महान स्पष्टीकरण।
रात को

4
@abarnert - मैंने हमेशा int.__iadd__कहा कि बस बुलाया __add__। मुझे आज कुछ नया सीखने की खुशी है :)।
मैगिलसन

@abarnert - मैं होने के लिए हो सकता है लगता है पूरा , x + yकॉल y.__radd__(x)करता है, तो x.__add__(या रिटर्न मौजूद नहीं है NotImplementedऔर xऔर yविभिन्न प्रकार के कर रहे हैं)
mgilson

यदि आप वास्तव में पूर्णवादी बनना चाहते हैं, तो आपको उल्लेख करना होगा कि "यदि यह मौजूद है" बिट सामान्य गेटअटार तंत्र के माध्यम से चला जाता है, क्लासिक कक्षाओं के साथ कुछ quirks को छोड़कर, और C API में लागू किए गए प्रकारों के बजाय इसके लिए दिखता है nb_inplace_addया sq_inplace_concat, और उन सी एपीआई कार्यों की अजगर आवश्यकताओं की तुलना में सख्त आवश्यकताएं हैं, और ... लेकिन मुझे नहीं लगता कि यह उत्तर के लिए प्रासंगिक है। मुख्य अंतर यह है कि +=वापस अभिनय करने से पहले इन-प्लेस ऐड करने की कोशिश करता है +, जो मुझे लगता है कि आपने पहले ही समझाया है।
abarnert

हाँ, मुझे लगता है कि आप सही हैं ... हालाँकि मैं अभी इस बात पर अड़ा हूँ कि C API अजगर का हिस्सा नहीं है । यह Cpython का हिस्सा है :-P
mgilson

67

कवर के तहत, i += 1कुछ इस तरह है:

try:
    i = i.__iadd__(1)
except AttributeError:
    i = i.__add__(1)

जबकि i = i + 1कुछ इस तरह है:

i = i.__add__(1)

यह एक मामूली निरीक्षण है, लेकिन आपको यह विचार मिलता है: पायथन प्रकार को +=विशेष रूप से संभालने का एक तरीका देता है , __iadd__साथ ही एक विधि भी बनाता है __add__

अभिप्राय यह है कि उत्परिवर्तनीय प्रकार, जैसे list, अपने आप में उत्परिवर्तित करेंगे __iadd__(और फिर वापस लौटेंगे self, जब तक कि आप कुछ बहुत मुश्किल नहीं कर रहे हैं), जबकि अपरिवर्तनीय प्रकार, जैसेint , बस इसे लागू नहीं करेंगे।

उदाहरण के लिए:

>>> l1 = []
>>> l2 = l1
>>> l1 += [3]
>>> l2
[3]

क्योंकि l2जैसी वस्तु है l1, और आपने उत्परिवर्तित की है l1, आपने भी उत्परिवर्तित किया है l2

परंतु:

>>> l1 = []
>>> l2 = l1
>>> l1 = l1 + [3]
>>> l2
[]

यहाँ, आपने उत्परिवर्तन नहीं किया l1; इसके बजाय, आपने एक नई सूची बनाई l1 + [3], और मूल l1बिंदु पर l2इंगित करते हुए, उस पर इंगित करने के लिए नाम को पुनर्जन्म किया ।

( +=संस्करण में, आप भी विद्रोह कर रहे थे l1, यह सिर्फ इतना है कि उस स्थिति में आप इसे उसी listके लिए बाध्य कर रहे थे जो पहले से ही बाध्य था, इसलिए आप आमतौर पर उस हिस्से को अनदेखा कर सकते हैं।)


__iadd__वास्तव __add__में एक की स्थिति में फोन करता है AttributeError?
मैगिलसन

खैर, i.__iadd__फोन नहीं करता है __add__; यह i += 1कॉल है __add__
अब्राहम

इर्र ... हाँ, यही मेरा मतलब है। दिलचस्प। मुझे एहसास नहीं था कि स्वचालित रूप से किया गया था।
एमजीसिलसन

3
पहला प्रयास वास्तव में है i = i.__iadd__(1)- ऑब्जेक्ट को जगह में संशोधित iadd कर सकता है, लेकिन ऐसा करने की आवश्यकता नहीं है, और इसलिए दोनों ही स्थिति में परिणाम वापस आने की उम्मीद है।
lvc

ध्यान दें कि इसका मतलब है कि operator.iaddकॉल __add__करता है AttributeError, लेकिन यह परिणाम को रिबंड नहीं कर सकता है ... इसलिए i=1; operator.iadd(i, 1)2 और रिटर्न iसेट पर जाता है 1। जो थोड़ा भ्रमित करने वाला है।
abarnert

6

यहां एक उदाहरण दिया गया है, जो सीधे तुलना i += xकरता है i = i + x:

def foo(x):
  x = x + [42]

def bar(x):
  x += [42]

c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.