2010-02-25 11 views
19

जब आप एक सरणी पैरामीटर के साथ पायथन में कोई फ़ंक्शन परिभाषित करते हैं, तो उस पैरामीटर का दायरा क्या है?पायथन में डिफ़ॉल्ट पैरामीटर का दायरा क्या है?

यह उदाहरण पायथन ट्यूटोरियल से लिया जाता है:

def f(a, L=[]): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 

प्रिंटों:

[1] 
[1, 2] 
[1, 2, 3] 

मैं काफी यकीन है कि अगर मैं समझता हूँ कि यहाँ क्या हो रहा है नहीं कर रहा हूँ। क्या इसका मतलब यह है कि सरणी का दायरा समारोह के बाहर है? सरणी कॉल से कॉल करने के लिए अपने मूल्य क्यों याद करती है? अन्य भाषाओं से आ रहा है, मैं केवल इस व्यवहार की अपेक्षा करता हूं अगर चर स्थिर था। अन्यथा ऐसा लगता है कि इसे हर बार रीसेट किया जाना चाहिए। और वास्तव में, जब मैंने निम्नलिखित कोशिश की:

def f(a): 
    L = [] 
    L.append(a) 
    return L 

मुझे वह व्यवहार मिला जो मुझे अपेक्षित था (सरणी प्रत्येक कॉल पर रीसेट की गई थी)।

तो मुझे ऐसा लगता है कि मुझे बस def f(a, L=[]): लाइन की आवश्यकता है - L चर का दायरा क्या है?

+9

यहां देखें: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – sth

+1

@sth - धन्यवाद, चर्चा बहुत बढ़िया है। यह लाइन मुझे बहुत वर्णनात्मक और सहायक के रूप में मारती है - "[पायथन] फ़ंक्शन परिभाषा पर डिफ़ॉल्ट तर्क को बांधती है, न कि फ़ंक्शन निष्पादन पर।" – charlie

उत्तर

17

दायरा जैसा आप उम्मीद करेंगे।

शायद आश्चर्य की बात यह है कि डिफ़ॉल्ट मान केवल एक बार गणना की जाती है और फिर से उपयोग की जाती है, इसलिए हर बार जब आप फ़ंक्शन को कॉल करते हैं तो आपको एक ही सूची मिलती है, न कि एक नई सूची []।

सूची f.func_defaults में संग्रहीत है।

def f(a, L=[]): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 
print f.func_defaults 
f.func_defaults = (['foo'],) # Don't do this! 
print f(4) 

परिणाम:

[1] 
[1, 2] 
[1, 2, 3] 
([1, 2, 3],) 
['foo', 4] 
+0

लेकिन मुझे वास्तव में यह समझ में नहीं आता है। उस सूची को कहां रखा जाता है? – charlie

+0

@charlie: यह एक अच्छा सवाल है। मैंने अपना जवाब अपडेट कर लिया है। –

+0

धन्यवाद :)। इसलिए किसी फ़ंक्शन के भीतर घोषित चर केवल फ़ंक्शन के दायरे में पहुंच योग्य होते हैं, लेकिन फ़ंक्शन हस्ताक्षर/हेडर/पैरामीटर सूची में घोषित चर सार्वजनिक क्षेत्र में डाल दिए जाते हैं और कहीं भी उपलब्ध होते हैं? – charlie

4

L चर का दायरा आपकी अपेक्षा के अनुरूप बर्ताव कर रही है।

"समस्या" उस सूची के साथ है जिसे आप [] के साथ बना रहे हैं। प्रत्येक बार जब आप फ़ंक्शन को कॉल करते हैं तो पाइथन एक नई सूची नहीं बनाता है। L प्रत्येक बार कॉल करते समय एक ही सूची को असाइन किया जाता है, यही कारण है कि फ़ंक्शन पिछली कॉल "याद करता है"।

mylist = [] 
def f(a, L=mylist): 
    L.append(a) 
    return L 

Python Tutorial puts it this way:

डिफ़ॉल्ट मान केवल एक बार मूल्यांकन किया जाता है

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

और अपेक्षित व्यवहार कोड करने के लिए निम्नलिखित तरीके से पता चलता है:

def f(a, L=None): 
    if L is None: 
     L = [] 
    L.append(a) 
    return L 
+0

आपके द्वारा दिया गया पहला कोड उदाहरण भ्रमित है क्योंकि यह सुझाव देता है कि डिफ़ॉल्ट पैरामीटर "एल" का वैश्विक दायरा है जब वास्तव में इसमें केवल दायरा है समारोह परिभाषित किया जा रहा है। – manifest

+0

@manifest - मुझे यकीन नहीं है कि आप ऐसा क्यों कहते हैं। मेरे लिए उदाहरण स्पष्ट रूप से कह रहा है कि प्रत्येक बार जब फ़ंक्शन को 'एल' कहा जाता है तो उसे एक मान असाइन किया जाता है जिसे प्रत्येक बार फ़ंक्शन कहा जाता है। –

3

वहाँ भी कम "जादू" से आपको संदेह हो सकता है। यह

m = [] 

def f(a, L=m): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 

m केवल एक बार बनाया गया है।

+0

बेशक, कि 'एम' का दायरा मूल में 'एल' जैसा नहीं है। –

+0

यह थोड़ा और समझ में आता है, क्योंकि सूची फ़ंक्शन के बाहर * मौजूद है। इस प्रकार यह समझ में आता है कि यह बनी हुई है। लेकिन अगर यह फ़ंक्शन के भीतर परिभाषित किया गया है, तो जैसे ही आप बाहर निकलेंगे, इसे खो जाना चाहिए। क्या इसका मतलब यह है कि पाइथन लाइन "def f (a, l = [])" को 2 पंक्तियों "एल = []" और फिर "def f (a, L)" के रूप में परिभाषित करता है? – charlie

+0

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

0

"समस्या" यहां है कि L=[] केवल का मूल्यांकन है, यानी, जब फ़ाइल संकलित की जाती है। पाइथन फ़ाइल की प्रत्येक पंक्ति के माध्यम से कदम उठाता है और इसे संकलित करता है। जब तक यह डिफ़ॉल्ट पैरामीटर के साथ def तक पहुंचता है, तो यह उस सूची उदाहरण को एक बार बनाता है।

यदि आप फ़ंक्शन कोड के अंदर L = [] डालते हैं, तो उदाहरण "संकलन समय" पर नहीं बनाया गया है (वास्तव में संकलन समय को रन टाइम का भी भाग कहा जा सकता है) क्योंकि पायथन फ़ंक्शन के कोड को संकलित करता है लेकिन इसे कॉल नहीं करता है। तो आपको नया सूची उदाहरण मिलेगा क्योंकि हर बार जब आप फ़ंक्शन को कॉल करते हैं (संकलन के दौरान एक बार) सृजन किया जाता है।

है कि समस्या के लिए एक समाधान None तरह डिफ़ॉल्ट पैरामीटर या केवल तय उदाहरणों के रूप में परिवर्तनशील वस्तुओं का उपयोग नहीं है:

def f(a, L = None): 
    if l == None: 
     l = [] 
    L.append(a) 
    return L 

ध्यान दें कि दोनों ही मामलों आप वर्णित में, L के दायरे समारोह गुंजाइश है।

0

व्याख्या this question के उत्तर में दी गई है। इसे यहां समेटने के लिए:

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

लेनार्ट रीगेब्रो में a good explanation और answer to the question रॉबर्टो लिफ्रेडो द्वारा उत्कृष्ट है।

Lennart का जवाब अनुकूलन करने के लिए ... अगर मैं एक BananaBunch वर्ग है:

class BananaBunch: 
    bananas = [] 

    def addBanana(self, banana): 
     self.bananas.append(banana) 


bunch = BananaBunch() 
>>> bunch 
<__main__.BananaBunch instance at 0x011A7FA8> 
>>> bunch.addBanana(1) 
>>> bunch.bananas 
[1] 
>>> for i in range(6): 
    bunch.addBanana("Banana #" + i) 
>>> for i in range(6): 
    bunch.addBanana("Banana #" + str(i)) 

>>> bunch.bananas 
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5'] 

// And for review ... 
//If I then add something to the BananaBunch class ... 
>>> BananaBunch.bananas.append("A mutated banana") 

//My own bunch is suddenly corrupted. :-) 
>>> bunch.bananas 
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5', 'A mutated banana'] 

यह कैसे काम करता है के लिए लागू होता है? Functions in Python are objects। यह दोहराना भालू। Functions in Python are object एस।

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

+0

(एसओ लिंक के लिए धन्यवाद, यह बहुत अच्छा है!) लेकिन फिर फ़ंक्शन के भीतर घोषित चर भी गुण/सदस्य-स्तर चर के समान होना चाहिए, और कॉल टू कॉल से भी जारी रहना चाहिए। नहीं...? – charlie

+0

@charlie, काफी नहीं। 'फ़ंक्शन' ऑब्जेक्ट में डिफ़ॉल्ट तर्कों के लिए एक विशेषता है, लेकिन इसके भीतर घोषित अन्य चर के लिए एक विशेषता नहीं है। अधिक विस्तृत स्पष्टीकरण के लिए @ मार्क बियर का उत्तर देखें। –

+0

इसके अलावा, मतदाता नीचे, अगर मैं प्रतिक्रिया नहीं करता हूं कि मैं क्यों गलत हूं, तो मैं इस पोस्ट में सुधार नहीं कर सकता। ;-) –

-1

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

def f(a, L=[]): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 

इस मुद्दे को ठीक करने के लिए यहां अन्य उत्तरों में मुहावरे के लिए सुझाव दिए गए हैं। एक मेरा सुझाव है इस प्रकार है:

def f(a, L=None): 
    L = L or [] 
    L.append(a) 
    return L 

यह या तो "L" पारित किया गया था कि ले, या एक नई सूची बनाने के लिए या शॉर्ट सर्किट का उपयोग करता है।

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

def func(a=[]): 
    a.append(1) 
    print("A:", a) 

func() 
func() 
func() 

आप क्या तुम समझते क्या हो रहा है मदद करने के लिए अजगर के खरोज का उपयोग कर सकते हैं:

+0

ध्यान दें कि आपको केवल गैर-अक्षर के लिए सुझाए गए मुहावरे का उपयोग करना है या अन्य शब्दों में सूचियों/डिक्ट्स/कक्षाओं – manifest

+0

'या' के शॉर्ट-सर्किटिंग व्यवहार का दुरुपयोग करना इस तरह के कथन का स्पष्ट रूप से उपयोग करने से कम पठनीय है ('यदि एल कोई नहीं है:')। उदाहरण के लिए, यदि मैं अपनी खुद की सूची-जैसी प्रकार के खाली उदाहरण में पास हुआ, तो मुझे उम्मीद है कि 'एफ' इसका उपयोग करेगा, लेकिन ऐसा नहीं होगा, हालांकि यह एक खाली रिक्त उदाहरण के साथ होगा। –

+0

आह, सूचियों के मामले में आप सही हैं। मुझे लगता है कि मुझे इस के साथ कभी काटा नहीं गया है क्योंकि जब मैं पैरामीटर के रूप में एक सूची पास करता हूं तो मुझे फ़ंक्शन/विधि कॉल (सी में पॉइंटर की तरह) के बाद इसका उपयोग करने की उम्मीद नहीं है। पठनीयता के लिए, मुझे यह पूरी तरह से पठनीय लगता है, जबकि यदि आपने 5 डिफ़ॉल्ट पैरामीटर कहा था तो आपको 5 की आवश्यकता होगी यदि आपके फ़ंक्शन के शीर्ष भाग में से अधिकतर बयानों को भरने वाले बयान जो मुझे बदसूरत लगते हैं। यह निश्चित रूप से सभी व्यक्तिपरक है। लेकिन जानकारीपूर्ण टिप्पणी के लिए धन्यवाद। – manifest

1

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

डिफ़ॉल्ट तर्कों के साथ यह क्या करता है एक दिलचस्प मुद्दा है। पायथन 3 में, यह दो स्थानों में एक समारोह के बारे में अधिकतर जानकारी रखता है: func.__code__ और func.__defaults__। पायथन 2, func.__code__func.func_codefunc.__defaults__func.func_defaults था। पायथन 2 के बाद के संस्करणों में, 2.6 समेत दोनों नामों के सेट हैं, पाइथन 2 से पायथन 3 में संक्रमण की सहायता के लिए। मैं अधिक आधुनिक __code__ और __defaults__ का उपयोग करूंगा। यदि आप पुराने अजगर पर फंस गए हैं, तो अवधारणाएं समान हैं; बस नाम अलग हैं।

डिफ़ॉल्ट मान func.__defaults__ में संग्रहीत हैं, और प्रत्येक बार फ़ंक्शन को कॉल किया जाता है।

इस प्रकार जब आप समारोह ऊपर परिभाषित करते हैं, समारोह के शरीर संकलित हो जाता है और __code__ तहत चर में संग्रहीत, बाद में निष्पादित किया जाना है, और डिफ़ॉल्ट तर्क __defaults__ में संग्रहीत। जब आप फ़ंक्शन को कॉल करते हैं, तो यह __defaults__ में मानों का उपयोग करता है। यदि वे मान किसी भी कारण से संशोधित हो जाते हैं, तो इसका उपयोग करने के लिए केवल संशोधित संस्करण उपलब्ध है।

इंटरैक्टिव दुभाषिया में विभिन्न कार्यों को परिभाषित करने के आसपास खेलें, और देखें कि पाइथन कैसे काम करता है और कार्यों का उपयोग करता है, इसके बारे में आप क्या समझ सकते हैं।

संबंधित मुद्दे