"कम से कम विस्मय" और म्यूटेबल डिफ़ॉल्ट तर्क

Jul 16 2009

पायथन के साथ लंबे समय से छेड़छाड़ करने वाले किसी भी व्यक्ति को निम्नलिखित मुद्दे से काट दिया गया है (या टुकड़े टुकड़े करना):

def foo(a=[]):
    a.append(5)
    return a

पायथन नौसिखियों को इस फ़ंक्शन से हमेशा केवल एक तत्व के साथ एक सूची वापस करने की उम्मीद होगी [5]:। परिणाम इसके बजाय बहुत अलग है, और बहुत आश्चर्यजनक है (एक नौसिखिए के लिए):

>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()

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

संपादित करें :

बेज़ेक ने एक दिलचस्प उदाहरण बनाया। आपकी अधिकांश टिप्पणियों और साथ में विशेष रूप से उतल की, मैंने आगे विस्तार से बताया:

>>> def a():
...     print("a executed")
...     return []
... 
>>>            
>>> def b(x=a()):
...     x.append(5)
...     print(x)
... 
a executed
>>> b()
[5]
>>> b()
[5, 5]

मेरे लिए, ऐसा लगता है कि डिजाइन का निर्णय मानकों के दायरे के सापेक्ष था: फ़ंक्शन के अंदर या इसके साथ "एक साथ"?

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

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

जवाब

1658 rob Jul 18 2009 at 04:29

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

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

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

278 EliCourtwright Jul 16 2009 at 01:11

मान लें कि आपके पास निम्नलिखित कोड है

fruits = ("apples", "bananas", "loganberries")

def eat(food=fruits):
    ...

जब मैं खाने की घोषणा देखता हूं, तो सबसे कम हैरान करने वाली बात यह है कि यदि पहला पैरामीटर नहीं दिया गया है, तो यह टपल के बराबर होगा ("apples", "bananas", "loganberries")

हालांकि, बाद में कोड में माना जाता है, मैं कुछ ऐसा करता हूं

def some_random_function():
    global fruits
    fruits = ("blueberries", "mangos")

तब यदि डिफॉल्ट डिक्लेरेशन के बजाय डिफॉल्ट पैरामीटर फंक्शन एक्जीक्यूशन में बंधे होते हैं तो मुझे आश्चर्य होगा (बहुत बुरे तरीके से) कि फलों को बदला गया था। यह पता लगाने की तुलना में अधिक आश्चर्यजनक IMO होगा कि fooऊपर आपका फ़ंक्शन सूची को म्यूट कर रहा था।

असली समस्या उत्परिवर्तनीय चर के साथ है, और सभी भाषाओं में कुछ हद तक यह समस्या है। यहाँ एक सवाल है: मान लीजिए जावा में मेरे पास निम्नलिखित कोड हैं:

StringBuffer s = new StringBuffer("Hello World!");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
System.out.println( counts.get(s) );  // does this work?

अब, क्या मेरा नक्शा StringBufferकुंजी के मूल्य का उपयोग करता है जब इसे मानचित्र में रखा गया था, या यह संदर्भ द्वारा कुंजी को संग्रहीत करता है? किसी भी तरह, कोई चकित है; या तो वह व्यक्ति जिसने किसी वस्तु का Mapउपयोग करने की कोशिश की है, उसी के समान, जिसे वे इसमें डालते हैं, या वह व्यक्ति जो अपनी वस्तु को प्राप्त नहीं कर सकता है, भले ही वे जिस कुंजी का उपयोग कर रहे हों वह वस्तुतः वही वस्तु हो। इसका उपयोग इसे मानचित्र में करने के लिए किया गया था (यह वास्तव में यही कारण है कि पायथन अपने परस्पर निर्मित डेटा प्रकारों को शब्दकोश कुंजियों के रूप में उपयोग करने की अनुमति नहीं देता है)।

आपका उदाहरण एक अच्छा मामला है जहां पायथन नए लोगों को आश्चर्यचकित और काट देगा। लेकिन मेरा तर्क है कि अगर हम इसे "ठीक" कर देते हैं, तो यह केवल एक अलग स्थिति पैदा करेगा जहां उन्हें इसके बजाय काट दिया जाएगा, और यह भी कम सहज होगा। इसके अलावा, यह हमेशा परिवर्तनशील चर के साथ काम करते समय होता है; आप हमेशा ऐसे मामलों में भाग लेते हैं, जहां कोई व्यक्ति सहजता से एक या विपरीत व्यवहार की उम्मीद कर सकता है कि वे किस कोड पर लिख रहे हैं।

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

244 glglgl Jul 10 2012 at 21:50

प्रलेखन का प्रासंगिक हिस्सा :

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

def whats_on_the_telly(penguin=None):
    if penguin is None:
        penguin = []
    penguin.append("property of the zoo")
    return penguin
121 Utaal Jul 16 2009 at 06:21

मुझे पता है कि पायथन दुभाषिया आंतरिक कामकाज के बारे में कुछ भी नहीं है (और मैं कंपाइलर और दुभाषियों में विशेषज्ञ नहीं हूं) तो मुझे दोष न दें अगर मैं कुछ भी असंभावना या असंभव का प्रस्ताव करता हूं।

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

a = []

आप द्वारा संदर्भित एक नई सूची प्राप्त करने की उम्मीद करते हैं a

क्यों चाहिए a=[]में

def x(a=[]):

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

def x(a=datetime.datetime.now()):

aजब आप परिभाषित या निष्पादित कर रहे होते हैं, तब क्या आप डेटाइम के अनुरूप डिफ़ॉल्ट करना चाहते हैं x? इस मामले में, पिछले एक के रूप में, मैं उसी व्यवहार को रखूंगा जैसे कि डिफ़ॉल्ट तर्क "असाइनमेंट" फ़ंक्शन का पहला निर्देश था ( datetime.now()फ़ंक्शन इन्वोकेशन पर कहा जाता है)। दूसरी ओर, यदि उपयोगकर्ता परिभाषा-समय मानचित्रण चाहता है, तो वह लिख सकता है:

b = datetime.datetime.now()
def x(a=b):

मुझे पता है, मुझे पता है: यह एक बंद है। वैकल्पिक रूप से पायथन परिभाषा-बाध्यकारी समय को बाध्य करने के लिए एक कीवर्ड प्रदान कर सकता है:

def x(static a=b):
83 LennartRegebro Jul 16 2009 at 01:54

ठीक है, कारण काफी सरल है कि जब कोड निष्पादित किया जाता है, तो बाइंडिंग की जाती है, और फ़ंक्शन परिभाषा को निष्पादित किया जाता है, ठीक है ... जब फ़ंक्शन परिभाषित होता है।

इसकी तुलना करें:

class BananaBunch:
    bananas = []

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

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

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

हां, यह अप्रत्याशित है। लेकिन एक बार पैसा गिर जाता है, यह पूरी तरह से फिट बैठता है कि अजगर सामान्य रूप से कैसे काम करता है। वास्तव में, यह एक अच्छी शिक्षण सहायता है, और एक बार जब आप समझ जाते हैं कि ऐसा क्यों होता है, तो आप अजगर को बेहतर तरीके से समझेंगे।

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

69 DimitrisFasarakisHilliard Dec 09 2015 at 14:13

आप आत्मनिरीक्षण क्यों नहीं करते?

मैं वास्तव में आश्चर्यचकित हूं कि किसी ने कॉलबॉल्स पर पायथन ( 2और 3लागू) द्वारा पेश किए गए आनंददायक आत्मनिरीक्षण का प्रदर्शन नहीं किया है ।

दिए गए एक साधारण छोटे कार्य को funcनिम्न प्रकार से दिया गया है:

>>> def func(a = []):
...    a.append(5)

जब पायथन इसका सामना करता है, तो पहली चीज जो यह करेगी वह codeइस फ़ंक्शन के लिए एक ऑब्जेक्ट बनाने के लिए इसे संकलित करता है। जबकि यह संकलन कदम किया जाता है, पायथन * का मूल्यांकन करता है और फिर फ़ंक्शन ऑब्जेक्ट में डिफ़ॉल्ट तर्क (यहां एक खाली सूची ) संग्रहीत करता []है । जैसा कि शीर्ष उत्तर में उल्लेख किया गया है: सूची aको अब फ़ंक्शन का सदस्य माना जा सकता है func

तो, चलो कुछ आत्मनिरीक्षण करते हैं, पहले और बाद में यह जांचने के लिए कि फ़ंक्शन ऑब्जेक्ट के अंदर सूची का विस्तार कैसे होता है। मैं इसके लिए उपयोग कर रहा हूँ Python 3.x, Python 2 के लिए समान लागू होता है (उपयोग __defaults__या func_defaultsPython 2 में, हाँ, एक ही चीज़ के लिए दो नाम)।

निष्पादन से पहले समारोह:

>>> def func(a = []):
...     a.append(5)
...     

पायथन द्वारा इस परिभाषा को निष्पादित करने के बाद यह निर्दिष्ट ( a = []यहां) कोई भी डिफ़ॉल्ट पैरामीटर लेगा और उन्हें __defaults__फ़ंक्शन ऑब्जेक्ट (प्रासंगिक अनुभाग: कॉलबल्स) के लिए विशेषता में क्रैम करेगा :

>>> func.__defaults__
([],)

ठीक है, तो एक खाली सूची में एकल प्रविष्टि के रूप में __defaults__, जैसा कि अपेक्षित था।

निष्पादन के बाद कार्य:

चलिए अब इस फंक्शन को अंजाम देते हैं:

>>> func()

अब, हम __defaults__फिर से देखते हैं :

>>> func.__defaults__
([5],)

आश्चर्यचकित? वस्तु के अंदर का मूल्य बदल जाता है! फ़ंक्शन के लिए लगातार कॉल अब बस उस एम्बेडेड listऑब्जेक्ट के लिए अपील करेंगे :

>>> func(); func(); func()
>>> func.__defaults__
([5, 5, 5, 5],)

इसलिए, आपके पास यह है, यही कारण है कि यह 'दोष' होता है, क्योंकि डिफ़ॉल्ट तर्क फ़ंक्शन ऑब्जेक्ट का हिस्सा हैं। यहां कुछ भी अजीब नहीं चल रहा है, यह सब थोड़ा आश्चर्यजनक है।

इसका मुकाबला करने के लिए सामान्य समाधान Noneडिफ़ॉल्ट के रूप में उपयोग करना है और फिर फ़ंक्शन बॉडी में प्रारंभ करना है:

def func(a = None):
    # or: a = [] if a is None else a
    if a is None:
        a = []

चूंकि फ़ंक्शन बॉडी को हर बार नए सिरे से निष्पादित किया जाता है, इसलिए यदि कोई तर्क पारित नहीं किया गया, तो आपको हमेशा एक नई नई खाली सूची मिलती है a


यह सत्यापित करने के लिए कि सूची में __defaults__वही है जो फ़ंक्शन में उपयोग किया गया है, funcआप फ़ंक्शन बॉडी के अंदर उपयोग idकी गई सूची को वापस करने के लिए अपने फ़ंक्शन को बदल सकते हैं a। फिर, इसे सूची में __defaults__(स्थिति [0]में __defaults__) से तुलना करें और आप देखेंगे कि ये वास्तव में एक ही सूची उदाहरण के लिए कैसे संदर्भित हैं:

>>> def func(a = []): 
...     a.append(5)
...     return id(a)
>>>
>>> id(func.__defaults__[0]) == func()
True

आत्मनिरीक्षण की शक्ति के साथ सभी!


* यह सत्यापित करने के लिए कि पायथन फ़ंक्शन के संकलन के दौरान डिफ़ॉल्ट तर्कों का मूल्यांकन करता है, निम्नलिखित को निष्पादित करने का प्रयास करें:

def bar(a=input('Did you just see me without calling the function?')): 
    pass  # use raw_input in Py2

जैसा कि आप देखेंगे, input()फ़ंक्शन के निर्माण की प्रक्रिया से पहले इसे बुलाया जाता है और इसे नाम barसे बांध दिया जाता है।

59 Brian Jul 16 2009 at 17:05

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

1. प्रदर्शन

def foo(arg=something_expensive_to_compute())):
    ...

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

2. बाध्य मापदंडों को मजबूर करना

एक उपयोगी चाल के लिए एक लैम्ब्डा का बाँध मानकों करने के लिए है वर्तमान में एक चर के बंधन जब लैम्ब्डा बनाई गई है। उदाहरण के लिए:

funcs = [ lambda i=i: i for i in range(10)]

यह क्रमशः 0,1,2,3 ... लौटने वाले कार्यों की एक सूची देता है। यदि व्यवहार को बदल दिया जाता है, तो वे इसके बजाय i iके कॉल-टाइम मान से बंध जाएंगे, इसलिए आपको उन फ़ंक्शन की सूची मिल जाएगी, जो सभी वापस आ गए 9

इसे लागू करने का एकमात्र तरीका यह होगा कि आई बाउंड के साथ एक और क्लोजर बनाया जाए, अर्थात:

def make_func(i): return lambda: i
funcs = [make_func(i) for i in range(10)]

3. आत्मनिरीक्षण

कोड पर विचार करें:

def foo(a='test', b=100, c=[]):
   print a,b,c

हम inspectमॉड्यूल का उपयोग करके तर्क और चूक के बारे में जानकारी प्राप्त कर सकते हैं , जो

>>> inspect.getargspec(foo)
(['a', 'b', 'c'], None, None, ('test', 100, []))

डॉक्यूमेंट जनरेशन, मेटाप्रोग्रामिंग, डेकोरेटर आदि जैसी चीजों के लिए यह जानकारी बहुत उपयोगी है।

अब, मान लीजिए कि चूक के व्यवहार को बदला जा सकता है ताकि यह बराबर हो:

_undefined = object()  # sentinel value

def foo(a=_undefined, b=_undefined, c=_undefined)
    if a is _undefined: a='test'
    if b is _undefined: b=100
    if c is _undefined: c=[]

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

55 LutzPrechelt Mar 30 2015 at 18:18

पायथन की रक्षा में 5 अंक

  1. सरलता : व्यवहार निम्नलिखित अर्थों में सरल है: अधिकांश लोग इस जाल में केवल एक बार गिरते हैं, कई बार नहीं।

  2. संगति : पायथन हमेशा वस्तुओं को पारित करता है, नामों को नहीं। डिफ़ॉल्ट पैरामीटर, जाहिर है, फ़ंक्शन हेडिंग का हिस्सा है (फ़ंक्शन बॉडी नहीं)। इसलिए इसे मॉड्यूल लोड समय पर मूल्यांकन किया जाना चाहिए (और केवल मॉड्यूल लोड समय पर, जब तक नेस्ट नहीं किया जाता है), फ़ंक्शन कॉल समय पर नहीं।

  3. उपयोगिता : जैसा कि फ्रेडरिक लुंड "पायथन में डिफ़ॉल्ट पैरामीटर मान" की अपनी व्याख्या में बताते हैं , वर्तमान व्यवहार उन्नत प्रोग्रामिंग के लिए काफी उपयोगी हो सकता है। (किफायत से इस्तेमाल करो।)

  4. पर्याप्त दस्तावेज : सबसे बुनियादी पायथन प्रलेखन में, ट्यूटोरियल, इस मुद्दे को जोर से "महत्वपूर्ण कार्यों को परिभाषित करने वाले खंड " के पहले उपधारा में "महत्वपूर्ण चेतावनी" के रूप में घोषित किया गया है । चेतावनी यहां तक ​​कि बोल्डफेस का उपयोग करती है, जो शायद ही कभी शीर्षकों के बाहर लागू होती है। RTFM: ठीक मैनुअल पढ़ें।

  5. मेटा-लर्निंग : जाल में गिरना वास्तव में एक बहुत ही उपयोगी क्षण है (कम से कम यदि आप एक चिंतनशील शिक्षार्थी हैं), क्योंकि आप बाद में ऊपर दिए गए बिंदु "संगति" को बेहतर ढंग से समझ पाएंगे और यह आपको पायथन के बारे में बहुत कुछ सिखाएगा।

53 ymv Jul 16 2009 at 02:15

इस व्यवहार को आसान तरीके से समझाया गया है:

  1. फ़ंक्शन (वर्ग आदि) घोषणा केवल एक बार निष्पादित की जाती है, सभी डिफ़ॉल्ट मान ऑब्जेक्ट बनाते हैं
  2. सब कुछ संदर्भ द्वारा पारित किया गया है

इसलिए:

def x(a=0, b=[], c=[], d=0):
    a = a + 1
    b = b + [1]
    c.append(1)
    print a, b, c
  1. a बदलता नहीं है - प्रत्येक असाइनमेंट कॉल नई int ऑब्जेक्ट बनाता है - नई ऑब्जेक्ट मुद्रित होती है
  2. b परिवर्तित नहीं होता है - डिफ़ॉल्ट मान और मुद्रित से नए सरणी का निर्माण होता है
  3. c परिवर्तन - ऑपरेशन एक ही वस्तु पर किया जाता है - और यह मुद्रित होता है
35 GlennMaynard Jul 16 2009 at 03:18

आप यह क्यों पूछ रहे हैं:

def func(a=[], b = 2):
    pass

आंतरिक रूप से इसके बराबर नहीं है:

def func(a=None, b = None):
    a_default = lambda: []
    b_default = lambda: 2
    def actual_func(a=None, b=None):
        if a is None: a = a_default()
        if b is None: b = b_default()
    return actual_func
func = func()

स्पष्ट रूप से फंक (कोई नहीं, कोई नहीं) के मामले को छोड़कर, जिसे हम अनदेखा करेंगे।

दूसरे शब्दों में, डिफ़ॉल्ट मापदंडों का मूल्यांकन करने के बजाय, उनमें से प्रत्येक को स्टोर क्यों नहीं किया जाता है, और फ़ंक्शन को कॉल करने पर उनका मूल्यांकन किया जाता है?

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

35 hynekcer Nov 23 2012 at 01:09

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

उदाहरण:

def foo(a=[]):                 # the same problematic function
    a.append(5)
    return a

>>> somevar = [1, 2]           # an example without a default parameter
>>> foo(somevar)
[1, 2, 5]
>>> somevar
[1, 2, 5]                      # usually expected [1, 2]

समाधान : एक प्रतिलिपि
एक बिल्कुल सुरक्षित समाधान करने के लिए है copyया deepcopyइनपुट वस्तु पहले और उसके बाद प्रति के साथ जो कुछ भी करना है।

def foo(a=[]):
    a = a[:]     # a copy
    a.append(5)
    return a     # or everything safe by one line: "return a + [5]"

कई अंतर्निहित उत्परिवर्तनीय प्रकारों की एक प्रतिलिपि विधि होती है जैसे some_dict.copy()या some_set.copy()आसानी से somelist[:]या जैसे कॉपी की जा सकती है list(some_list)। हर वस्तु की नकल copy.copy(any_object)या उससे अधिक पूरी तरह से copy.deepcopy()(बाद में उपयोगी वस्तु अगर उत्परिवर्तनीय वस्तुओं से बनी हो) से की जा सकती है। कुछ वस्तुएं मूल रूप से "फ़ाइल" ऑब्जेक्ट जैसे साइड इफेक्ट्स पर आधारित होती हैं और कॉपी द्वारा सार्थक रूप से पुन: प्रस्तुत नहीं की जा सकती हैं। नकल

एक समान SO प्रश्न के लिए उदाहरण समस्या

class Test(object):            # the original problematic class
  def __init__(self, var1=[]):
    self._var1 = var1

somevar = [1, 2]               # an example without a default parameter
t1 = Test(somevar)
t2 = Test(somevar)
t1._var1.append([1])
print somevar                  # [1, 2, [1]] but usually expected [1, 2]
print t2._var1                 # [1, 2, [1]] but usually expected [1, 2]

इस फ़ंक्शन द्वारा लौटाए गए उदाहरण के किसी भी सार्वजनिक विशेषता में इसे न तो सहेजा जाना चाहिए । (यह मानते हुए कि निजी । उदाहरण के गुण इस वर्ग या प्रथा के अनुसार उपवर्गों के बाहर से संशोधित नहीं किया जाना चाहिए यानी _var1एक निजी विशेषता है)

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

2)
केवल अगर वास्तविक पैरामीटर पर साइड इफेक्ट की आवश्यकता होती है लेकिन डिफ़ॉल्ट पैरामीटर पर अवांछित है तो उपयोगी समाधान def ...(var1=None): if var1 is None: var1 = [] मोर है।

3) कुछ मामलों में डिफ़ॉल्ट मापदंडों का परस्पर व्यवहार उपयोगी है ।

31 Ben May 23 2011 at 11:24

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

>>> def foo(a):
    a.append(5)
    print a

>>> a  = [5]
>>> foo(a)
[5, 5]
>>> foo(a)
[5, 5, 5]
>>> foo(a)
[5, 5, 5, 5]
>>> foo(a)
[5, 5, 5, 5, 5]

इस कोड में दृष्टि में कोई डिफ़ॉल्ट मान नहीं है, लेकिन आपको ठीक यही समस्या मिलती है।

समस्या यह है कि कॉलर से पास किए गए एक परिवर्तनशील चर fooको संशोधित कर रहा है, जब कॉलर को इसकी उम्मीद नहीं है। इस तरह से कोड ठीक होगा यदि फ़ंक्शन को कुछ ऐसा कहा जाता है append_5; तब कॉल करने वाला फ़ंक्शन को कॉल करेगा ताकि वे उस मान को संशोधित कर सकें, और व्यवहार अपेक्षित होगा। लेकिन इस तरह के एक फ़ंक्शन को डिफ़ॉल्ट तर्क लेने की बहुत संभावना नहीं होगी, और संभवत: सूची वापस नहीं होगी (क्योंकि कॉलर के पास पहले से ही उस सूची का संदर्भ है, वह जिसे अभी पारित किया गया है)।

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

यदि आपको किसी चीज़ की गणना करने के लिए स्थानीय अस्थायी रूप से अस्थायी रूप से हेरफेर करने की आवश्यकता है, और आपको तर्क मान से अपना हेरफेर शुरू करने की आवश्यकता है, तो आपको एक प्रतिलिपि बनाने की आवश्यकता है।

27 Stéphane Mar 27 2015 at 06:14

पहले से ही व्यस्त विषय, लेकिन जो मैंने यहां पढ़ा, उससे निम्नलिखित ने मुझे यह महसूस करने में मदद की कि यह आंतरिक रूप से कैसे काम कर रहा है:

def bar(a=[]):
     print id(a)
     a = a + [1]
     print id(a)
     return a

>>> bar()
4484370232
4484524224
[1]
>>> bar()
4484370232
4484524152
[1]
>>> bar()
4484370232 # Never change, this is 'class property' of the function
4484523720 # Always a new object 
[1]
>>> id(bar.func_defaults[0])
4484370232
25 JasonBaker Jul 16 2009 at 06:18

यह एक प्रदर्शन अनुकूलन है। इस कार्यक्षमता के परिणामस्वरूप, आपको लगता है कि इन दो फ़ंक्शन कॉल में से कौन सा तेज़ है?

def print_tuple(some_tuple=(1,2,3)):
    print some_tuple

print_tuple()        #1
print_tuple((1,2,3)) #2

मैं तुम्हें एक संकेत देता हूँ। यहाँ disassembly (देखें) हैhttp://docs.python.org/library/dis.html):

#1

0 LOAD_GLOBAL              0 (print_tuple)
3 CALL_FUNCTION            0
6 POP_TOP
7 LOAD_CONST               0 (None)
10 RETURN_VALUE

#

 0 LOAD_GLOBAL              0 (print_tuple)
 3 LOAD_CONST               4 ((1, 2, 3))
 6 CALL_FUNCTION            1
 9 POP_TOP
10 LOAD_CONST               0 (None)
13 RETURN_VALUE

मुझे संदेह है कि अनुभवी व्यवहार का व्यावहारिक उपयोग होता है (जो वास्तव में स्थैतिक चर का उपयोग करते हैं, बग के प्रजनन के बिना?)

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

25 AaronHall May 01 2016 at 23:20

पायथन: द म्यूटेबल डिफ़ॉल्ट तर्क

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

जब वे उत्परिवर्तित होते हैं, जब उत्परिवर्तित होते हैं (उदाहरण के लिए, इसमें एक तत्व जोड़कर) वे लगातार कॉल पर उत्परिवर्तित रहते हैं।

वे उत्परिवर्तित रहते हैं क्योंकि वे हर बार एक ही वस्तु हैं।

समतुल्य कोड:

चूंकि सूची फ़ंक्शन के लिए बाध्य है जब फ़ंक्शन ऑब्जेक्ट संकलित और त्वरित किया जाता है, यह:

def foo(mutable_default_argument=[]): # make a list the default argument
    """function that uses a list"""

इसके लगभग बराबर है:

_a_list = [] # create a list in the globals

def foo(mutable_default_argument=_a_list): # make it the default argument
    """function that uses a list"""

del _a_list # remove globals name binding

प्रदर्शन

यहां एक प्रदर्शन है - आप सत्यापित कर सकते हैं कि वे हर बार उसी वस्तु के होते हैं, जिसके द्वारा उन्हें संदर्भित किया जाता है

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

example.py

print('1. Global scope being evaluated')

def create_list():
    '''noisily create a list for usage as a kwarg'''
    l = []
    print('3. list being created and returned, id: ' + str(id(l)))
    return l

print('2. example_function about to be compiled to an object')

def example_function(default_kwarg1=create_list()):
    print('appending "a" in default default_kwarg1')
    default_kwarg1.append("a")
    print('list with id: ' + str(id(default_kwarg1)) + 
          ' - is now: ' + repr(default_kwarg1))

print('4. example_function compiled: ' + repr(example_function))


if __name__ == '__main__':
    print('5. calling example_function twice!:')
    example_function()
    example_function()

और इसके साथ चल रहा है python example.py:

1. Global scope being evaluated
2. example_function about to be compiled to an object
3. list being created and returned, id: 140502758808032
4. example_function compiled: <function example_function at 0x7fc9590905f0>
5. calling example_function twice!:
appending "a" in default default_kwarg1
list with id: 140502758808032 - is now: ['a']
appending "a" in default default_kwarg1
list with id: 140502758808032 - is now: ['a', 'a']

क्या यह "लिस्ट विस्मय" के सिद्धांत का उल्लंघन करता है?

निष्पादन का यह क्रम अक्सर पायथन के नए उपयोगकर्ताओं को भ्रमित कर रहा है। यदि आप पायथन निष्पादन मॉडल को समझते हैं, तो यह काफी अपेक्षित है।

नए पायथन उपयोगकर्ताओं के लिए सामान्य निर्देश:

लेकिन यही कारण है कि नए उपयोगकर्ताओं के लिए सामान्य निर्देश इसके बजाय उनके डिफ़ॉल्ट तर्क बनाना है:

def example_function_2(default_kwarg=None):
    if default_kwarg is None:
        default_kwarg = []

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

जैसा कि नियंत्रण प्रवाह पर ट्यूटोरियल अनुभाग कहता है:

यदि आप नहीं चाहते हैं कि डिफ़ॉल्ट को बाद की कॉल के बीच साझा किया जाए, तो आप इसके स्थान पर फ़ंक्शन लिख सकते हैं:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L
24 Baczek Jul 16 2009 at 19:19

सबसे छोटा उत्तर शायद "परिभाषा निष्पादन" है, इसलिए पूरे तर्क का कोई मतलब नहीं है। एक और अधिक उदाहरण के रूप में, आप इसका हवाला दे सकते हैं:

def a(): return []

def b(x=a()):
    print x

उम्मीद है कि यह दिखाने के लिए पर्याप्त है कि defकथन के निष्पादन के समय डिफ़ॉल्ट तर्क अभिव्यक्तियों को निष्पादित करना आसान नहीं है या समझ में नहीं आता है, या दोनों।

जब आप डिफ़ॉल्ट कंस्ट्रक्टर का उपयोग करने का प्रयास करते हैं तो मैं मानता हूं कि यह एक गोच है।

21 DmitryMinkovsky Apr 25 2012 at 02:43

यदि आप निम्नलिखित बातों को ध्यान में रखते हैं तो यह व्यवहार आश्चर्यजनक नहीं है:

  1. असाइनमेंट के प्रयासों पर रीड-ओनली क्लास विशेषताओं का व्यवहार, और वह
  2. कार्य ऑब्जेक्ट हैं (स्वीकृत उत्तर में अच्छी तरह से समझाया गया है)।

इस थ्रेड में (2) की भूमिका को बड़े पैमाने पर कवर किया गया है। (1) संभवतया विस्मय कारक है, क्योंकि अन्य भाषाओं से आने पर यह व्यवहार "सहज" नहीं है।

(1) कक्षाओं पर पायथन ट्यूटोरियल में वर्णित है । केवल-पढ़ने के लिए एक विशेषता वर्ग के लिए एक मान असाइन करने के प्रयास में:

... अंतरतम दायरे के बाहर पाए जाने वाले सभी चर केवल-पढ़ने के लिए हैं ( इस तरह के चर को लिखने का प्रयास बस अंतरतम दायरे में एक नया स्थानीय चर बनाएगा, जिससे बाहरी नाम अपरिवर्तित नाम बदल जाता है )।

मूल उदाहरण पर वापस देखें और उपरोक्त बिंदुओं पर विचार करें:

def foo(a=[]):
    a.append(5)
    return a

यहाँ fooएक वस्तु है और (पर उपलब्ध ) aका एक गुण है । चूँकि एक सूची है, परस्पर है और इस प्रकार यह एक पढ़ने-लिखने की विशेषता है । यह फ़ंक्शन द्वारा त्वरित होने पर हस्ताक्षर द्वारा निर्दिष्ट के रूप में खाली सूची में आरंभीकृत होता है, और जब तक फ़ंक्शन ऑब्जेक्ट मौजूद रहता है तब तक पढ़ने और लिखने के लिए उपलब्ध है।foofoo.func_defs[0]aafoo

fooकिसी डिफ़ॉल्ट को ओवरराइड किए बिना कॉल करना उस डिफ़ॉल्ट के मान का उपयोग करता है foo.func_defs। इस स्थिति में, फ़ंक्शन ऑब्जेक्ट के कोड दायरे foo.func_defs[0]में उपयोग किया जाता aहै। परिवर्तन करने के लिए aबदलने के foo.func_defs[0], जो का हिस्सा है fooवस्तु और में कोड के निष्पादन के बीच बनी रहती है foo

अब, इसे अन्य भाषाओं के डिफ़ॉल्ट तर्क व्यवहार के अनुकरण पर प्रलेखन से उदाहरण के लिए तुलना करें , जैसे कि फ़ंक्शन हस्ताक्षर डिफ़ॉल्ट को हर बार फ़ंक्शन निष्पादित होने पर उपयोग किया जाता है:

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

खाते में (1) और (2) लेते हुए , कोई यह देख सकता है कि यह वांछित व्यवहार क्यों पूरा करता है:

  • जब fooफ़ंक्शन ऑब्जेक्ट को तुरंत foo.func_defs[0]किया जाता है None, तो एक अपरिवर्तनीय ऑब्जेक्ट पर सेट किया जाता है।
  • जब फ़ंक्शन को डिफॉल्ट्स के साथ निष्पादित किया जाता है ( Lफ़ंक्शन कॉल में निर्दिष्ट कोई पैरामीटर नहीं है ), foo.func_defs[0]( None) के रूप में स्थानीय दायरे में उपलब्ध है L
  • पर L = [], असाइनमेंट सफल नहीं हो सकता foo.func_defs[0], क्योंकि वह विशेषता केवल-पढ़ने के लिए है।
  • प्रति (1) , नाम Lसे एक नया स्थानीय चर भी स्थानीय दायरे में बनाया गया है और फ़ंक्शन कॉल के शेष के लिए उपयोग किया जाता है। foo.func_defs[0]इस प्रकार भविष्य के आक्रमणों के लिए अपरिवर्तित रहता है foo
20 hugo24 Feb 28 2013 at 18:10

कोई भी नहीं का उपयोग करके एक साधारण समाधान

>>> def bar(b, data=None):
...     data = data or []
...     data.append(b)
...     return data
... 
>>> bar(3)
[3]
>>> bar(3)
[3]
>>> bar(3)
[3]
>>> bar(3, [34])
[34, 3]
>>> bar(3, [34])
[34, 3]
19 Alexander Sep 12 2015 at 13:00

मैं एक फ़ंक्शन के लिए डिफ़ॉल्ट सूची मान पास करने के लिए एक वैकल्पिक संरचना प्रदर्शित करने जा रहा हूं (यह शब्दकोशों के साथ समान रूप से अच्छी तरह से काम करता है)।

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

गलत तरीका (शायद ...) :

def foo(list_arg=[5]):
    return list_arg

a = foo()
a.append(6)
>>> a
[5, 6]

b = foo()
b.append(7)
# The value of 6 appended to variable 'a' is now part of the list held by 'b'.
>>> b
[5, 6, 7]  

# Although 'a' is expecting to receive 6 (the last element it appended to the list),
# it actually receives the last element appended to the shared list.
# It thus receives the value 7 previously appended by 'b'.
>>> a.pop()             
7

आप सत्यापित कर सकते हैं कि वे एक और एक ही वस्तु का उपयोग करके हैं id:

>>> id(a)
5347866528

>>> id(b)
5347866528

ब्रेट स्लेटकिन के "प्रभावी पायथन: 59 बेहतर पायथन को लिखने के लिए विशिष्ट तरीके", आइटम 20: Noneगतिशील डिफ़ॉल्ट तर्क को निर्दिष्ट करने के लिए उपयोग और डॉकस्ट्रिंग (पृष्ठ 48)।

पाइथन में वांछित परिणाम प्राप्त करने के लिए कन्वेंशन को डिफ़ॉल्ट मान प्रदान करना है Noneऔर डॉकस्ट्रिंग में वास्तविक व्यवहार का दस्तावेजीकरण करना है।

यह कार्यान्वयन सुनिश्चित करता है कि फ़ंक्शन के लिए प्रत्येक कॉल या तो डिफ़ॉल्ट सूची प्राप्त करता है या फिर फ़ंक्शन को दी गई सूची।

पसंदीदा तरीका :

def foo(list_arg=None):
   """
   :param list_arg:  A list of input values. 
                     If none provided, used a list with a default value of 5.
   """
   if not list_arg:
       list_arg = [5]
   return list_arg

a = foo()
a.append(6)
>>> a
[5, 6]

b = foo()
b.append(7)
>>> b
[5, 7]

c = foo([10])
c.append(11)
>>> c
[10, 11]

'गलत पद्धति' के लिए वैध उपयोग के मामले हो सकते हैं जिससे प्रोग्रामर ने डिफ़ॉल्ट सूची पैरामीटर को साझा करने का इरादा किया है, लेकिन यह नियम की तुलना में अधिक संभावना है।

17 Marcin Mar 21 2012 at 00:22

यहाँ समाधान हैं:

  1. Noneअपने डिफ़ॉल्ट मान (या एक गैर object) के रूप में उपयोग करें , और रनटाइम पर अपने मूल्यों को बनाने के लिए उस पर स्विच करें; या
  2. lambdaअपने डिफ़ॉल्ट पैरामीटर के रूप में उपयोग करें , और इसे डिफ़ॉल्ट मान प्राप्त करने के लिए एक कोशिश ब्लॉक के भीतर कॉल करें (यह इस तरह की चीज है कि लैम्ब्डा एब्स्ट्रक्शन के लिए है)।

दूसरा विकल्प अच्छा है क्योंकि फ़ंक्शन के उपयोगकर्ता एक कॉल करने योग्य में गुजर सकते हैं, जो पहले से मौजूद हो सकता है (जैसे कि type)

16 joedborg Jan 15 2013 at 18:02

आप ऑब्जेक्ट के स्थान पर इसे गोल कर सकते हैं (और इसलिए गुंजाइश के साथ टाई):

def foo(a=[]):
    a = list(a)
    a.append(5)
    return a

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

16 Saish Sep 12 2014 at 05:05

जब हम ऐसा करते हैं:

def foo(a=[]):
    ...

... हम तर्क aको एक अनाम सूची में निर्दिष्ट करते हैं , यदि कॉलर कोई मान नहीं देता है।

इस चर्चा के लिए चीजों को सरल बनाने के लिए, आइए अस्थायी रूप से अनाम सूची को नाम दें। कैसे के बारे में pavlo?

def foo(a=pavlo):
   ...

किसी भी समय, यदि कॉलर हमें नहीं बताता है कि क्या aहै, तो हम पुन: उपयोग करते हैं pavlo

यदि pavloउत्परिवर्तनीय (परिवर्तनीय) है, और fooइसे संशोधित करना समाप्त होता है, तो अगली बार जब हम नोटिस करते हैं तो एक प्रभाव fooको निर्दिष्ट किए बिना कहा जाता है a

तो यह वही है जो आप देख रहे हैं (याद रखें, pavloयह आरंभिक है []):

 >>> foo()
 [5]

अब, pavlo[5] है।

कॉलिंग foo()फिर से संशोधित करता है pavlo:

>>> foo()
[5, 5]

निर्दिष्ट aजब बुला foo()सुनिश्चित pavloछुआ नहीं है।

>>> ivan = [1, 2, 3, 4]
>>> foo(a=ivan)
[1, 2, 3, 4, 5]
>>> ivan
[1, 2, 3, 4, 5]

तो, pavloअभी भी है [5, 5]

>>> foo()
[5, 5, 5]
16 bgreen-litl Feb 06 2015 at 04:44

मैं कभी-कभी इस व्यवहार का निम्न पैटर्न के विकल्प के रूप में शोषण करता हूं:

singleton = None

def use_singleton():
    global singleton

    if singleton is None:
        singleton = _make_singleton()

    return singleton.use_me()

यदि singletonकेवल द्वारा उपयोग किया जाता है use_singleton, तो मुझे प्रतिस्थापन के रूप में निम्न पैटर्न पसंद है:

# _make_singleton() is called only once when the def is executed
def use_singleton(singleton=_make_singleton()):
    return singleton.use_me()

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

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

13 ChristosHayward Jul 17 2009 at 02:17

यह सच हो सकता है:

  1. कोई व्यक्ति हर भाषा / पुस्तकालय सुविधा का उपयोग कर रहा है, और
  2. यहां व्यवहार को बदलना बीमार होगा, लेकिन

यह पूरी तरह से ऊपर की दोनों विशेषताओं को पकड़ने के लिए सुसंगत है और फिर भी एक और बिंदु बनाता है:

  1. यह एक भ्रामक विशेषता है और यह पायथन में दुर्भाग्यपूर्ण है।

अन्य उत्तर, या उनमें से कम से कम कुछ या तो अंक 1 और 2 बनाते हैं, लेकिन 3 नहीं, या बिंदु 3 और नीचे बिंदु 1 और 2 बनाते हैं। लेकिन तीनों सत्य हैं।

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

मौजूदा व्यवहार pythonic नहीं है, और अजगर सफल हुआ क्योंकि बहुत कम भाषा के बारे में कम से कम विस्मय कहीं के सिद्धांत का उल्लंघन करता है के पास इस बुरी तरह से। यह एक वास्तविक समस्या है, कि इसे उखाड़ना बुद्धिमानी होगी या नहीं। यह एक डिजाइन दोष है। यदि आप व्यवहार का पता लगाने की कोशिश करके भाषा को बहुत बेहतर समझते हैं, तो मैं कह सकता हूं कि C ++ यह सब और बहुत कुछ करता है; उदाहरण के लिए, सूक्ष्म सूचक त्रुटियों के लिए आप नेविगेट करके बहुत कुछ सीखते हैं। लेकिन यह पायथोनिक नहीं है: जो लोग इस व्यवहार के सामने दृढ़ता के लिए अजगर की परवाह करते हैं, वे लोग हैं जो भाषा के लिए तैयार हैं क्योंकि पायथन में अन्य भाषा की तुलना में बहुत कम आश्चर्य है। डाबब्लर्स और जिज्ञासु तब पाइथोनिस्टस बन जाते हैं जब वे चकित होते हैं कि किसी काम को करने में कितना कम समय लगता है - न कि एक डिज़ाइन फ़्ल के कारण - मेरा मतलब है, छिपी हुई तर्क पहेली - जो प्रोग्रामर के अंतर्ज्ञान के खिलाफ कटौती करता है जो पाइथन के लिए खींचे जाते हैं क्योंकि यह काम करता है

10 MarkRansom Oct 18 2017 at 00:38

यह एक डिजाइन दोष नहीं है । जो भी इस पर यात्रा करता है वह कुछ गलत कर रहा है।

आपके द्वारा इस समस्या में भाग लेने के 3 मामले हैं:

  1. आप फ़ंक्शन के साइड इफेक्ट के रूप में तर्क को संशोधित करने का इरादा रखते हैं। इस मामले में यह कभी नहीं होता कि डिफ़ॉल्ट तर्क हो। एकमात्र अपवाद तब होता है जब आप फ़ंक्शन विशेषताओं का उपयोग करने के लिए तर्क सूची का दुरुपयोग कर रहे होते हैं, जैसे cache={}, और आपको फ़ंक्शन को वास्तविक तर्क के साथ कॉल करने की उम्मीद नहीं होगी।
  2. आप तर्क को बिना तर्क के छोड़ने का इरादा रखते हैं, लेकिन आपने गलती से इसे संशोधित किया है। यह एक बग है, इसे ठीक करें।
  3. आप फ़ंक्शन के अंदर उपयोग के लिए तर्क को संशोधित करने का इरादा रखते हैं, लेकिन फ़ंक्शन के बाहर संशोधन देखने योग्य होने की उम्मीद नहीं करता है। उस मामले में आपको तर्क की एक प्रति बनाने की आवश्यकता है , चाहे वह डिफ़ॉल्ट थी या नहीं! अजगर कॉल-बाय-वैल्यू भाषा नहीं है, इसलिए यह आपके लिए कॉपी नहीं बनाता है, आपको इसके बारे में स्पष्ट होना चाहिए।

प्रश्न में उदाहरण श्रेणी 1 या 3 में गिर सकता है। यह विचित्र है कि यह पारित सूची को संशोधित करता है और इसे वापस लौटाता है; आपको एक या दूसरे को चुनना चाहिए।

9 Norfeldt Jul 22 2013 at 14:35

इस "बग" ने मुझे बहुत सारे ओवरटाइम काम के घंटे दिए! लेकिन मुझे इसका संभावित उपयोग दिखाई देने लगा है (लेकिन मुझे इसका निष्पादन समय पर होना पसंद है, फिर भी)

मैं आपको एक उपयोगी उदाहरण के रूप में जो कुछ देखता हूं वह आपको देने वाला हूं।

def example(errors=[]):
    # statements
    # Something went wrong
    mistake = True
    if mistake:
        tryToFixIt(errors)
        # Didn't work.. let's try again
        tryToFixItAnotherway(errors)
        # This time it worked
    return errors

def tryToFixIt(err):
    err.append('Attempt to fix it')

def tryToFixItAnotherway(err):
    err.append('Attempt to fix it by another way')

def main():
    for item in range(2):
        errors = example()
    print '\n'.join(errors)

main()

निम्नलिखित प्रिंट करता है

Attempt to fix it
Attempt to fix it by another way
Attempt to fix it
Attempt to fix it by another way
8 ytpillai May 26 2015 at 06:04

बस फ़ंक्शन को होने के लिए बदलें:

def notastonishinganymore(a = []): 
    '''The name is just a joke :)'''
    a = a[:]
    a.append(5)
    return a
7 user2384994 Aug 22 2013 at 12:58

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

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

उपरोक्त दो बिंदुओं को स्वीकार करते हुए, आइए बताते हैं कि अजगर कोड का क्या हुआ। यह केवल वस्तुओं के लिए संदर्भ से गुजरने के कारण है, लेकिन परस्पर / अपरिवर्तनीय, या निश्चित रूप से इस तथ्य से कोई लेना-देना नहीं है कि "डीफ़" कथन केवल एक बार निष्पादित होने पर निष्पादित किया जाता है।

[] एक वस्तु है, इसलिए अजगर को [] के संदर्भ में पास किया जाता है a, यानी, aकेवल एक संकेतक है [] जो एक वस्तु के रूप में स्मृति में निहित है। हालाँकि, [] की एक ही प्रति है, इसके कई संदर्भ हैं। पहले फू () के लिए, सूची [] को परिशिष्ट विधि द्वारा 1 में बदल दिया जाता है । लेकिन ध्यान दें कि सूची वस्तु की केवल एक प्रति है और यह वस्तु अब 1 हो गई है । दूसरा फू () चलाने के दौरान, जो वेबबोट कहता है, (आइटम का मूल्यांकन नहीं किया जाता है) गलत है। aसूची ऑब्जेक्ट होने के लिए मूल्यांकन किया जाता है, हालांकि अब ऑब्जेक्ट की सामग्री 1 है । यह संदर्भ से गुजरने का प्रभाव है! फू (3) का परिणाम उसी तरह आसानी से प्राप्त किया जा सकता है।

मेरे उत्तर को और मान्य करने के लिए, आइए दो अतिरिक्त कोडों पर एक नज़र डालते हैं।

====== नंबर 2 ========

def foo(x, items=None):
    if items is None:
        items = []
    items.append(x)
    return items

foo(1)  #return [1]
foo(2)  #return [2]
foo(3)  #return [3]

[]एक वस्तु है, ऐसा है None(पूर्व परस्पर परिवर्तनशील है जबकि बाद वाला अपरिवर्तनीय है। लेकिन परिवर्तनशीलता का सवाल से कोई लेना-देना नहीं है)। अंतरिक्ष में कोई नहीं है, लेकिन हम जानते हैं कि यह वहां है और वहां कोई नहीं की केवल एक प्रति है। इसलिए हर बार फू का आह्वान किया जाता है, वस्तुओं का मूल्यांकन किया जाता है (जैसा कि कुछ जवाब के विपरीत है कि यह केवल एक बार मूल्यांकन किया जाता है) कोई नहीं, स्पष्ट होने के लिए, संदर्भ (या पते) में से कोई नहीं। फिर foo में, आइटम को बदल दिया जाता है [], अर्थात, किसी अन्य ऑब्जेक्ट को इंगित करता है जिसका एक अलग पता है।

====== नंबर 3 =======

def foo(x, items=[]):
    items.append(x)
    return items

foo(1)    # returns [1]
foo(2,[]) # returns [2]
foo(3)    # returns [1,3]

फू का आह्वान (1) एक सूची वस्तु [] को एक पते के साथ इंगित करते हैं, 11111111 कहते हैं। सूची की सामग्री अगली कड़ी में foo फ़ंक्शन में 1 से बदल जाती है , लेकिन पता नहीं बदला गया है, फिर भी 11111111 .फिर फू (2, []) आ रहा है। हालाँकि [] in foo (2, []) में डिफ़ॉल्ट पैरामीटर [] के समान ही सामग्री है, जब foo (१) कहते हैं, तो उनका पता अलग होता है! चूंकि हम स्पष्ट रूप से पैरामीटर प्रदान करते हैं, itemsइस नए का पता लेना है [], 2222222 कहते हैं, और कुछ बदलाव करने के बाद इसे वापस लौटाएं। अब foo (3) को निष्पादित किया जाता है। चूंकि केवल xप्रदान किया गया है, इसलिए आइटम को फिर से अपना डिफ़ॉल्ट मान लेना होगा। डिफ़ॉल्ट मान क्या है? फू फंक्शन को परिभाषित करते समय इसे सेट किया जाता है: सूची ऑब्जेक्ट 11111111 में स्थित है। इसलिए आइटम का मूल्यांकन 11111111 के तत्व के रूप में किया जाता है। 2222222 पर स्थित सूची में एक तत्व 2 भी शामिल है, लेकिन यह किसी भी आइटम द्वारा इंगित नहीं किया गया है। अधिक। नतीजतन, 3 का एक परिशिष्ट items[1,3] बना देगा ।

उपरोक्त स्पष्टीकरण से, हम देख सकते हैं कि स्वीकार किए गए उत्तर में सुझाए गए effbot वेबपेज इस प्रश्न का प्रासंगिक उत्तर देने में विफल रहे। क्या अधिक है, मुझे लगता है कि effbot वेबपेज में एक बिंदु गलत है। मुझे लगता है कि UI.Button के बारे में कोड सही है:

for i in range(10):
    def callback():
        print "clicked button", i
    UI.Button("button %s" % i, callback)

प्रत्येक बटन एक अलग कॉलबैक फ़ंक्शन को पकड़ सकता है जो अलग-अलग मूल्य प्रदर्शित करेगा i। मैं यह दिखाने के लिए एक उदाहरण प्रदान कर सकता हूं:

x=[]
for i in range(10):
    def callback():
        print(i)
    x.append(callback) 

यदि हम निष्पादित x[7]()करते हैं तो हमें उम्मीद के मुताबिक 7 मिलेंगे, और x[9]()9, दूसरा मूल्य देगा i

6 MisterMiyagi Dec 15 2018 at 19:09

TLDR: परिभाषित समय चूक लगातार और सख्ती से अधिक अभिव्यंजक हैं।


परिभाषित करने गुंजाइश: एक समारोह को परिभाषित करना दो स्कोप को प्रभावित करता है युक्त समारोह, और निष्पादन गुंजाइश द्वारा निहित कार्य करते हैं। हालांकि यह स्पष्ट है कि मैप्स को ब्लॉक करने के तरीके, सवाल यह है कि यह कहां def <name>(<args=defaults>):है:

...                           # defining scope
def name(parameter=default):  # ???
    ...                       # execution scope

def nameभाग जाना चाहिए परिभाषित करने दायरे में मूल्यांकन - हम चाहते हैं name, वहाँ उपलब्ध होने की सब के बाद। केवल अपने अंदर के फंक्शन का मूल्यांकन करना इसे दुर्गम बना देगा।

चूंकि parameterएक स्थिर नाम है, हम इसे उसी समय "मूल्यांकन" कर सकते हैं def name। यह भी name(parameter=...):एक नंगे के बजाय एक ज्ञात हस्ताक्षर के साथ समारोह का उत्पादन लाभ है name(...):

अब, कब मूल्यांकन करना है default?

संगति पहले से ही "परिभाषा पर" कहती है: बाकी सब कुछ def <name>(<args=defaults>):सबसे अच्छी तरह से परिभाषा में मूल्यांकन किया गया है। इसके कुछ हिस्सों में देरी करना आश्चर्यजनक विकल्प होगा।

दो विकल्प समान नहीं हैं, या तो: यदि defaultपरिभाषा समय पर मूल्यांकन किया जाता है , तो यह अभी भी निष्पादन समय को प्रभावित कर सकता है । यदि defaultनिष्पादन समय पर मूल्यांकन किया जाता है, तो यह परिभाषा समय को प्रभावित नहीं कर सकता है । "परिभाषा में" चुनना दोनों मामलों को व्यक्त करने की अनुमति देता है, जबकि "निष्पादन पर" चुनना केवल एक को व्यक्त कर सकता है:

def name(parameter=defined):  # set default at definition time
    ...

def name(parameter=default):     # delay default until execution time
    parameter = default if parameter is None else parameter
    ...
4 PrzemekD Jan 03 2019 at 14:38

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

हम एक डेकोरेटर के साथ इस व्यवहार को "ठीक" करेंगे जो अपने डिफ़ॉल्ट मान पर छोड़े गए प्रत्येक स्थितीय तर्क के लिए एक ही उदाहरण का पुन: उपयोग करने के बजाय डिफ़ॉल्ट मान को कॉपी करेगा।

import inspect
from copy import copy

def sanify(function):
    def wrapper(*a, **kw):
        # store the default values
        defaults = inspect.getargspec(function).defaults # for python2
        # construct a new argument list
        new_args = []
        for i, arg in enumerate(defaults):
            # allow passing positional arguments
            if i in range(len(a)):
                new_args.append(a[i])
            else:
                # copy the value
                new_args.append(copy(arg))
        return function(*new_args, **kw)
    return wrapper

अब इस डेकोरेटर का उपयोग करके हमारे फ़ंक्शन को फिर से परिभाषित करें:

@sanify
def foo(a=[]):
    a.append(5)
    return a

foo() # '[5]'
foo() # '[5]' -- as desired

यह कई तर्कों को लेने वाले कार्यों के लिए विशेष रूप से साफ है। तुलना करें:

# the 'correct' approach
def bar(a=None, b=None, c=None):
    if a is None:
        a = []
    if b is None:
        b = []
    if c is None:
        c = []
    # finally do the actual work

साथ से

# the nasty decorator hack
@sanify
def bar(a=[], b=[], c=[]):
    # wow, works right out of the box!

यह ध्यान रखना महत्वपूर्ण है कि यदि आप कीवर्ड args का उपयोग करने का प्रयास करते हैं तो उपरोक्त समाधान टूट जाता है, जैसे:

foo(a=[4])

डेकोरेटर को इसके लिए अनुमति देने के लिए समायोजित किया जा सकता है, लेकिन हम इसे पाठक के लिए एक अभ्यास के रूप में छोड़ते हैं;)