Python Language
पाइथन में म्यूटेबल बनाम इम्यूटेबल (और हशेबल)
खोज…
म्यूटेबल बनाम अचल
पाइथन में दो तरह के प्रकार होते हैं। अपरिवर्तनीय प्रकार और उत्परिवर्तनीय प्रकार।
Immutables
एक अपरिवर्तनीय प्रकार की वस्तु को बदला नहीं जा सकता है। ऑब्जेक्ट को संशोधित करने के किसी भी प्रयास के परिणामस्वरूप एक प्रतिलिपि बनाई जाएगी।
इस श्रेणी में शामिल हैं: पूर्णांक, फ़्लोट्स, कॉम्प्लेक्स, स्ट्रिंग्स, बाइट्स, ट्यूपल्स, रेंज और फ्रोज़ेन्सेट्स।
इस संपत्ति को उजागर करने के लिए, के साथ खेलते हैं id
builtin। यह फ़ंक्शन पैरामीटर के रूप में पारित ऑब्जेक्ट की विशिष्ट पहचानकर्ता लौटाता है। यदि आईडी समान है, तो यह एक ही वस्तु है। यदि यह बदलता है, तो यह एक और वस्तु है। (कुछ का कहना है कि यह वास्तव में ऑब्जेक्ट का मेमोरी एड्रेस है, लेकिन उनसे सावधान रहें, वे बल के अंधेरे पक्ष से हैं ...)
>>> a = 1
>>> id(a)
140128142243264
>>> a += 2
>>> a
3
>>> id(a)
140128142243328
ठीक है, 1 नहीं 3 है ... ब्रेकिंग न्यूज ... शायद नहीं। हालांकि, यह व्यवहार अक्सर भूल जाता है जब अधिक जटिल प्रकार, विशेष रूप से तार की बात आती है।
>>> stack = "Overflow"
>>> stack
'Overflow'
>>> id(stack)
140128123955504
>>> stack += " rocks!"
>>> stack
'Overflow rocks!'
अहा! देख? हम इसे संशोधित कर सकते हैं!
>>> id(stack)
140128123911472
नहीं, जबकि ऐसा लगता है कि हम चर stack
द्वारा नामित स्ट्रिंग को बदल सकते हैं, जो हम वास्तव में करते हैं, नतीजे के परिणाम को शामिल करने के लिए एक नई वस्तु बना रहा है। हम मूर्ख हैं क्योंकि इस प्रक्रिया में, पुरानी वस्तु कहीं नहीं जाती है, इसलिए यह नष्ट हो जाती है। एक और स्थिति में, यह अधिक स्पष्ट होगा:
>>> stack = "Stack"
>>> stackoverflow = stack + "Overflow"
>>> id(stack)
140128069348184
>>> id(stackoverflow)
140128123911480
इस मामले में यह स्पष्ट है कि यदि हम पहले स्ट्रिंग को बनाए रखना चाहते हैं, तो हमें एक कॉपी की आवश्यकता है। लेकिन क्या यह अन्य प्रकारों के लिए इतना स्पष्ट है?
व्यायाम
अब, यह जानना कि एक अपरिवर्तनीय प्रकार कैसे काम करता है, आप नीचे दिए गए कोड के साथ क्या कहेंगे? क्या यह बुद्धिमान है?
s = ""
for i in range(1, 1000):
s += str(i)
s += ","
Mutables
एक परिवर्तनशील प्रकार की एक वस्तु को बदला जा सकता है, और इसे इन-सीटू में बदल दिया जाता है । कोई अंतर्निहित प्रतियां नहीं हैं।
इस श्रेणी में शामिल हैं: सूचियाँ, शब्दकोष, उपवाक्य और सेट।
आइए हमारे छोटे id
फ़ंक्शन के साथ खेलना जारी रखें।
>>> b = bytearray(b'Stack')
>>> b
bytearray(b'Stack')
>>> b = bytearray(b'Stack')
>>> id(b)
140128030688288
>>> b += b'Overflow'
>>> b
bytearray(b'StackOverflow')
>>> id(b)
140128030688288
(एक साइड नोट के रूप में, मैं अपनी बात को स्पष्ट करने के लिए ascii डेटा युक्त बाइट्स का उपयोग करता हूं, लेकिन याद रखें कि बाइट्स को शाब्दिक डेटा रखने के लिए डिज़ाइन नहीं किया गया है। बल मुझे माफ कर सकता है।)
हमारे पास क्या है? हम एक बायरएयर बनाते हैं, इसे संशोधित करते हैं और id
का उपयोग करके, हम यह सुनिश्चित कर सकते हैं कि यह एक ही वस्तु है, संशोधित। इसकी प्रति नहीं।
बेशक, यदि किसी वस्तु को अक्सर संशोधित किया जा रहा है, तो एक परिवर्तनशील प्रकार अपरिवर्तनीय प्रकार की तुलना में बहुत बेहतर काम करता है। दुर्भाग्य से, इस संपत्ति की वास्तविकता को अक्सर भुला दिया जाता है जब यह सबसे अधिक दर्द होता है।
>>> c = b
>>> c += b' rocks!'
>>> c
bytearray(b'StackOverflow rocks!')
ठीक है...
>>> b
bytearray(b'StackOverflow rocks!')
वैअइत एक सेकंड ...
>>> id(c) == id(b)
True
वास्तव में। c
, b
कॉपी नहीं है। c
है b
।
व्यायाम
अब आप बेहतर ढंग से समझते हैं कि एक उत्परिवर्तित प्रकार से क्या दुष्प्रभाव होता है, क्या आप बता सकते हैं कि इस उदाहरण में क्या गलत है?
>>> ll = [ [] ]*4 # Create a list of 4 lists to contain our results
>>> ll
[[], [], [], []]
>>> ll[0].append(23) # Add result 23 to first list
>>> ll
[[23], [23], [23], [23]]
>>> # Oops...
तर्क के रूप में पारस्परिक और अपरिवर्तनीय
जब किसी डेवलपर को किसी फ़ंक्शन के लिए तर्क पारित करने की आवश्यकता होती है, तो प्रमुख उपयोग के मामलों में से एक का उपयोग करना पड़ता है। यह बहुत महत्वपूर्ण है, क्योंकि यह फ़ंक्शन के लिए उन वस्तुओं को संशोधित करने की क्षमता का निर्धारण करेगा जो इसके दायरे से संबंधित नहीं हैं, या दूसरे शब्दों में यदि फ़ंक्शन के दुष्प्रभाव हैं। यह समझना भी महत्वपूर्ण है कि किसी फ़ंक्शन का परिणाम कहां उपलब्ध कराया जाना है।
>>> def list_add3(lin):
lin += [3]
return lin
>>> a = [1, 2, 3]
>>> b = list_add3(a)
>>> b
[1, 2, 3, 3]
>>> a
[1, 2, 3, 3]
यहां, गलती यह सोचने की है कि lin
फ़ंक्शन के पैरामीटर के रूप में, स्थानीय रूप से संशोधित किया जा सकता है। इसके बजाय, lin
और a
ही वस्तु संदर्भ। जैसा कि यह ऑब्जेक्ट उत्परिवर्तनीय है, संशोधन को इन-प्लेस किया जाता है, जिसका अर्थ है कि lin
और a
दोनों द्वारा संदर्भित ऑब्जेक्ट को संशोधित किया गया है। lin
को वास्तव में वापस करने की आवश्यकता नहीं है, क्योंकि हमारे पास पहले से ही इस वस्तु का एक संदर्भ a
। a
और b
एंड एक ही ऑब्जेक्ट को संदर्भित करता है।
यह tuples के लिए समान नहीं है।
>>> def tuple_add3(tin):
tin += (3,)
return tin
>>> a = (1, 2, 3)
>>> b = tuple_add3(a)
>>> b
(1, 2, 3, 3)
>>> a
(1, 2, 3)
फ़ंक्शन की शुरुआत में, tin
और a
संदर्भ एक ही वस्तु। लेकिन यह एक अपरिवर्तनीय वस्तु है। समारोह इसे संशोधित करने की कोशिश करता तो जब, tin
, संशोधन के साथ एक नई वस्तु प्राप्त करते हुए a
मूल वस्तु के लिए एक संदर्भ रहता है। इस स्थिति में, tin
वापस करना अनिवार्य है, या नई वस्तु खो जाएगी।
व्यायाम
>>> def yoda(prologue, sentence):
sentence.reverse()
prologue += " ".join(sentence)
return prologue
>>> focused = ["You must", "stay focused"]
>>> saying = "Yoda said: "
>>> yoda_sentence = yoda(saying, focused)
नोट: reverse
इन-प्लेस संचालित करता है
आप इस फ़ंक्शन के बारे में क्या सोचते हैं? क्या इसके साइड इफेक्ट्स हैं? क्या रिटर्न जरूरी है? कॉल करने के बाद, का मूल्य क्या है saying
? focused
? यदि फ़ंक्शन को समान मापदंडों के साथ फिर से बुलाया जाता है तो क्या होता है?