सवाल "कम एस्टोनिश" और म्यूटेबल डिफॉल्ट तर्क


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

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

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

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

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

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

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

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

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

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

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


2057
2017-07-15 18:00


मूल


पूरक प्रश्न - परिवर्तनीय डिफ़ॉल्ट तर्क के लिए अच्छा उपयोग - Jonathan
मुझे संदेह नहीं है कि उत्परिवर्ती तर्क एक औसत व्यक्ति के लिए कम से कम विस्मयकारी सिद्धांत का उल्लंघन करते हैं, और मैंने शुरुआती लोगों को वहां कदम उठाने को देखा है, फिर मेलिंग टुपल्स के साथ मेलिंग सूचियों को बदल दिया है। फिर भी परिवर्तनीय तर्क अभी भी पायथन जेन (पेप 20) के अनुरूप हैं और "डच के लिए स्पष्ट" (हार्ड कोर पायथन प्रोग्रामर द्वारा समझा / शोषित) खंड में आते हैं। डॉक्टर स्ट्रिंग के साथ अनुशंसित कामकाज सबसे अच्छा है, फिर भी डॉक्टर स्ट्रिंग्स के प्रतिरोध और किसी भी (लिखित) दस्तावेज़ आजकल असामान्य नहीं हैं। व्यक्तिगत रूप से, मैं एक सजावट पसंद करूंगा (@fixed_defaults कहें)। - Serge
जब मैं इस पर आ जाता हूं तो मेरा तर्क यह है: "आपको ऐसा फ़ंक्शन बनाने की आवश्यकता क्यों है जो एक म्यूटेबल लौटाता है जो वैकल्पिक रूप से एक उत्परिवर्तनीय हो सकता है, आप फ़ंक्शन पर जायेंगे? या तो यह एक उत्परिवर्तनीय बदलता है या एक नया बनाता है। आपको इसकी आवश्यकता क्यों है एक समारोह के साथ दोनों करने के लिए? और दुभाषिया को फिर से लिखने के लिए क्यों लिखा जाना चाहिए ताकि आप अपने कोड में तीन लाइनों को जोड़ने के बिना ऐसा कर सकें? " क्योंकि हम यहां दुभाषिया को कार्य परिभाषाओं और उत्थानों को संभालने के तरीके को फिर से लिखने के बारे में बात कर रहे हैं। मुश्किल से उपयोग के मामले के लिए यह बहुत कुछ करना है। - Alan Leuthard
"पायथन नौसिखियों से उम्मीद है कि यह फ़ंक्शन हमेशा एक तत्व के साथ एक सूची लौटाएगा: [5]"मैं एक पाइथन नौसिखिया हूँ, और मैं इसकी अपेक्षा नहीं करता, क्योंकि जाहिर है foo([1]) वापस होगा [1, 5], नहीं [5]। आप जो कहना चाहते थे वह यह है कि एक नौसिखिया समारोह की अपेक्षा करेगा कोई पैरामीटर के साथ बुलाया हमेशा वापस आ जाएगा [5]। - symplectomorphic
के लिये पायथन ट्यूटोरियल में उदाहरण, यही वजह है कि if L is None: जरूरत है? मैंने इस परीक्षा को हटा दिया और इससे कोई फर्क नहीं पड़ता - sdaffa23fdsf


जवाब:


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

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

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


1353
2017-07-17 21:29



उपर्युक्त उत्तर पढ़ने वाले किसी को भी, मैं दृढ़ता से अनुशंसा करता हूं कि आप लिंक किए गए Effbot आलेख को पढ़ने के लिए समय दें। साथ ही साथ अन्य सभी उपयोगी जानकारी, परिणाम कैशिंग / ज्ञापन के लिए इस भाषा सुविधा का उपयोग कैसे किया जा सकता है, इसका हिस्सा जानना बहुत आसान है! - Cam Jackson
यहां तक ​​कि यदि यह प्रथम श्रेणी की वस्तु है, तो भी कोई एक डिज़ाइन की कल्पना कर सकता है जहां कोड प्रत्येक डिफ़ॉल्ट मान ऑब्जेक्ट के साथ संग्रहीत किया जाता है और प्रत्येक बार फ़ंक्शन कहलाता है फिर से मूल्यांकन किया जाता है। मैं यह नहीं कह रहा हूं कि बेहतर होगा, केवल यह कि प्रथम श्रेणी की वस्तुएं होने वाली कार्य पूरी तरह से इसे रोक नहीं देती हैं। - gerrit
क्षमा करें, लेकिन कुछ भी "पायथन में सबसे बड़ा डब्ल्यूटीएफ" माना जाता है सबसे निश्चित रूप से एक डिजाइन दोष। यह के लिए बग का एक स्रोत है हर कोई किसी बिंदु पर, क्योंकि कोई भी उस व्यवहार को पहले की अपेक्षा नहीं करता है - जिसका अर्थ यह है कि इसे शुरू करने के लिए इस तरह से डिजाइन नहीं किया जाना चाहिए था। मुझे परवाह नहीं है कि वे किस हुप्स को कूदना चाहते थे, वे चाहिए पाइथन डिजाइन किया है ताकि डिफ़ॉल्ट तर्क गैर स्थैतिक हैं। - BlueRaja - Danny Pflughoeft
चाहे यह एक डिज़ाइन दोष है या नहीं, आपका उत्तर यह इंगित करता है कि यह व्यवहार किसी भी तरह से आवश्यक है, प्राकृतिक और स्पष्ट है कि कार्य प्रथम श्रेणी की वस्तुएं हैं, और यह मामला बस नहीं है। पाइथन बंद हो गया है। यदि आप फ़ंक्शन की पहली पंक्ति पर असाइनमेंट के साथ डिफ़ॉल्ट तर्क को प्रतिस्थापित करते हैं, तो यह प्रत्येक कॉल (संभावित रूप से एक संलग्न क्षेत्र में घोषित नामों का उपयोग करके) की अभिव्यक्ति का मूल्यांकन करता है। इस बात का कोई कारण नहीं है कि जब भी फ़ंक्शन को ठीक उसी तरह से बुलाया जाता है तो डिफ़ॉल्ट तर्कों का मूल्यांकन करना उचित नहीं होगा। - Mark Amery
डिजाइन सीधे से पालन नहीं करता है functions are objects। आपके प्रतिमान में, प्रस्ताव कार्यों के बजाय गुणों के रूप में कार्यों के डिफ़ॉल्ट मानों को कार्यान्वित करना होगा। - bukzor


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

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

def eat(food=fruits):
    ...

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

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

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

तो यदि फ़ंक्शन घोषणा के बजाए फ़ंक्शन निष्पादन पर डिफ़ॉल्ट पैरामीटर बाध्य किए गए थे तो फलों को बदल दिया गया था, यह जानने के लिए मैं आश्चर्यचकित हूं (बहुत बुरे तरीके से)। यह आपके खोज की तुलना में अधिक आश्चर्यजनक आईएमओ होगा 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 उस मूल्य के समान मूल्य का उपयोग करके जिसे उन्होंने इसे रखा है, या वह व्यक्ति जो अपनी ऑब्जेक्ट को पुनर्प्राप्त नहीं कर सकता है, भले ही वे जिस कुंजी का उपयोग कर रहे हैं वह सचमुच वही ऑब्जेक्ट है जिसका उपयोग इसे मानचित्र में रखने के लिए किया गया था (यह है वास्तव में क्यों पाइथन अपने उत्परिवर्तनीय अंतर्निहित डेटा प्रकारों को शब्दकोश कुंजी के रूप में उपयोग करने की अनुमति नहीं देता है)।

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

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


231
2017-07-15 18:11



मुझे लगता है कि यह बहस का विषय है। आप एक वैश्विक चर पर अभिनय कर रहे हैं। आपके वैश्विक चर से जुड़े आपके कोड में कहीं भी किए गए किसी भी मूल्यांकन से अब (सही) ("ब्लूबेरी", "मैंगो") का संदर्भ लेंगे। डिफ़ॉल्ट पैरामीटर किसी अन्य मामले की तरह हो सकता है। - Stefano Borini
असल में, मुझे नहीं लगता कि मैं आपके पहले उदाहरण से सहमत हूं। मुझे यकीन नहीं है कि मुझे पहले स्थान पर प्रारंभकर्ता को संशोधित करने का विचार पसंद है, लेकिन यदि मैंने किया, तो मैं उम्मीद करता हूं कि आप वास्तव में व्यवहार करें जैसा कि आप वर्णन करते हैं - डिफ़ॉल्ट मान को बदलना ("blueberries", "mangos")। - Ben Blank
डिफ़ॉल्ट पैरामीटर है किसी भी अन्य मामले की तरह। अप्रत्याशित क्या है कि पैरामीटर एक वैश्विक चर है, न कि स्थानीय। जो बदले में है क्योंकि कोड फ़ंक्शन परिभाषा पर निष्पादित किया जाता है, कॉल नहीं। एक बार जब आप इसे प्राप्त कर लेंगे, और यह कक्षाओं के लिए भी जाता है, तो यह बिल्कुल स्पष्ट है। - Lennart Regebro
मुझे उदाहरण शानदार दिखने के बजाय भ्रामक लगता है। अगर some_random_function() में जोड़ता है fruits इसे सौंपने के बजाय, व्यवहार eat()  मर्जी परिवर्तन। वर्तमान अद्भुत डिजाइन के लिए बहुत कुछ। यदि आप किसी अन्य तर्क का उपयोग करते हुए डिफ़ॉल्ट तर्क का उपयोग करते हैं और फिर फ़ंक्शन के बाहर से संदर्भ संशोधित करते हैं, तो आप परेशानी के लिए पूछ रहे हैं। असली डब्ल्यूटीएफ तब होता है जब लोग एक ताजा डिफ़ॉल्ट तर्क परिभाषित करते हैं (एक सूची शाब्दिक या एक कन्स्ट्रक्टर को कॉल), और फिर भी थोड़ा हो जाओ - alexis
आपने बस स्पष्ट रूप से घोषित किया है global और tuple फिर से सौंप दिया - अगर आश्चर्यजनक कुछ भी आश्चर्यजनक है eat उसके बाद अलग-अलग काम करता है। - user3467349


AFAICS ने अभी तक किसी का भी प्रासंगिक हिस्सा पोस्ट नहीं किया है प्रलेखन:

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


195
2017-07-10 14:50



वाक्यांश "यह आम तौर पर इरादा नहीं था" और "इसके चारों ओर एक रास्ता" गंध है जैसे वे एक डिजाइन दोष दस्तावेज कर रहे हैं। - bukzor
@ मैथ्यू: मैं अच्छी तरह से जानता हूं, लेकिन यह गड़बड़ी के लायक नहीं है। आप आम तौर पर स्टाइल गाइड और लिंटर्स को बिना किसी कारण के गलत रूप से फ़्लैग म्यूटेबल डिफ़ॉल्ट मान देखेंगे। एक ही काम करने का स्पष्ट तरीका फ़ंक्शन पर एक विशेषता को भरना है (function.data = []) या बेहतर अभी तक, एक वस्तु बनाओ। - bukzor
@ बुक्ज़ोर: संकटों को ध्यान में रखना और दस्तावेज करने की आवश्यकता है, यही कारण है कि यह सवाल अच्छा है और इतने सारे उत्थान प्राप्त हुए हैं। उसी समय, नुकसान को हटाने की आवश्यकता नहीं है। कितने पायथन शुरुआती लोगों ने उस फ़ंक्शन में एक सूची उत्तीर्ण की है जो इसे संशोधित करता है, और मूल चर में परिवर्तनों को देखने के लिए चौंक गया था? फिर भी म्यूटेबल ऑब्जेक्ट प्रकार अद्भुत हैं, जब आप समझते हैं कि उनका उपयोग कैसे करें। मुझे लगता है कि यह इस विशेष गड़बड़ी पर सिर्फ राय उबालता है। - Matthew
वाक्यांश "यह आम तौर पर इरादा नहीं था" का अर्थ है "प्रोग्रामर वास्तव में क्या नहीं करना चाहता था," नहीं "पाइथन को क्या करना चाहिए।" - holdenweb
@oriadam शायद आप इसके बारे में एक प्रश्न पोस्ट करना चाहते हो सकता है। हो सकता है कि आप इरादे से अलग कुछ करें ... - glglgl


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

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

a = []

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

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

def x(a=[]):

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

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

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

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

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

def x(static a=b):

97
2017-07-15 23:21



आप कर सकते हैं: def x (a = none): और फिर, यदि कोई नहीं है, तो एक = datetime.datetime.now सेट करें () - Anon
मुझे पता है, यह समझाने के लिए सिर्फ एक उदाहरण था कि मैं निष्पादन-समय बाध्यकारी क्यों पसंद करूंगा। - Utaal
इसके लिए शुक्रिया। मैं वास्तव में अपनी उंगली नहीं डाल सका कि यह मुझे खत्म करने के लिए क्यों परेशान करता है। आपने इसे कम से कम फ़ज़ और भ्रम के साथ खूबसूरती से किया है। जैसा कि कोई व्यक्ति सी ++ में सिस्टम प्रोग्रामिंग से आ रहा है और कभी-कभी नैतिक रूप से "अनुवाद" भाषा विशेषताओं से बात करता है, इस झूठे दोस्त ने मुझे क्लास विशेषताओं की तरह बड़े समय के सिर में नरम में लात मार दिया। मैं समझता हूं कि चीजें इस तरह क्यों हैं, लेकिन मैं मदद नहीं कर सकता लेकिन इसे नापसंद करता हूं, इससे कोई फर्क नहीं पड़ता कि इससे क्या सकारात्मक हो सकता है। कम से कम यह मेरे अनुभव के विपरीत है, कि शायद मैं (उम्मीद है) इसे कभी नहीं भूल जाऊंगा ... - AndreasT
@Andreas एक बार जब आप पाइथन का उपयोग लंबे समय तक करते हैं, तो आप यह देखने लगते हैं कि पाइथन के लिए चीजों की व्याख्या करने के लिए यह कितना तार्किक है क्योंकि क्लास इस तरह से गुण करता है - यह केवल विशेष प्रश्नों और सी ++ (और जावा, और भाषाओं जैसे भाषाओं की सीमाओं के कारण है) सी # ...) कि यह सामग्री के लिए कोई समझ में आता है class {} से संबंधित के रूप में व्याख्या करने के लिए ब्लॉक उदाहरणों:) लेकिन जब कक्षाएं प्रथम श्रेणी की वस्तुएं होती हैं, तो स्पष्ट रूप से प्राकृतिक सामग्री उनकी सामग्री (स्मृति में) के लिए उनकी सामग्री (कोड में) को प्रतिबिंबित करने के लिए होती है। - Karl Knechtel
मेरी संरचना में सामान्य संरचना कोई क्विर्क या सीमा नहीं है। मुझे पता है कि यह बेकार और बदसूरत हो सकता है, लेकिन आप इसे किसी चीज़ की "परिभाषा" कह सकते हैं। गतिशील भाषाएं मुझे अराजकतावादियों की तरह लगती हैं: निश्चित रूप से सभी स्वतंत्र हैं, लेकिन आपको किसी को कूड़े को खाली करने और सड़क को घुमाने के लिए संरचना की आवश्यकता है। मुझे लगता है कि मैं बूढ़ा हूँ ... :) - AndreasT


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

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

class BananaBunch:
    bananas = []

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

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

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

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

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


72
2017-07-15 18:54



यदि यह प्रत्येक उदाहरण के लिए अलग है तो यह वर्ग विशेषता नहीं है। क्लास गुण क्लास पर गुण हैं। इसके कारण नाम। इसलिए वे सभी मामलों के लिए समान हैं। - Lennart Regebro
वह पाइथन के व्यवहार का वर्णन नहीं मांग रहा था, वह तर्क के लिए पूछ रहा था। पायथन में कुछ भी नहीं है "यह कैसे काम करता है"; यह सब एक कारण के लिए करता है। - Glenn Maynard
और मैंने तर्क दिया। - Lennart Regebro
मैं यह नहीं कहूंगा कि यह "यह एक अच्छी शिक्षण सहायता है", क्योंकि यह नहीं है। - Geo
@ जीओ: सिवाय इसके कि यह है। यह आपको पाइथन में बहुत सी चीजों को समझने में मदद करता है। - Lennart Regebro


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

1. प्रदर्शन

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

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

2. बाध्य पैरामीटर मजबूर करना

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

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

यह उन कार्यों की एक सूची देता है जो क्रमश: 0,1,2,3 ... लौटाते हैं। यदि व्यवहार बदल जाता है, तो वे बदले में बाध्य होंगे 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=[]

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


50
2017-07-16 10:05



आप आत्मनिरीक्षण भी प्राप्त कर सकते हैं यदि प्रत्येक के लिए मूल्य के बजाय डिफ़ॉल्ट तर्क बनाने के लिए कोई फ़ंक्शन था। निरीक्षण मॉड्यूल बस उस समारोह को बुलाएगा। - yairchu
@SilentGost: मैं इस बारे में बात कर रहा हूं कि व्यवहार को फिर से बनाने के लिए बदल दिया गया था - इसे एक बार बनाना वर्तमान व्यवहार है, और क्यों म्यूटेबल डिफ़ॉल्ट समस्या मौजूद है। - Brian
@Yairchu: यह मानता है कि निर्माण इतना सुरक्षित है (यानी कोई दुष्प्रभाव नहीं है)। तर्कों का आत्मनिरीक्षण नहीं करना चाहिए कर कुछ भी, लेकिन मनमाने ढंग से कोड का मूल्यांकन करने से प्रभाव पड़ सकता है। - Brian
एक अलग भाषा डिजाइन अक्सर शब्दों को अलग-अलग लिखने का मतलब है। आपका पहला उदाहरण आसानी से लिखा जा सकता है: _expensive = महंगा (); def foo (arg = _expensive), यदि आप विशेष रूप से नहीं इसे फिर से मूल्यांकन करना चाहते हैं। - Glenn Maynard
@Glenn - यही वह है जिसे मैं "बाहरी रूप से कैश" के साथ संदर्भित कर रहा था - यह थोड़ा और वर्बोज़ है, और आप अपने नेमस्पेस में अतिरिक्त चर के साथ समाप्त होते हैं। - Brian


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

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

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

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

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

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


47
2018-03-30 11:18



मुझे यह व्यवहार खोजने में एक साल लग गया कि यह व्यवहार उत्पादन पर मेरे कोड को गड़बड़ कर रहा है, जब तक कि मैं इस डिजाइन में मौके से फंस नहीं जाता तब तक एक पूर्ण सुविधा को समाप्त कर दिया। मैं Django का उपयोग कर रहा हूँ। चूंकि स्टेजिंग पर्यावरण में कई अनुरोध नहीं थे, इसलिए इस बग को क्यूए पर कभी भी कोई प्रभाव नहीं पड़ा। जब हम लाइव गए और कई बार अनुरोध प्राप्त किए - कुछ उपयोगिता कार्यों ने एक दूसरे के पैरामीटर को ओवरराइट करना शुरू कर दिया! सुरक्षा छेद, बग और क्या नहीं बनाते हैं। - oriadam
@oriadam, कोई अपराध नहीं, लेकिन मुझे आश्चर्य है कि आपने पहले इस में बिना पाइथन को कैसे सीखा। मैं अभी पाइथन सीख रहा हूं और यह संभव नुकसान है आधिकारिक पायथन ट्यूटोरियल में उल्लेख किया गया है डिफ़ॉल्ट तर्क के पहले उल्लेख के साथ ही सही। (जैसा कि इस उत्तर के बिंदु 4 में उल्लिखित है।) मुझे लगता है कि नैतिक है - बल्कि असंवेदनशील रूप से-पढ़ने के लिए आधिकारिक दस्तावेज़ उस भाषा का जिसका उपयोग आप उत्पादन सॉफ्टवेयर बनाने के लिए करते हैं। - Wildcard
साथ ही, यह आश्चर्यजनक होगा (मेरे लिए) यदि अज्ञात जटिलता का एक कार्य जिसे मैं कॉल कर रहा हूं, के अलावा बुलाया गया था। - Vatine


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

मैं हूँ वास्तव में हैरान ने किसी ने पाइथन द्वारा दी गई अंतर्दृष्टिपूर्ण आत्मनिरीक्षण नहीं किया है (2 तथा 3 लागू) कॉलबेल पर।

एक साधारण छोटे समारोह को देखते हुए func के रूप में परिभाषित किया गया है:

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

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

तो, सूची का विस्तार कैसे किया जाता है यह जांचने के लिए पहले और बाद में कुछ आत्मनिरीक्षण करें के भीतर समारोह वस्तु। मैं उपयोग कर रहा हूँ Python 3.x इसके लिए, पायथन 2 के लिए एक ही लागू होता है (उपयोग करें __defaults__ या func_defaults पायथन 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 से बना।


43
2017-12-09 07:13



है id(...) उस अंतिम सत्यापन के लिए आवश्यक है, या होगा is ऑपरेटर एक ही सवाल का जवाब? - das-g
@ दास-जी is ठीक है, मैं बस इस्तेमाल किया id(val) क्योंकि मुझे लगता है कि यह अधिक सहज हो सकता है। - Jim Fasarakis Hilliard
@JimFasarakisHilliard मैं एक संकेत जोड़ दूंगा input। पसंद input('Did you just see me without calling me?')। यह इसे स्पष्ट इमो बनाता है। - Ev. Kounis
@ Ev.Kounis मुझे यह पसंद है! यह बात बताने के लिए धन्यवाद। - Jim Fasarakis Hilliard


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

  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 परिवर्तन - ऑपरेशन एक ही ऑब्जेक्ट पर किया जाता है - और यह मुद्रित होता है

40
2017-07-15 19:15



आपका # 4 लोगों को भ्रमित कर सकता है, क्योंकि पूर्णांक अपरिवर्तनीय हैं और इसलिए "अगर" सत्य नहीं है। उदाहरण के लिए, 0 से डी सेट के साथ, डी .__ जोड़ें __ (1) 1 वापस आ जाएगा, लेकिन डी अभी भी 0 होगा। - Anon
(वास्तव में, जोड़ना एक बुरा उदाहरण है, लेकिन पूर्णांक अभी भी अपरिवर्तनीय है मेरा मुख्य बिंदु है।) - Anon
हाँ, यह अच्छा उदाहरण नहीं था - ymv
यह देखने के बाद इसे मेरी chagrin में महसूस किया, बी के साथ सेट [], बी .__ जोड़ने __ ([1]) रिटर्न [1] देता है लेकिन यह अभी भी छोड़ देता है [] सूचियां उत्परिवर्तनीय हैं। मेरी गलती। - Anon
@ANON: वहाँ है __iadd__, लेकिन यह int के साथ काम नहीं करता है। बेशक। :-) - Veky