मैं संदर्भ से चर कैसे पार करूं?


2627

पायथन प्रलेखन अस्पष्ट लगता है कि क्या संदर्भ या मूल्य द्वारा पैरामीटर पारित किए जाते हैं, और निम्न कोड अपरिवर्तित मूल्य 'मूल' पैदा करता है

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

क्या वास्तविक संदर्भ द्वारा चर को पारित करने के लिए मैं कुछ कर सकता हूं?


23
एक छोटी व्याख्या / स्पष्टीकरण के लिए इस स्टैकओवरफ़्लो प्रश्न का पहला उत्तर देखें । जैसा कि तार अपरिवर्तनीय हैं, उन्हें बदला नहीं जाएगा और एक नया चर बनाया जाएगा, इस प्रकार "बाहरी" चर का अभी भी समान मूल्य है।
फिल्स

10
ब्लेयरकोनराड के उत्तर में कोड अच्छा है, लेकिन डेविडकोरनैपो और डैरेनथोमास द्वारा प्रदान की गई व्याख्या सही है।
एथन फुरमैन

70
चयनित उत्तर को पढ़ने से पहले, कृपया इस छोटे पाठ को पढ़ने पर विचार करें अन्य भाषाओं में "चर" हैं, पायथन में "नाम" हैं । "चर" और "संदर्भ" के बजाय "नाम" और "ऑब्जेक्ट" के बारे में सोचें और आपको बहुत सी समान समस्याओं से बचना चाहिए।
lqc

24
इसके लिए किसी वर्ग की जरूरत क्यों पड़ती है? यह जावा नहीं है: पी
पीटर आर

2
एक अन्य वर्कअराउंड इस तरह एक रैपर 'संदर्भ' बनाने के लिए है: Ref = type ('', (), {'n': 1}) stackoverflow.com/a/1123054/409638
robert

जवाबों:


2830

तर्क असाइनमेंट द्वारा पारित किए जाते हैं । इसके पीछे तर्क दोहरा है:

  1. पैरामीटर में पारित कर दिया वास्तव में एक है संदर्भ किसी ऑब्जेक्ट को (लेकिन संदर्भ मूल्य से पारित हो जाता है)
  2. कुछ डेटा प्रकार परस्पर हैं, लेकिन अन्य नहीं हैं

इसलिए:

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

  • यदि आप किसी विधि से अपरिवर्तनीय वस्तु को पास करते हैं , तो आप अभी भी बाहरी संदर्भ को अस्वीकार नहीं कर सकते हैं, और आप ऑब्जेक्ट को म्यूट भी नहीं कर सकते।

इसे और अधिक स्पष्ट करने के लिए, आइए कुछ उदाहरण देखें।

सूची - एक उत्परिवर्तित प्रकार

आइए उस सूची को संशोधित करने का प्रयास करें जो एक विधि से पारित की गई थी:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

आउटपुट:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

चूंकि इसमें दिया गया पैरामीटर इसका संदर्भ है outer_list, इसकी प्रति नहीं, हम इसे बदलने के लिए उत्परिवर्तन सूची विधियों का उपयोग कर सकते हैं और बाहरी दायरे में परिलक्षित परिवर्तन हो सकते हैं।

अब देखते हैं कि जब हम एक पैरामीटर के रूप में पारित किए गए संदर्भ को बदलने की कोशिश करते हैं तो क्या होता है:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

आउटपुट:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

चूंकि the_listपैरामीटर मान द्वारा पारित किया गया था, इसलिए इसे एक नई सूची सौंपने का कोई प्रभाव नहीं था कि विधि के बाहर का कोड देख सकता था। the_listकी एक प्रति था outer_listसंदर्भ, और हम था the_listएक नई सूची इंगित, लेकिन वहाँ जहां बदलने के लिए कोई रास्ता नहीं था outer_listउठाई।

स्ट्रिंग - एक अपरिवर्तनीय प्रकार

यह अपरिवर्तनीय है, इसलिए स्ट्रिंग की सामग्री को बदलने के लिए हम कुछ नहीं कर सकते

अब, संदर्भ बदलने की कोशिश करते हैं

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

आउटपुट:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

फिर से, चूंकि the_stringपैरामीटर मान द्वारा पारित किया गया था , इसलिए इसे एक नया स्ट्रिंग असाइन करने का कोई प्रभाव नहीं था कि विधि के बाहर का कोड देख सकता था। the_stringकी एक प्रति था outer_stringसंदर्भ, और हम था the_stringएक नया स्ट्रिंग के लिए बिंदु, लेकिन वहाँ जहां बदलने के लिए कोई रास्ता नहीं था outer_stringउठाई।

मुझे उम्मीद है कि इससे चीजें थोड़ी साफ होंगी।

संपादित करें: यह नोट किया गया है कि यह इस सवाल का जवाब नहीं देता है कि @ डेविड ने मूल रूप से पूछा, "क्या कुछ ऐसा है जिसे मैं वास्तविक संदर्भ द्वारा चर को पारित करने के लिए कर सकता हूं?" चलो उस पर काम करते हैं।

हम इसके आसपास कैसे पहुंचते हैं?

जैसा कि @ एंड्रिया के जवाब से पता चलता है, आप नया मान वापस कर सकते हैं। यह चीजों को पास करने के तरीके को नहीं बदलता है, लेकिन क्या आपको वह जानकारी प्राप्त करने देता है जो आप वापस चाहते हैं:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

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

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

हालांकि यह थोड़ा बोझिल लगता है।


165
फिर वही सी में है, जब आप "संदर्भ द्वारा" पास करते हैं, तो आप वास्तव में संदर्भ द्वारा मूल्य पास कर रहे हैं ... संदर्भ द्वारा "परिभाषित करें": पी
एंड्रिया अंबु

104
मुझे यकीन नहीं है कि मैं आपकी शर्तों को समझता हूं। मैं थोड़ी देर के लिए सी गेम से बाहर हो गया, लेकिन जब मैं इसमें था, तो कोई "संदर्भ द्वारा पास" नहीं था - आप चीजों को पास कर सकते थे, और यह हमेशा मूल्य से गुजरता था, इसलिए जो कुछ भी था वह पैरामीटर सूची में था नकल की गई। लेकिन कभी-कभी वह चीज़ एक पॉइंटर थी, जिसे कोई भी मेमोरी के टुकड़े (आदिम, सरणी, संरचना, जो भी हो) का अनुसरण कर सकता था, लेकिन आप उस पॉइंटर को नहीं बदल सकते थे जिसे बाहरी दायरे से कॉपी किया गया था - जब आप फ़ंक्शन के साथ किए गए थे , मूल सूचक अभी भी उसी पते की ओर इशारा करता है। C ++ ने संदर्भ प्रस्तुत किए, जो अलग तरह से व्यवहार करते थे।
ब्लेयर कॉनरैड

34
@Zac बॉलिंग मुझे वास्तव में नहीं मिली कि आप जो कह रहे हैं वह प्रासंगिक है, व्यावहारिक अर्थ में, इस उत्तर के लिए। यदि एक पायथन नवागंतुक रेफरी / वैल से गुजरने के बारे में जानना चाहता था, तो इस उत्तर से takeaway है: 1- आप उस संदर्भ का उपयोग कर सकते हैं जो एक फ़ंक्शन को उसके तर्कों के रूप में प्राप्त होता है, चर के 'बाहर' मान को संशोधित करने के लिए, जब तक जैसा कि आप नए ऑब्जेक्ट को संदर्भित करने के लिए पैरामीटर को पुन: असाइन नहीं करते हैं। 2- एक अपरिवर्तनीय प्रकार को असाइन करना हमेशा एक नई वस्तु का निर्माण करेगा , जो उस संदर्भ को तोड़ता है जो आपके पास बाहरी चर के लिए था।
कैम जैक्सन

11
@CamJackson, आपको एक बेहतर उदाहरण की आवश्यकता है - संख्याएं पायथन में अपरिवर्तनीय वस्तुएं भी हैं। इसके अलावा, यह कहना सही नहीं होगा कि बराबरी के बायीं ओर बिना किसी भी असाइनमेंट के किसी नए ऑब्जेक्ट को नाम फिर से असाइन किया जाएगा कि यह अपरिवर्तनीय है या नहीं? कॉलर्स परिप्रेक्ष्य से सूची की सामग्री को संशोधित नहींdef Foo(alist): alist = [1,2,3] करेगा ।
मार्क रैनसम

59
-1। दिखाया गया कोड अच्छा है, यह स्पष्टीकरण कि कैसे पूरी तरह से गलत है। क्यों सही स्पष्टीकरण के लिए DavidCournapeau या DarenThomas द्वारा उत्तर देखें।
एथन फुरमान

685

यह समस्या इस बात की गलतफहमी से है कि पायथन में चर क्या हैं। यदि आप अधिकांश पारंपरिक भाषाओं में उपयोग किए जाते हैं, तो आपके पास निम्न अनुक्रम में क्या होता है, इसका एक मानसिक मॉडल है:

a = 1
a = 2

आप मानते हैं कि aएक मेमोरी लोकेशन है जो वैल्यू 1को स्टोर करता है, फिर वैल्यू को स्टोर करने के लिए अपडेट किया जाता है 2। यही नहीं पायथन में चीजें काम करती हैं। बल्कि, aमूल्य के साथ किसी ऑब्जेक्ट के संदर्भ के रूप में शुरू होता है 1, फिर मूल्य के साथ किसी ऑब्जेक्ट के संदर्भ के रूप में पुन: असाइन किया जाता है 2। उन दो वस्तुओं को सह-अस्तित्व जारी रह सकता है, भले ही aवह पहले वाले को संदर्भित नहीं करता हो; वास्तव में उन्हें कार्यक्रम के भीतर किसी भी अन्य संदर्भ द्वारा साझा किया जा सकता है।

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

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variableस्ट्रिंग ऑब्जेक्ट का संदर्भ है 'Original'। जब आप कॉल Changeकरते हैं तो आप varऑब्जेक्ट के लिए दूसरा संदर्भ बनाते हैं । फ़ंक्शन के अंदर आप varएक अलग स्ट्रिंग ऑब्जेक्ट के संदर्भ को फिर से असाइन करते हैं 'Changed', लेकिन संदर्भ self.variableअलग है और बदलता नहीं है।

इसके चारों ओर एक ही रास्ता है कि एक उत्परिवर्तित वस्तु को पास किया जाए। क्योंकि दोनों संदर्भ एक ही वस्तु को संदर्भित करते हैं, दोनों स्थानों पर वस्तु में कोई भी परिवर्तन परिलक्षित होता है।

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

106
अच्छी रसीली व्याख्या। आपका पैराग्राफ "जब आप किसी फ़ंक्शन को कॉल करते हैं ..." सबसे अच्छा स्पष्टीकरणों में से एक है जो मैंने बल्कि गूढ़ वाक्यांश के बारे में सुना है जो कि 'पायथन फ़ंक्शन पैरामीटर संदर्भ हैं, मूल्य द्वारा पारित किया गया है।' मुझे लगता है कि यदि आप उस पैराग्राफ को अकेले समझते हैं, तो बाकी सब कुछ केवल समझ में आता है और वहाँ से एक तार्किक निष्कर्ष के रूप में बहता है। तब आपको बस एक नई वस्तु बनाते समय जागरूक होना होगा और जब आप किसी मौजूदा को संशोधित कर रहे हों।
कैम जैक्सन

3
लेकिन आप संदर्भ को कैसे पुन: सौंप सकते हैं? मैंने सोचा था कि आप 'var' का पता नहीं बदल सकते, लेकिन आपका स्ट्रिंग "चेंजेड" अब 'var' मेमोरी एड्रेस में स्टोर होने वाला है। आपका वर्णन ऐसा लगता है कि "परिवर्तित" और "मूल" के बजाय स्मृति में अलग-अलग स्थानों से संबंधित हैं और आप बस 'var' को एक अलग पते पर स्विच करते हैं। क्या वो सही है?
ग्लासजॉव

8
@Glassjawed, मुझे लगता है कि आप इसे प्राप्त कर रहे हैं। "परिवर्तित" और "मूल" अलग-अलग मेमोरी पतों पर दो अलग-अलग स्ट्रिंग ऑब्जेक्ट हैं और एक से दूसरे को इंगित करने के लिए 'var' परिवर्तन होते हैं।
मार्क रैनसम

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

1
@MinhTran सबसे सरल शब्दों में, एक संदर्भ कुछ ऐसा है जो किसी वस्तु को "संदर्भित करता है"। उस का भौतिक प्रतिनिधित्व सबसे अधिक संभावना सूचक है, लेकिन यह केवल एक कार्यान्वयन विवरण है। यह वास्तव में दिल में एक अमूर्त धारणा है।
मार्क

329

मुझे अन्य उत्तर लंबे और जटिल लगे, इसलिए मैंने पायथन चरों और मापदंडों के व्यवहार के तरीके को समझाने के लिए यह सरल चित्र बनाया। यहां छवि विवरण दर्ज करें


2
प्यारा, सूक्ष्म अंतर को स्पष्ट करना आसान बनाता है कि एक मध्यवर्ती असाइनमेंट है, जो स्पष्ट रूप से एक आकस्मिक दर्शक के लिए नहीं है। +1
user22866

9
इससे कोई फर्क नहीं पड़ता कि A परस्पर है या नहीं। यदि आप B को कुछ अलग असाइन करते हैं, तो A नहीं बदलता है । यदि कोई वस्तु उत्परिवर्तनीय है, तो आप इसे उत्परिवर्तित कर सकते हैं, सुनिश्चित करें। लेकिन इसका नाम के साथ सीधे असाइनमेंट से कोई लेना-देना नहीं है ..
मार्टिज़न पीटरर्स

1
@Martijn तुम सही हो। मैंने उस उत्तर के भाग को हटा दिया जिसमें उत्परिवर्तन का उल्लेख है। मुझे नहीं लगता कि यह अब कोई भी सरल हो सकता है।
जिनाडिक्स

4
अद्यतन के लिए धन्यवाद, बहुत बेहतर! अधिकांश लोगों को एक सदस्यता के लिए क्या भ्रमित करता है; उदाहरण के लिए B[0] = 2, प्रत्यक्ष असाइनमेंट B = 2,।
मार्टिन पीटर्स

11
"बी को सौंपा गया है" क्या यह अस्पष्ट नहीं है? मैं साधारण अंग्रेजी में लगता है कि हो सकता है या तो A=Bया B=A
हत्शेपसुत

240

यह न तो पास-दर-मूल्य या पास-बाय-संदर्भ है - यह कॉल-बाय-ऑब्जेक्ट है। इसे देखें, फ्रेड्रिक लुंड द्वारा:

http://effbot.org/zone/call-by-object.htm

यहाँ एक महत्वपूर्ण उद्धरण है:

"... चर [नाम] वस्तुएं नहीं हैं; उन्हें अन्य चर द्वारा निरूपित नहीं किया जा सकता है या वस्तुओं द्वारा संदर्भित नहीं किया जा सकता है।"

आपके उदाहरण में, जब Changeविधि को बुलाया जाता है - इसके लिए एक नाम स्थान बनाया जाता है; और varस्ट्रिंग ऑब्जेक्ट के लिए उस नाम स्थान के भीतर एक नाम बन जाता है 'Original'। उस वस्तु का तब दो नामस्थानों में एक नाम होता है। अगला, एक नई स्ट्रिंग ऑब्जेक्ट से var = 'Changed'बांधता varहै, और इस प्रकार विधि का नाम स्थान भूल जाता है 'Original'। अंत में, उस नाम स्थान को भुला दिया जाता है, और 'Changed'उसके साथ स्ट्रिंग ।


21
मुझे खरीदना मुश्किल लगता है। मेरे लिए जावा के समान है, पैरामीटर स्मृति में वस्तुओं के संकेत हैं, और उन बिंदुओं को स्टैक या रजिस्टरों के माध्यम से पारित किया जाता है।
लुसियानो

10
यह जावा की तरह नहीं है। किसी एक मामले में यह समान नहीं है। तुच्छ कार्य के बारे में सोचो lambda x: x। इसे x = [1, 2, 3] और x = (1, 2, 3) के लिए लागू करें। पहले मामले में, लौटाया गया मूल्य इनपुट की एक प्रति होगी, और दूसरे मामले में समान।
डेविड कोर्टन्यू

26
नहीं, यह है वास्तव में वस्तुओं के लिए जावा के अर्थ विज्ञान की तरह। मुझे यकीन नहीं है कि आपका क्या मतलब है "पहले मामले में, लौटाया गया मूल्य इनपुट की एक प्रति होगी, और दूसरे मामले में समान होगा।" लेकिन यह कथन स्पष्ट रूप से गलत प्रतीत होता है।
माइक ग्राहम

23
यह जावा के समान ही है। वस्तु संदर्भ मूल्य द्वारा पारित किए जाते हैं। जो कोई भी अलग तरीके से सोचता है, उसे एक swapफ़ंक्शन के लिए पायथन कोड संलग्न करना चाहिए जो दो संदर्भों को स्वैप कर सकता है, जैसे: a = [42] ; b = 'Hello'; swap(a, b) # Now a is 'Hello', b is [42]
cayhorstmann

24
जब आप जावा में ऑब्जेक्ट पास करते हैं तो यह बिल्कुल जावा जैसा होता है। हालांकि, जावा में भी आदिम हैं, जो कि आदिम के मूल्य की नकल करके पारित किए जाते हैं। इस प्रकार वे उस मामले में भिन्न हैं।
क्लॉडिउ

174

संदर्भ / मूल्य के बजाय असाइनमेंट द्वारा पारित किए जा रहे सामान के बारे में सोचें । इस तरह, यह हमेशा स्पष्ट होता है कि जब तक आप समझते हैं कि सामान्य असाइनमेंट के दौरान क्या होता है।

इसलिए, जब किसी फ़ंक्शन / विधि के लिए सूची पास करते हैं, तो सूची को पैरामीटर नाम को सौंपा जाता है। सूची में लागू करने के परिणामस्वरूप सूची को संशोधित किया जाएगा। फ़ंक्शन के अंदर सूची को पुन: असाइन करने से मूल सूची नहीं बदलेगी, क्योंकि:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

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


3
पहली नज़र में यह जवाब मूल सवाल को दरकिनार कर देता है। एक दूसरे पढ़ने के बाद मुझे पता चला है कि इससे मामला काफी हद तक स्पष्ट हो जाता है। इस "नाम असाइनमेंट" अवधारणा का एक अच्छा अनुसरण यहाँ पाया जा सकता है: कोड लाइक ए पाइथोनिस्टा: आइडोमैटिक पायथन
क्रिश्चियन ग्रॉउ नोव

65

एफबॉट (उर्फ फ्रेड्रिक लुन्ध) ने पायथन के वैरिएबल पासिंग स्टाइल को कॉल-बाय-ऑब्जेक्ट के रूप में वर्णित किया है: http://effbot.org/zone/call-by-object.htm

ऑब्जेक्ट्स को ढेर पर आवंटित किया जाता है और उन्हें पॉइंटर्स कहीं भी पास किया जा सकता है।

  • जब आप ऐसा असाइनमेंट करते हैं x = 1000, तो एक डिक्शनरी एंट्री बनाई जाती है, जो स्ट्रिंग "x" को वर्तमान नेमस्पेस में एक पॉइंटर से पूर्णांक ऑब्जेक्ट में एक हजार तक मैप करती है।

  • जब आप "x" को अपडेट करते हैं x = 2000, तो एक नया पूर्णांक ऑब्जेक्ट बनाया जाता है और शब्दकोश को नई ऑब्जेक्ट पर इंगित करने के लिए अपडेट किया जाता है। पुरानी एक हजार वस्तु अपरिवर्तित है (और इस बात के आधार पर जीवित हो भी सकती है कि नहीं भी हो सकती है)।

  • जब आप एक नया असाइनमेंट करते हैं जैसे कि y = x, एक नया शब्दकोश प्रविष्टि "y" बनाया जाता है जो "x" के प्रवेश के समान ऑब्जेक्ट को इंगित करता है।

  • तार और पूर्णांक जैसी वस्तुएँ अपरिवर्तनीय हैं । इसका सीधा सा अर्थ है कि ऐसी कोई विधियाँ नहीं हैं जो बनने के बाद वस्तु को बदल सकें। उदाहरण के लिए, एक बार पूर्णांक वस्तु एक-हजार बनने के बाद, यह कभी नहीं बदलेगी। गणित नए पूर्णांक वस्तुओं को बनाने के द्वारा किया जाता है।

  • सूची जैसी वस्तुएं आपस में मिलती हैं । इसका मतलब यह है कि ऑब्जेक्ट की सामग्री को ऑब्जेक्ट की ओर इशारा करते हुए कुछ भी बदला जा सकता है। उदाहरण के लिए, x = []; y = x; x.append(10); print yप्रिंट करेगा [10]। खाली सूची बनाई गई थी। दोनों "x" और "y" एक ही सूची में इंगित करते हैं। संलग्न विधि mutates (नवीनीकरण) सूची वस्तु (एक डेटाबेस के लिए एक रिकार्ड को जोड़ना) और परिणाम के लिए दोनों "x" और "y" दिखाई देता है (सिर्फ एक डेटाबेस अद्यतन के रूप में है कि डेटाबेस के लिए हर कनेक्शन को दिखाई हो जाएगा)।

आशा है कि आप के लिए इस मुद्दे को स्पष्ट करता है।


2
मैं वास्तव में एक डेवलपर से इस बारे में सीखने की सराहना करता हूं। क्या यह सच है कि id()फ़ंक्शन पॉइंटर का (ऑब्जेक्ट संदर्भ का) मान लौटाता है, जैसा कि पेपर्स का उत्तर बताता है?
ईमानदार अबे

4
@HonestAbe हाँ, CPython में id () पता देता है। लेकिन अन्य अजगर जैसे कि PyPy और Jython में, id () सिर्फ एक अद्वितीय वस्तु पहचानकर्ता है।
रेमंड हेटिंगर

59

तकनीकी रूप से, पायथन हमेशा संदर्भ मानों द्वारा पास का उपयोग करता है । मैं अपने कथन का समर्थन करने के लिए अपने दूसरे उत्तर को दोहराने जा रहा हूं।

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

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

यहाँ उदाहरण है कि साबित करता है कि पायथन संदर्भ से गुजरता है:

तर्क पारित करने का उदाहरण दिया

यदि तर्क को मूल्य द्वारा पारित किया गया था, तो बाहरी lstको संशोधित नहीं किया जा सकता है। हरे रंग की लक्षित वस्तुएं हैं (काला अंदर संग्रहीत मूल्य है, लाल वस्तु प्रकार है), पीला स्मृति है जो संदर्भ मूल्य के अंदर - तीर के रूप में खींची गई है। नीला ठोस तीर संदर्भ मान है जिसे फ़ंक्शन (धराशायी नीले तीर पथ के माध्यम से) में पारित किया गया था। बदसूरत गहरा पीला आंतरिक शब्दकोश है। (यह वास्तव में हरे दीर्घवृत्त के रूप में भी खींचा जा सकता है। रंग और आकार केवल यह कहता है कि यह आंतरिक है।)

id()संदर्भ मान (यानी, लक्ष्य वस्तु का पता) क्या है, यह जानने के लिए आप अंतर्निहित फ़ंक्शन का उपयोग कर सकते हैं ।

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

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


1
वास्तव में यह संदर्भ मूल्य द्वारा इसके पास की पुष्टि की जाती है। इस जवाब के लिए +1 हालांकि उदाहरण अच्छा नहीं था।
BugShotGG 17

30
नई शब्दावली का आविष्कार करना (जैसे "संदर्भ मूल्य से गुज़रना" या "ऑब्जेक्ट द्वारा कॉल करना" सहायक नहीं है)। "कॉल बाय (मूल्य | संदर्भ | नाम)" मानक शब्द हैं। "संदर्भ" एक मानक शब्द है। मान द्वारा संदर्भों को पास करना मानक शब्दावली का उपयोग करते हुए, पायथन, जावा और अन्य भाषाओं के एक मेजबान के व्यवहार का सटीक वर्णन करता है।
cayhorstmann 3

4
@ क्रेयॉर्स्टमैन: समस्या यह है कि पायथन वैरिएबल का अन्य भाषाओं की तरह शब्दावली अर्थ नहीं है। इस तरह, संदर्भ द्वारा कॉल यहां अच्छी तरह से फिट नहीं है। इसके अलावा, आप कैसे करते हैं वास्तव में शब्द को परिभाषित संदर्भ ? अनौपचारिक रूप से, पायथन के रास्ते को आसानी से वस्तु के पते के रूप में वर्णित किया जा सकता है। लेकिन यह पायथन के संभावित रूप से वितरित कार्यान्वयन के साथ फिट नहीं है।
pepr

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

2
उस उद्धरण के अंत में एक फुटनोट इंगित किया गया है, जिसमें लिखा है: "वास्तव में, ऑब्जेक्ट रेफरेंस द्वारा कॉल करना एक बेहतर विवरण होगा, क्योंकि यदि एक उत्परिवर्तनीय वस्तु पास हो जाती है, तो कॉल करने वाला किसी भी बदलाव को देख सकता है, जो केली करता है ... " मैं आपसे सहमत हूं कि भ्रम अन्य भाषाओं के साथ स्थापित शब्दावली को फिट करने की कोशिश के कारण होता है। शब्दार्थ एक तरफ, जिन चीजों को समझने की आवश्यकता है वे हैं: शब्दकोष / नाम स्थान, नाम बंधन संचालन और नाम का संबंध → सूचक → वस्तु (जैसा कि आप पहले से जानते हैं)।
ईमानदार अबे

56

पायथन में कोई चर नहीं हैं

पासिंग पैरामीटर को समझने की कुंजी "चर" के बारे में सोचना बंद करना है। पायथन में नाम और ऑब्जेक्ट हैं और साथ में वे चर की तरह दिखाई देते हैं, लेकिन यह हमेशा तीनों को अलग करने के लिए उपयोगी है।

  1. अजगर के नाम और वस्तुएं हैं।
  2. असाइनमेंट किसी ऑब्जेक्ट को एक नाम देता है।
  3. किसी फ़ंक्शन में किसी तर्क को पास करना किसी ऑब्जेक्ट को फ़ंक्शन का नाम (फ़ंक्शन का पैरामीटर नाम) भी बांधता है।

इसके लिए वहां यही सब है। इस प्रश्न के लिए उत्परिवर्तन अप्रासंगिक है।

उदाहरण:

a = 1

यह नाम aको उस प्रकार पूर्णांक के ऑब्जेक्ट से बांधता है जो मान 1 रखता है।

b = x

यह नाम bको उसी वस्तु से बांधता है जिस xपर वर्तमान में नाम जुड़ा हुआ है। बाद में, नाम का bअब नाम से कोई लेना-देना xनहीं है।

पायथन 3 भाषा के संदर्भ में अनुभाग 3.1 और 4.2 देखें ।

प्रश्न में उदाहरण कैसे पढ़ें

प्रश्न में दिखाए गए कोड में, कथन self.Change(self.variable)उस नाम var(फ़ंक्शन के दायरे में Change) उस वस्तु को बांधता है जो मान रखता है 'Original'और असाइनमेंट var = 'Changed'(फ़ंक्शन के निकाय में Change) उसी नाम को फिर से असाइन करता है: किसी अन्य ऑब्जेक्ट पर (जो होता है) स्ट्रिंग पकड़ना और पूरी तरह से कुछ और भी हो सकता है)।

संदर्भ से कैसे गुजरना है

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

यदि यह एक अपरिवर्तनीय वस्तु है (उदाहरण के लिए एक बूल, संख्या, स्ट्रिंग), तो जाने का तरीका इसे एक परिवर्तनशील वस्तु में लपेटना है।
इसके लिए त्वरित और गंदा समाधान एक-तत्व सूची (इसके बजाय self.variable, पास [self.variable]और फ़ंक्शन संशोधित में var[0]) है।
अधिक पायथोनिक दृष्टिकोण एक तुच्छ, एक-विशेषता वर्ग को पेश करना होगा। फ़ंक्शन वर्ग का एक उदाहरण प्राप्त करता है और विशेषता में हेरफेर करता है।


20
"पायथन के पास कोई चर नहीं है" एक मूर्खतापूर्ण और भ्रमित करने वाला नारा है, और मैं वास्तव में चाहता हूं कि लोग यह कहना बंद कर दें ... :( इस उत्तर का बाकी हिस्सा अच्छा है!
नेड बैचेल्ड

9
यह चौंकाने वाला हो सकता है, लेकिन यह मूर्खतापूर्ण नहीं है। और मुझे नहीं लगता कि यह भ्रामक है या तो: यह उम्मीद है कि आने वाले स्पष्टीकरण के लिए प्राप्तकर्ता के दिमाग को खोल देता है और उसे एक उपयोगी "मुझे आश्चर्य है कि उनके पास चर के बजाय क्या है" रवैया डालता है। (हां, आपका माइलेज अलग-अलग हो सकता है।)
लूत्ज़ प्रीचेल

13
क्या आप यह भी कहेंगे कि जावास्क्रिप्ट का कोई चर नहीं है? वे पायथन के समान ही काम करते हैं। इसके अलावा, जावा, रूबी, पीएचपी, .... मुझे लगता है कि एक बेहतर शिक्षण तकनीक है, "पायथन के चर एक सी से अलग काम करते हैं।"
नेड बैचेल्ड

9
हां, जावा में चर हैं। तो पायथन, और जावास्क्रिप्ट, रूबी, पीएचपी, आदि आप जावा में नहीं कहेंगे जो intएक चर घोषित करता है, लेकिन Integerनहीं करता है। वे दोनों चर घोषित करते हैं। Integerचर, एक वस्तु है intचर एक आदिम है। एक उदाहरण के रूप में, आपने दिखाया कि आपके चर किस तरह से काम करते हैं a = 1; b = a; a++ # doesn't modify b। पाइथन में भी यह बिल्कुल सही है ( += 1क्योंकि ++पाइथन में कोई नहीं है)!
नेड बैचेल्ड

1
"चर" की अवधारणा जटिल और अक्सर अस्पष्ट है: एक चर एक मूल्य के लिए एक कंटेनर है, जिसे एक नाम से पहचाना जाता है। पायथन में, मान ऑब्जेक्ट हैं, कंटेनर ऑब्जेक्ट हैं (समस्या देखें?) और नाम वास्तव में अलग चीजें हैं। मेरा मानना ​​है कि इस तरीके से चर की सटीक समझ प्राप्त करना बहुत कठिन है। नाम-और-वस्तु स्पष्टीकरण अधिक कठिन प्रतीत होता है, लेकिन वास्तव में सरल है।
लुत्ज प्रेचल

43

एक सामान्य ट्रिक जो मैं आमतौर पर इस्तेमाल करता हूं, वह है इसे सिर्फ एक सूची में लपेटना:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(हाँ, मुझे पता है कि यह असुविधाजनक हो सकता है, लेकिन कभी-कभी ऐसा करना काफी सरल होता है।)


7
+1 पाठ की छोटी मात्रा के लिए पायथन की समस्या को आवश्यक वर्कअराउंड पास-पास-संदर्भ न होना। (इस पृष्ठ पर और साथ ही कहीं भी फिट होने वाली टिप्पणी / प्रश्न के रूप में: यह मेरे लिए स्पष्ट नहीं है कि क्यों अजगर सी # की तरह एक "रेफरी" कीवर्ड प्रदान नहीं कर सकता है, कि बस की तरह सूची में कॉलर के तर्क को लपेटता है यह, और सूची के 0 तत्व के रूप में फ़ंक्शन के भीतर तर्क के संदर्भ का इलाज करें।)
एम काट्ज़

5
अच्छा लगा। रेफरी से गुजरने के लिए, [] में लपेटें।
जस्टास

38

(संपादित करें - ब्लेयर ने अपने अत्यधिक लोकप्रिय उत्तर को अपडेट किया है ताकि यह अब सटीक हो)

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

डेविड कर्नप्यू के उत्तर वास्तविक उत्तर की ओर इशारा करते हैं और बताते हैं कि ब्लेयर कोनराड के पद का व्यवहार सही क्यों लगता है जबकि परिभाषाएँ नहीं हैं।

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

यदि आप व्यवहार चाहते हैं, तो ब्लेयर कॉनराड का जवाब ठीक है। लेकिन अगर आप जानना चाहते हैं कि पायथन न तो वैल्यू से पास है और न ही संदर्भ से गुजरता है, तो डेविड कोर्टन्यू का जवाब पढ़ें।


4
यह केवल सच नहीं है कि सभी भाषाएं मूल्य से पुकारती हैं। C ++ या पास्कल में (और निश्चित रूप से कई अन्य जिन्हें मैं नहीं जानता), आपके पास संदर्भ द्वारा कॉल है। उदाहरण के लिए, C ++ में, इसमें void swap(int& x, int& y) { int temp = x; x = y; y = temp; }दिए गए चर को स्वैप करेंगे। पास्कल में, आप के varबजाय का उपयोग करें &
cayhorstmann

2
मुझे लगा कि मैंने बहुत पहले ही इसका जवाब दे दिया था लेकिन मैं इसे नहीं देखता। पूर्णता के लिए - cayhorstmann ने मेरे उत्तर को गलत समझा। मैं यह नहीं कह रहा था कि सब कुछ उन शब्दों में मूल्य के आधार पर किया जाता है जो ज्यादातर लोग पहले C / C ++ के बारे में सीखते हैं । यह केवल इतना था कि कुछ मान पारित किया गया है (मूल्य, नाम, सूचक, आदि) और ब्लेयर के मूल उत्तर में प्रयुक्त शब्द गलत थे।
कोबे जॉन

24

आपको यहां कुछ बहुत अच्छे जवाब मिले।

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

2
हाँ, हालांकि अगर आप x = [2, 4, 4, 5, 5], y = x, X [0] = 1, प्रिंट x # [1, 4, 4, 5, 5] प्रिंट y # [1 , 4, 4, 5, 5]
छंटनी

19

इस मामले varमें विधि में शीर्षक वाले चर Changeको एक संदर्भ सौंपा गया है self.variable, और आप तुरंत एक स्ट्रिंग असाइन करते हैं var। यह अब ओर इशारा नहीं करता है self.variable। निम्न कोड स्निपेट दिखाता है कि यदि आप डेटा संरचना को संशोधित करते हैं तो क्या होगा varऔर self.variableइस मामले में एक सूची:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

मुझे यकीन है कि कोई और इसे स्पष्ट कर सकता है।


18

पायथन की पास-बाय-असाइनमेंट स्कीम C ++ के संदर्भ पैरामीटर विकल्प के समान नहीं है, लेकिन यह व्यवहार में C भाषा (और अन्य) के तर्क-पासिंग मॉडल के समान है।

  • अपरिवर्तनीय दलीलें प्रभावी रूप से " मूल्य से " पारित की जाती हैं । पूर्णांक और स्ट्रिंग्स जैसे ऑब्जेक्ट्स को कॉपी करने के बजाय ऑब्जेक्ट संदर्भ द्वारा पास किया जाता है, लेकिन क्योंकि आप किसी भी तरह अपरिवर्तनीय वस्तुओं को बदल नहीं सकते हैं, प्रभाव बहुत कुछ कॉपी बनाने जैसा है।
  • प्रभावी तर्क को " सूचक द्वारा " प्रभावी रूप से पारित किया जाता है । ऑब्जेक्ट और संदर्भ जैसी सूचियाँ भी ऑब्जेक्ट रेफ़रेंस द्वारा पास की जाती हैं, जो कि सी पास सरणियों के समान है, जैसे कि पॉइंटर्स-म्यूटेबल ऑब्जेक्ट्स को फ़ंक्शन में जगह में बदला जा सकता है, सी एरेज़ की तरह।

16

जैसा कि आप बता सकते हैं कि आपके पास एक परिवर्तनशील वस्तु होने की आवश्यकता है, लेकिन मैं आपको वैश्विक चर पर जांच करने का सुझाव देता हूं क्योंकि वे आपकी मदद कर सकते हैं या इस तरह के मुद्दे को हल कर सकते हैं!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

उदाहरण:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

5
मुझे एक समान प्रतिक्रिया पोस्ट करने के लिए लुभाया गया था- मूल प्रश्नकर्ता को यह नहीं पता होगा कि वह जो चाहता था वह वास्तव में एक वैश्विक चर का उपयोग करने के लिए था, जिसे कार्यों के बीच साझा किया गया था। यहां वह लिंक है जो मैंने साझा किया होगा: stackoverflow.com/questions/423379/… @Tim के जवाब में, स्टैक ओवरफ्लो केवल एक प्रश्न और उत्तर साइट नहीं है, यह संदर्भ ज्ञान का एक विशाल भंडार है जो केवल मजबूत और अधिक बारीक होता है- बहुत कुछ एक सक्रिय विकी की तरह- अधिक इनपुट के साथ।
मैक्स पी मैगी

13

उत्तर में बहुत सारी अंतर्दृष्टि, लेकिन मुझे लगता है कि एक अतिरिक्त बिंदु स्पष्ट रूप से यहां स्पष्ट रूप से उल्लेख नहीं किया गया है। अजगर के दस्तावेज से उद्धरण https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

"पायथन में, चर जो केवल किसी फ़ंक्शन के अंदर संदर्भित होते हैं, वे अंतर्निहित रूप से वैश्विक होते हैं। यदि फ़ंक्शन के शरीर के भीतर कहीं भी एक चर को एक नया मान सौंपा जाता है, तो इसे स्थानीय माना जाता है। यदि किसी चर को फ़ंक्शन के अंदर एक नया मान सौंपा जाता है। चर स्पष्ट रूप से स्थानीय है, और आपको इसे स्पष्ट रूप से 'वैश्विक' घोषित करने की आवश्यकता है। हालांकि पहले थोड़ा सा आश्चर्य की बात है, एक पल का विचार यह बताता है। एक तरफ, असाइन किए गए चर के लिए वैश्विक की आवश्यकता होती है, अनपेक्षित दुष्प्रभावों के खिलाफ एक बार प्रदान करता है। दूसरी ओर, यदि वैश्विक सभी वैश्विक संदर्भों के लिए आवश्यक था, तो आप हर समय वैश्विक उपयोग कर रहे होंगे। आपको अंतर्निहित फ़ंक्शन या आयातित मॉड्यूल के घटक के लिए प्रत्येक संदर्भ को वैश्विक घोषित करना होगा। दुष्प्रभावों की पहचान करने के लिए वैश्विक घोषणा की उपयोगिता को पराजित करेगा। "

किसी फ़ंक्शन के लिए एक परिवर्तनशील वस्तु पास करते समय भी यह अभी भी लागू होता है। और मुझे स्पष्ट रूप से ऑब्जेक्ट को असाइन करने और फ़ंक्शन में ऑब्जेक्ट पर काम करने के बीच व्यवहार में अंतर का कारण बताता है।

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

देता है:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

वैश्विक चर के लिए असाइनमेंट जिसे वैश्विक घोषित नहीं किया गया है, इसलिए एक नई स्थानीय वस्तु बनाता है और मूल वस्तु के लिंक को तोड़ता है।


10

यहाँ pass by objectपायथन में प्रयुक्त अवधारणा का सरल (मुझे आशा है) स्पष्टीकरण है ।
जब भी आप फ़ंक्शन को ऑब्जेक्ट पास करते हैं, तो ऑब्जेक्ट खुद ही पास हो जाता है (पायथन में ऑब्जेक्ट वास्तव में वह है जिसे आप अन्य प्रोग्रामिंग भाषाओं में मान कहेंगे) इस ऑब्जेक्ट का संदर्भ नहीं। दूसरे शब्दों में, जब आप कॉल करते हैं:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

वास्तविक वस्तु - [0, 1] (जिसे अन्य प्रोग्रामिंग भाषाओं में मूल्य कहा जाएगा) पारित किया जा रहा है। तो वास्तव में फ़ंक्शन change_meकुछ ऐसा करने की कोशिश करेगा:

[0, 1] = [1, 2, 3]

जो स्पष्ट रूप से फ़ंक्शन में दी गई वस्तु को नहीं बदलेगा। यदि फ़ंक्शन इस तरह दिखता है:

def change_me(list):
   list.append(2)

तब कॉल में परिणाम होगा:

[0, 1].append(2)

जो स्पष्ट रूप से वस्तु को बदल देगा। यह उत्तर इसे अच्छी तरह से समझाता है।


2
समस्या यह है कि असाइनमेंट आपकी अपेक्षा से कुछ अधिक है। list = [1, 2, 3]कारणों में पुन: उपयोग listकुछ और के लिए नाम और मूल रूप से पारित कर दिया वस्तु forgeting। हालाँकि, आप कोशिश कर सकते हैं list[:] = [1, 2, 3](वैसे listतो एक चर के लिए गलत नाम है। सोच के बारे [0, 1] = [1, 2, 3]में एक पूरी बकवास है। वैसे भी, आपको क्या लगता है कि इसका मतलब है कि वस्तु खुद ही पारित हो गई है? आपकी राय में इस फ़ंक्शन को क्या कॉपी किया गया है?
pepr

@pepr ऑब्जेक्ट्स शाब्दिक नहीं हैं। वे वस्तु हैं। उनके बारे में बात करने का एकमात्र तरीका उन्हें कुछ नाम दे रहा है। इसीलिए जब आप इसे समझ लेते हैं तो यह बहुत सरल होता है, लेकिन समझाने के लिए बहुत जटिल होता है। :-)
वेकी

@Veky: मैं इसके बारे में पता कर रहा हूँ। वैसे भी, सूची शाब्दिक को सूची वस्तु में बदल दिया जाता है। दरअसल, पायथन में कोई भी वस्तु बिना नाम के मौजूद हो सकती है, और इसका उपयोग तब भी किया जा सकता है जब उसे कोई नाम नहीं दिया जाता है। और आप उनके बारे में गुमनाम वस्तुओं के बारे में सोच सकते हैं। किसी सूची के तत्व होने के बारे में सोचें। उन्हें नाम की जरूरत नहीं है। आप उन्हें सूची के माध्यम से अनुक्रमण या पुनरावृत्ति के माध्यम से एक्सेस कर सकते हैं। वैसे भी, मैं [0, 1] = [1, 2, 3]केवल एक बुरा उदाहरण है। पायथन में ऐसा कुछ नहीं है।
पेजर

@pepr: मुझे जरूरी नहीं है कि पाइथन-डेफिनिशन नाम, सिर्फ साधारण नाम। बेशक alist[2]एक तीसरे तत्व के नाम के रूप में गिना जाता है। लेकिन मुझे लगता है कि मैंने गलत समझा कि आपकी समस्या क्या थी। :-)
विक्की

अरे। मेरी अंग्रेजी जाहिर तौर पर मेरे पायथन से बहुत ज्यादा खराब है। :-) मैं सिर्फ एक बार और कोशिश करूँगा। मैंने सिर्फ इतना कहा कि आपको उनके बारे में बात करने के लिए कुछ नाम देना होगा। उस "नाम" से मेरा मतलब "पायथन द्वारा परिभाषित" नामों से नहीं था। मैं पायथन तंत्र जानता हूं, चिंता मत करो।
वेकी

8

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

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

उदाहरण के तरीकों में, आप सामान्य रूप से selfइंस्टेंस विशेषताओं तक पहुँचने का उल्लेख करते हैं। __init__उदाहरण के तरीकों को सेट करना और उन्हें इंस्टेंस विधियों में पढ़ना या बदलना सामान्य है । यही कारण है कि आप selfपहले तर्क को पास करते हैं def Change

एक अन्य समाधान इस तरह से एक स्थिर विधि बनाना होगा:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

6

किसी ऑब्जेक्ट को संदर्भ द्वारा पास करने के लिए एक छोटी सी चाल है, भले ही भाषा इसे संभव नहीं बनाती है। यह जावा में भी काम करता है, यह एक आइटम के साथ सूची है। ;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

यह एक बदसूरत हैक है, लेकिन यह काम करता है। ;-P


pएक उत्परिवर्ती सूची वस्तु का संदर्भ है जो बदले में वस्तु को संग्रहीत करता है obj। संदर्भ 'पी', में पारित हो जाता है changeRef। अंदर changeRef, एक नया संदर्भ बनाया जाता है (नया संदर्भ कहा जाता है ref) जो उसी सूची ऑब्जेक्ट को pइंगित करता है जो इंगित करता है। लेकिन क्योंकि सूची परस्पर हैं, सूची में परिवर्तन दोनों संदर्भों से दिखाई देते हैं । इस स्थिति में, आपने refऑब्जेक्ट को इंडेक्स 0 में बदलने के लिए संदर्भ का उपयोग किया ताकि यह बाद में PassByReference('Michael')ऑब्जेक्ट को स्टोर करे । सूची ऑब्जेक्ट में refपरिवर्तन का उपयोग किया गया था, लेकिन यह परिवर्तन दिखाई दे रहा है p
मिन्ह

तो अब, संदर्भ pऔर refसूची ऑब्जेक्ट को इंगित करता है जो एकल ऑब्जेक्ट को संग्रहीत करता है PassByReference('Michael'),। तो यह इस प्रकार है कि p[0].nameरिटर्न Michael। बेशक, refअब दायरे से बाहर हो गया है और कचरा इकट्ठा हो सकता है लेकिन सभी समान।
मिन्ह ट्रान

आपने संदर्भ से जुड़ी मूल वस्तु के , निजी उदाहरण चर को नहीं बदला है । वास्तव में, लौटेगा । उपर्युक्त टिप्पणियाँ दी गई परिभाषा को मानती हैं । namePassByReferenceobjobj.namePeterMark Ransom
मिन्ह ट्रान

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

5

मैंने जल्दी से पायथन को फोरट्रान कोड के एक जोड़े को परिवर्तित करने के लिए निम्नलिखित विधि का उपयोग किया। सच है, यह संदर्भ से नहीं गुजरता है क्योंकि मूल प्रश्न को प्रस्तुत किया गया था, लेकिन कुछ मामलों में एक सरल काम है।

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

हां, यह मेरे उपयोग के मामले में भी 'पास बाय रेफरेंस' को हल करता है। मेरे पास एक फ़ंक्शन है जो मूल रूप से मानों को साफ करता है dictऔर फिर रिटर्न देता है dict। हालाँकि, सफाई करते समय यह स्पष्ट हो सकता है कि सिस्टम के एक हिस्से के पुनर्निर्माण की आवश्यकता है। इसलिए, फ़ंक्शन को न केवल साफ लौटना चाहिए, dictबल्कि पुनर्निर्माण को संकेत देने में भी सक्षम होना चाहिए। मैं एक boolसंदर्भ द्वारा पारित करने की कोशिश की , लेकिन inc कि काम नहीं करता है। इसे हल करने के तरीके का पता लगाते हुए, मैंने पाया कि आपका समाधान (मूल रूप से एक टुपल लौटा रहा है) सबसे अच्छा काम करने के लिए, जबकि वह भी एक हैक / वर्कअराउंड नहीं हो रहा है।
कासिमिर

3

हालांकि संदर्भ द्वारा पास कुछ भी नहीं है जो अजगर में अच्छी तरह से फिट बैठता है और इसका उपयोग शायद ही कभी किया जाना चाहिए कुछ वर्कअराउड्स हैं जो वास्तव में वर्तमान में एक स्थानीय चर को सौंपी गई वस्तु को प्राप्त करने के लिए काम कर सकते हैं या किसी फ़ंक्शन के अंदर से एक स्थानीय चर को पुन: असाइन कर सकते हैं।

मूल विचार एक फ़ंक्शन है जो उस पहुंच को कर सकता है और अन्य कार्यों में ऑब्जेक्ट के रूप में पारित किया जा सकता है या एक कक्षा में संग्रहीत किया जा सकता है।

एक तरीका है global(ग्लोबल वेरिएबल्स के लिए) या nonlocalरैपर फ़ंक्शन में (किसी फ़ंक्शन में स्थानीय चर के लिए)।

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

एक ही विचार delएक चर को पढ़ने और खाने के लिए काम करता है ।

सिर्फ पढ़ने के लिए यहां तक ​​कि उपयोग करने का एक छोटा तरीका है lambda: xजो एक कॉल करने योग्य रिटर्न देता है जिसे x का वर्तमान मान लौटाया जाता है। यह कुछ हद तक सुदूर अतीत की भाषाओं में प्रयुक्त "कॉल बाय नेम" जैसा है।

एक वैरिएबल का उपयोग करने के लिए 3 रैपर को पास करना थोड़ा कठिन है, ताकि उन्हें एक ऐसे वर्ग में लपेटा जा सके जिसमें प्रॉक्सी विशेषता हो:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

पायथन "प्रतिबिंब" समर्थन एक ऐसी वस्तु को प्राप्त करना संभव बनाता है जो उस दायरे में स्पष्ट रूप से कार्यों को परिभाषित किए बिना किसी दिए गए दायरे में एक नाम / चर को पुन: सौंपने में सक्षम है:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

यहाँ ByRefकक्षा एक डिक्शनरी का उपयोग करती है। तो wrappedपास किए गए शब्दकोश में आइटम एक्सेस के लिए विशेषता एक्सेस का अनुवाद किया जाता है। बिलिन के परिणाम localsऔर स्थानीय चर के नाम को पारित करने से यह स्थानीय चर तक पहुंचता है। 3.5 के रूप में अजगर प्रलेखन सलाह देता है कि शब्दकोश को बदलना काम नहीं कर सकता है लेकिन यह मेरे लिए काम करता है।


3

जिस तरह से अजगर मूल्यों और उनके संदर्भों को संभालता है, आप जिस तरह से एक मनमाने ढंग से उदाहरण को संदर्भित कर सकते हैं वह विशेषता नाम से है:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

वास्तविक कोड में, आप निश्चित रूप से, हुकुम लुकअप पर त्रुटि जाँच जोड़ेंगे।


3

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

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

3

पायथन में पास-दर-संदर्भ C ++ / Java में संदर्भ द्वारा पास की अवधारणा से काफी अलग है।

  • जावा और सी #: आदिम प्रकार (स्ट्रिंग शामिल करें) मूल्य (प्रतिलिपि) से गुजरती हैं, संदर्भ प्रकार संदर्भ (एड्रेस कॉपी) द्वारा पारित किया जाता है, इसलिए कॉल किए गए फ़ंक्शन में पैरामीटर में किए गए सभी परिवर्तन कॉलर को दिखाई देते हैं।
  • C ++: पास-दर-संदर्भ या पास-बाय-वैल्यू दोनों की अनुमति है। यदि कोई पैरामीटर संदर्भ द्वारा पारित किया जाता है, तो आप या तो इसे संशोधित कर सकते हैं या नहीं कि पैरामीटर को कास्ट के रूप में पारित किया गया था या नहीं। हालाँकि, कांस्ट या नहीं, पैरामीटर ऑब्जेक्ट के संदर्भ को बनाए रखता है और संदर्भ को फ़ंक्शन के भीतर किसी भिन्न ऑब्जेक्ट को इंगित करने के लिए असाइन नहीं किया जा सकता है।
  • पायथन: पायथन "पास-बाय-ऑब्जेक्ट-रेफरेंस" है, जिसके बारे में अक्सर कहा जाता है: "ऑब्जेक्ट रेफरेंस वैल्यू द्वारा पास किया जाता है।" [यहां पढ़ें] 1। कॉलर और फ़ंक्शन दोनों एक ही ऑब्जेक्ट को संदर्भित करते हैं, लेकिन फ़ंक्शन में पैरामीटर एक नया चर है जो कॉलर में ऑब्जेक्ट की एक प्रति पकड़े हुए है। सी ++ की तरह, एक पैरामीटर को संशोधित किया जा सकता है या फ़ंक्शन में नहीं हो सकता है - यह पारित किए गए ऑब्जेक्ट के प्रकार पर निर्भर करता है। जैसे; एक अपरिवर्तनीय ऑब्जेक्ट प्रकार को तथाकथित फ़ंक्शन में संशोधित नहीं किया जा सकता है, जबकि एक परिवर्तनशील वस्तु को या तो अपडेट किया जा सकता है या फिर से आरंभ किया जा सकता है। म्यूटेबल वैरिएबल को अपडेट या री-असाइनिंग / री-इनिशियलाइज़ करने के बीच एक महत्वपूर्ण अंतर यह है कि अपडेटेड वैल्यू को वापस फंक्शन में परिलक्षित किया जाता है, जबकि रीइन्टिग्रेटेड वैल्यू नहीं करता है। एक परिवर्तनशील चर के लिए नई वस्तु के किसी भी कार्य का दायरा अजगर में कार्य करने के लिए स्थानीय है। @ ब्लेयर-कॉनराड द्वारा प्रदान किए गए उदाहरण इसे समझने में बहुत अच्छे हैं।

1
पुराना है, लेकिन मैं इसे सही करने के लिए बाध्य महसूस करता हूं। स्ट्रिंग्स को जावा और C # दोनों के संदर्भ में पास किया जाता है, मान से नहीं
John

2

चूँकि आपका उदाहरण ऑब्जेक्ट-ओरिएंटेड होता है, आप समान परिणाम प्राप्त करने के लिए निम्नलिखित बदलाव कर सकते हैं:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

1
हालांकि यह काम करता है। यह संदर्भ से पारित नहीं है। यह 'ऑब्जेक्ट रेफरेंस पास' है।
बिस्वास मिश्रा

1

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

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

1

चूँकि ऐसा प्रतीत होता है कि संदर्भों का अनुकरण करने के लिए एक दृष्टिकोण का उल्लेख किया गया है, उदाहरण के लिए, जैसे कि C ++ को "अपडेट" फ़ंक्शन का उपयोग करना है और पास करना है जो वास्तविक चर के बजाय (या बल्कि "नाम") है:

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

यह ज्यादातर "केवल-बाहर संदर्भ" के लिए या कई थ्रेड्स / प्रक्रियाओं के साथ स्थिति में (अपडेट फ़ंक्शन थ्रेड / मल्टीप्रोसेसिंग को सुरक्षित बनाकर) उपयोगी है।

जाहिर है कि उपरोक्त मूल्य को पढ़ने की अनुमति नहीं है , केवल इसे अद्यतन करना।

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