जब आप एक चर का मूल्य दूसरे चर में पायथन में देते हैं तो क्या होता है?


80

यह अजगर सीखने का मेरा दूसरा दिन है (मैं C ++ और कुछ OOP की मूल बातें जानता हूं।), और मुझे अजगर में चर के बारे में कुछ मामूली भ्रम है।

यहाँ मैं उन्हें वर्तमान में कैसे समझ सकता हूँ:

पायथन चर वस्तुओं के संदर्भ (या संकेत?) हैं (जो या तो परस्पर या अपरिवर्तनीय हैं)। जब हमारे पास कुछ ऐसा होता है num = 5, तो अपरिवर्तनीय ऑब्जेक्ट 5को मेमोरी में कहीं बनाया जाता है, और नाम-ऑब्जेक्ट संदर्भ जोड़ी numएक निश्चित नामस्थान में बनाई जाती है। जब हमारे पास है a = num, तो कुछ भी कॉपी नहीं किया जा रहा है, लेकिन अब दोनों चर एक ही वस्तु को संदर्भित करते हैं और एक aही नामस्थान में जोड़ दिए जाते हैं।

यह वह जगह है जहां मेरी पुस्तक, पायथन के साथ उबाऊ सामान को स्वचालित करती है , मुझे भ्रमित करती है। जैसा कि यह एक नौसिखिया पुस्तक है, इसमें वस्तुओं, नामस्थानों आदि का उल्लेख नहीं है, और यह निम्नलिखित कोड को समझाने का प्रयास करता है:

>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42

यह जो स्पष्टीकरण प्रदान करता है, वह सी ++ पुस्तक की तरह ही है, जिसे लेकर मैं खुश नहीं हूं क्योंकि हम वस्तुओं के संदर्भ / संकेत के साथ काम कर रहे हैं। तो इस मामले में, मुझे लगता है कि 3 जी लाइन में, जैसा कि पूर्णांक अपरिवर्तनीय हैं, spamको मेमोरी में एक अलग स्थान पर एक पूरी तरह से नया पॉइंटर / संदर्भ सौंपा जा रहा है, अर्थात मेमोरी जिसे यह शुरू में इंगित करता था कि संशोधित नहीं किया गया था। इसलिए हमारे पास cheeseसंदर्भित प्रारंभिक वस्तु है spam। क्या यह सही स्पष्टीकरण है?



5
आप संग्रहीत 42में spamअब आप संग्रहीत, spamपनीर साधन में cheese = 42, उसके बाद आप प्रतिस्थापित spamकरने के लिए 100, आप संपादित नहीं किया cheeseहै यही वजह है कि cheeseअब भी है 42

15
जैसा कि जोनास अपने जवाब में बताते हैं, अपरिवर्तनीयता यहां एक लाल हेरिंग है: यदि आप एक सूची की तरह एक परस्पर वस्तु का उपयोग करते हैं, तो आपको बिल्कुल वही परिणाम मिलेगा। मुद्दा यह है कि आप चर को एक नए संदर्भ में पुन: असाइन करते हैं।
डैनियल रोसमैन


27
@ मिनि: मैं पायथन वेरिएबल्स को बकेट्स के बजाय लेबल के रूप में सोचना पसंद करता हूं। आपने spam42 नंबर पर लेबल चिपका दिया है । इसके बाद, आपने लेबल cheeseको उस चीज़ पर चिपका दिया है, जो लेबल के spamऊपर नहीं है , आप पर निर्भर करता है । फिर आपने उस spamलेबल को
छीलकर

जवाबों:


81

C ++ डेवलपर के रूप में आप पायथन वेरिएबल्स को पॉइंटर्स के रूप में सोच सकते हैं।

इस प्रकार जब आप लिखते हैं spam = 100, तो इसका मतलब है कि आप "पॉइंटर असाइन करें", जो पहले ऑब्जेक्ट 42को इंगित करने के लिए ऑब्जेक्ट को इंगित कर रहा था 100

इससे पहले, cheeseउसी वस्तु को इंगित करने के लिए निर्दिष्ट किया गया था spam, जो 42उस समय हुआ था। चूंकि आपने संशोधित नहीं किया है cheese, यह अभी भी इंगित करता है 42

इस मामले में अपरिवर्तनीयता का इससे कोई लेना-देना नहीं है, क्योंकि सूचक असाइनमेंट द्वारा इंगित की जा रही वस्तु के बारे में कुछ भी नहीं बदलता है।


2
js में समान वस्तुएं
दुष्यंत बंगाल

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

21

जिस तरह से मैं इसे देखता हूं वहां एक भाषा के विभिन्न दृष्टिकोण हैं।

  • "भाषा वकील" परिप्रेक्ष्य।
  • "व्यावहारिक प्रोग्रामर" परिप्रेक्ष्य।
  • "कार्यान्वयनकर्ता" परिप्रेक्ष्य।

भाषा के वकील के दृष्टिकोण से अजगर चर हमेशा "बिंदु" एक वस्तु पर। हालाँकि, जावा और C ++ के विपरीत == <=> = का behvaiour आदि चर के बिंदु प्रकारों पर निर्भर करता है जो कि चर बिंदु पर होते हैं। इसके अलावा अजगर स्मृति प्रबंधन भाषा द्वारा नियंत्रित किया जाता है।

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

कार्यान्वयनकर्ताओं के दृष्टिकोण से अधिकांश भाषाओं में कुछ प्रकार के नियम होते हैं, जैसे कि यदि निर्दिष्ट व्यवहार सही हैं, तो कार्यान्वयन सही है, भले ही चीजें वास्तव में हुड के तहत कैसे की गई हों।

तो हाँ, आपका स्पष्टीकरण भाषा वकील के दृष्टिकोण से सही है। आपकी पुस्तक व्यावहारिक प्रोग्रामर दृष्टिकोण से सही है। वास्तव में कार्यान्वयन क्या होता है, यह कार्यान्वयन पर निर्भर करता है। Cpython पूर्णांकों में वास्तविक वस्तुएं हैं, हालांकि छोटे मूल्य के पूर्णांकों को नए सिरे से बनाए जाने के बजाय कैश पूल से लिया जाता है। मुझे यकीन नहीं है कि अन्य कार्यान्वयन (जैसे कि पीपा और जेथॉन) क्या करते हैं।

* यहाँ परस्पर और अपरिवर्तनीय वस्तुओं के बीच अंतर पर ध्यान दें। एक परिवर्तनशील वस्तु के साथ हमें इसे "एक मूल्य की तरह" व्यवहार करने के बारे में सावधान रहना होगा क्योंकि कुछ अन्य कोड इसे म्यूट कर सकते हैं। एक अपरिवर्तनीय वस्तु के साथ हमें ऐसी कोई चिंता नहीं है।


20

यह सही है कि आप संकेत के रूप में चर की कम या ज्यादा बात कर सकते हैं। हालांकि उदाहरण कोड यह समझाने में बहुत मदद करेगा कि यह वास्तव में कैसे काम कर रहा है।

सबसे पहले, हम idफ़ंक्शन का भारी उपयोग करेंगे :

किसी वस्तु की "पहचान" लौटाएं। यह एक पूर्णांक है जो अपने जीवनकाल के दौरान इस वस्तु के लिए अद्वितीय और स्थिर रहने की गारंटी है। गैर-अतिव्यापी जीवनकाल वाली दो वस्तुओं में एक ही आईडी () मूल्य हो सकता है।

यह संभावना है कि यह आपकी मशीन पर विभिन्न पूर्ण मान लौटाएगा।

इस उदाहरण पर विचार करें:

>>> foo = 'a string'
>>> id(foo) 
4565302640
>>> bar = 'a different string'
>>> id(bar)
4565321816
>>> bar = foo
>>> id(bar) == id(foo)
True
>>> id(bar)
4565302640

आप वह देख सकते हैं:

  • मूल फू / बार में अलग-अलग आईडी होते हैं, क्योंकि वे अलग-अलग वस्तुओं की ओर इशारा करते हैं
  • जब बार को फू को सौंपा जाता है, तो उनकी आईडी अब समान होती है। यह उन दोनों के समान है जो मेमोरी में एक ही स्थान की ओर इशारा करते हैं जो आप C ++ पॉइंटर बनाने में देखते हैं

जब हम फू के मूल्य को बदलते हैं, तो इसे एक अलग आईडी को सौंपा जाता है:

>>> foo = 42
>>> id(foo)
4561661488
>>> foo = 'oh no'
>>> id(foo)
4565257832

एक दिलचस्प अवलोकन यह भी है कि पूर्णांकों में निहित है कि यह कार्यक्षमता 256 तक है:

>>> a = 100
>>> b = 100
>>> c = 100
>>> id(a) == id(b) == id(c)
True

हालांकि 256 के पार यह अब सच नहीं है:

>>> a = 256
>>> b = 256
>>> id(a) == id(b)
True
>>> a = 257
>>> b = 257
>>> id(a) == id(b)
False

हालांकि बताए aकरने के लिए bवास्तव में के रूप में पहले से पता चला आईडी एक ही रखना होगा:

>>> a = b
>>> id(a) == id(b)
True

18

पायथन न तो पास-दर-संदर्भ या पास-बाय-वैल्यू है। पायथन चर बिंदु नहीं हैं, वे संदर्भ नहीं हैं, वे मूल्य नहीं हैं। अजगर चर नाम हैं

इसे "पास-बाय-उर्फ" के रूप में सोचें यदि आपको समान वाक्यांश प्रकार की आवश्यकता है, या संभवतः "पास-बाय-ऑब्जेक्ट", क्योंकि आप किसी भी चर से उसी वस्तु को उत्परिवर्तित कर सकते हैं जो इसे इंगित करता है, यदि यह परस्पर है, लेकिन पुन: असाइनमेंट एक चर (उपनाम) केवल एक चर को बदलता है।

यदि यह मदद करता है: C वैरिएबल वह बॉक्स है जिसमें आप मान लिखते हैं। पायथन नाम ऐसे टैग हैं जिन्हें आप मूल्यों पर रखते हैं।

पायथन वैरिएबल का नाम वैश्विक (या स्थानीय) नाम स्थान की एक कुंजी है, जो प्रभावी रूप से एक शब्दकोश है। अंतर्निहित मान स्मृति में कुछ ऑब्जेक्ट है। असाइनमेंट उस ऑब्जेक्ट को एक नाम देता है। एक चर का दूसरे चर पर संस्थापन का मतलब है कि दोनों चर एक ही वस्तु के नाम हैं। एक चर के पुन: असाइनमेंट से उस चर का नाम किस वस्तु से बदला जाता है, दूसरे चर को बदले बिना। आपने टैग को स्थानांतरित कर दिया है, लेकिन पिछली वस्तु या उस पर कोई अन्य टैग नहीं बदला है।

सीपीथॉन कार्यान्वयन के अंतर्निहित सी कोड में, प्रत्येक पायथन ऑब्जेक्ट एक है PyObject*, इसलिए आप इसे सी की तरह काम करने के बारे में सोच सकते हैं यदि आपके पास कभी केवल डेटा के लिए संकेत थे (कोई पॉइंटर्स-टू-पॉइंटर्स, कोई सीधे-पास मान नहीं)।

आप कह सकते हैं कि पायथन पास-बाय-वैल्यू है, जहाँ मान पॉइंटर्स हैं ... या आप कह सकते हैं कि पायथन पास-बाय-रेफरेंस है, जहाँ संदर्भ प्रतियां हैं।


1
इसे "पास-बाय-नेम" कहने में समस्या यह है कि "नाम से कॉल" नामक एक पैरामीटर पासिंग कन्वेंशन है, जिसका पूरी तरह से अर्थ है। नाम से कॉल में, पैरामीटर अभिव्यक्ति का मूल्यांकन हर बार फ़ंक्शन पैरामीटर का उपयोग करता है, और कभी मूल्यांकन नहीं किया जाता है कि क्या फ़ंक्शन पैरामीटर का उपयोग नहीं करता है।
user2357112

11

जब आप spam = 100अजगर चलाते हैं , तो मेमोरी में एक और वस्तु बनाते हैं, लेकिन मौजूदा नहीं बदलते हैं। तो आप अभी भी cheese42 और spam100 के लिए सूचक है


8

spam = 100लाइन में जो कुछ हो रहा है वह पिछले मान (पॉइंटर टू ऑब्जेक्ट ऑफ़ टाइप टू intवैल्यू 42) के साथ दूसरे पॉइंटर के साथ दूसरी ऑब्जेक्ट (टाइप int, वैल्यू 100) का प्रतिस्थापन है


इंटेगर मूल्य वस्तु हैं जो स्टैक सही पर हैं?
गर्ट कोमर

हां, वे कुछ ऐसी वस्तुएं हैं जिन्हें आप new Class()C ++ में सिंटैक्स का उपयोग करके बनाते हैं । इसके अलावा, पायथन में, कुछ भीobject कक्षा / उपवर्ग का एक उदाहरण है ।
बकाटौरबल

4
@ GertKommer CPython में कम से कम, सभी ऑब्जेक्ट्स ढेर पर रहते हैं। एक "मूल्य वस्तु" का कोई भेद नहीं है। बस वस्तुएं हैं, और सब कुछ एक वस्तु है । यही कारण है कि पायथन संस्करण के आधार पर एक विशिष्ट इंट का आकार लगभग 28 बाइट्स है, क्योंकि इसमें पूरे Py_Object ओवरहेड हैं। छोटे-छोटे किट्स को सीपीथॉन ऑप्टिमाइज़ेशन के रूप में कैश्ड किया जाता है।
juanpa.arrivillaga

8

जैसा कि @DeepSpace ने टिप्पणियों में उल्लेख किया है, नेड बैचेल्ड एक ब्लॉग में मूल्यों (नामों) और असाइनमेंट को नष्ट करने के लिए एक महान काम करता है, जिसमें से उसने PyCon नाम और मूल्यों के बारे में PyCon 2015, तथ्य और मिथकों पर एक बात की । यह किसी भी स्तर पर निपुणता के लिए पाइथोनिस्टस के लिए व्यावहारिक हो सकता है।


1

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

अब जब आप किसी वैरिएबल को मान प्रदान करते हैं, तो पायथन वास्तव में उन संदर्भों को असाइन करता है जो ऑब्जेक्ट्स को आवंटित स्मृति स्थानों के लिए संकेत हैं:

# x holds a reference to the memory location allocated for  
# the object(type=string, value="Hello World", refCounter=1)

x = "Hello World" 

अब जब आप विभिन्न प्रकार की वस्तुओं को एक ही चर में निर्दिष्ट करते हैं, तो आप वास्तव में संदर्भ बदलते हैं ताकि यह एक अलग वस्तु (यानी अलग स्मृति स्थान) को इंगित करे। जब तक आप किसी भिन्न संदर्भ (और इस प्रकार ऑब्जेक्ट) को एक चर में निर्दिष्ट करते हैं, तब तक कचरा कलेक्टर पिछली वस्तु को आवंटित स्थान को तुरंत पुनः प्राप्त कर लेगा, यह मानते हुए कि यह स्रोत कोड में किसी अन्य चर द्वारा संदर्भित नहीं किया जा रहा है:

# x holds a reference to the memory location allocated for  
# the object(type=string, value="Hello World", refCounter=1)

x = "Hello World" 

# Now x holds the reference to a different object(type=int, value=10, refCounter=1)
# and object(type=string, value="Hello World", refCounter=0) -which is not refereced elsewhere
# will now be garbage-collected.
x = 10

अब आपके उदाहरण पर आते हैं,

spam ऑब्जेक्ट के संदर्भ (प्रकार = int, मान = 42, refCounter = 1) रखती है:

>>> spam = 42

अब cheeseऑब्जेक्ट के संदर्भ को भी रखेगा (टाइप करें = int, value = 42, refCounter = 2)

>>> cheese = spam

अब स्पैम एक अलग ऑब्जेक्ट (प्रकार = int, मान = 100, refCounter = 1) का संदर्भ रखता है

>>> spam = 100
>>> spam
100

लेकिन पनीर ऑब्जेक्ट को इंगित करता रहेगा (टाइप करें = इंट, वैल्यू = 42, रिफांकेर = 1)

>>> cheese
42

0

जब आप स्टोर करते हैं spam = 42, तो यह मेमोरी में एक ऑब्जेक्ट बनाता है। फिर आप असाइन करते हैं cheese = spam, यह संदर्भित ऑब्जेक्ट को असाइन करता spamहै cheese। और अंत में, जब आप बदलते हैं spam = 100, तो यह केवल spamवस्तु में बदल जाता है। तो cheese = 42


7
"फिर आप पनीर = स्पैम असाइन करते हैं, यह मेमोरी में एक और ऑब्जेक्ट बनाता है" नहीं, यह नहीं है। यह द्वारा संदर्भित ऑब्जेक्ट असाइन करता spamहै cheese। कोई नई वस्तु नहीं बनाई जाती है।
juanpa.arrivillaga

-1

numpy.copy () फ़ंक्शन पेज में एक स्पष्टीकरण है

https://docs.scipy.org/doc/numpy/reference/generated/numpy.copy.html

जो उदाहरण देता है वह इस प्रकार है:

एक संदर्भ x और एक प्रतिलिपि z के साथ एक सरणी x बनाएँ:

x = np.array([1, 2, 3])
y = x
z = np.copy(x)

ध्यान दें कि, जब हम x को बदलते हैं, y बदलता है, लेकिन z नहीं:

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