2011-09-09 28 views
16

पायथन 2.6 में:अजीब व्यवहार: सूची समझ के अंदर Lambda

[x() for x in [lambda: m for m in [1,2,3]]] 

परिणाम:

[3, 3, 3] 

मैं आउटपुट [1, 2, 3] होने की अपेक्षा करता हूं। मुझे एक गैर सूची समझ दृष्टिकोण के साथ भी वही समस्या मिलती है। और मैं एम को एक अलग चर में कॉपी करने के बाद भी।

मुझे क्या याद आ रही है?

+0

... लेकिन यह इटरेटर के साथ काम करता है। >>> l = (lambda: m के लिए m [1,2,3 ]) >>> [x() x में x के लिए] – GeneralBecos

+0

ऐसा इसलिए है क्योंकि जेनरेटर एक बार में अपने मूल्यों को नहीं बनाता है, जब अनुरोध किया जाता है तो यह उन्हें बनाता है। एक सूची समझ और जनरेटर अभिव्यक्ति समान नहीं है, हालांकि उन्हें अक्सर एक दूसरे के लिए उपयोग किया जा सकता है। ऐसी स्थितियां हैं (इस तरह की) जहां व्यवहार _ignignantly_ अलग है। –

+0

क्यों 'x()' सिर्फ 'x' नहीं ?? यह अलग कैसे है ?? – amyassin

उत्तर

14

lambdas m का मूल्य याद करने के लिए, आप एक डिफ़ॉल्ट मान के साथ एक बहस इस्तेमाल कर सकते हैं:

[x() for x in [lambda m=m: m for m in [1,2,3]]] 
# [1, 2, 3] 

यह काम करता है क्योंकि मूलभूत मूल्यों परिभाषा समय में एक बार स्थापित कर रहे हैं,। प्रत्येक लैम्ब्डा अब m का अपना डिफ़ॉल्ट मान लैम्ब्डा निष्पादन समय पर बाहरी दायरे में m के मान को देखने के बजाय उपयोग करता है।

+0

बहुत बढ़िया! लैम्ब्डा को डिफ़ॉल्ट मान निर्धारित करने के बारे में नहीं सोचा था। महान पद। –

-1

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

इसे आज़माएं और तत्वों की आईडी() को जांचें।

[नोट: यह उत्तर सही नहीं है; टिप्पणियां देखें]

+0

लैम्ब्डा एक बार नहीं बनाया गया है। '[लैम्ब्डा: मी के लिए मी [1,2,3]] 'तीन अलग भेड़ का बच्चा पैदा करेगा। –

+0

हाँ, उसने देखा। क्षमा करें :) आईडी की शुरुआत और अंत मेरे लिए समान दिखता है ... – Gerenuk

5

लंबी कहानी छोटी, आप यह नहीं करना चाहते हैं। अधिक विशेष रूप से, आप जो सामना कर रहे हैं वह संचालन समस्या का एक आदेश है। आप तीन अलग lambda बना रहे हैं कि सभी m लौटते हैं, लेकिन उनमें से कोई भी तुरंत नहीं कहा जाता है। फिर, जब आप बाहरी सूची समझ में आते हैं और उन्हें m का अवशिष्ट मूल्य कहा जाता है, तो आंतरिक सूची समझ का अंतिम मूल्य होता है।

- टिप्पणियों के लिए -

>>> [lambda: m for m in range(3)] 
[<function <lambda> at 0x021EA230>, <function <lambda> at 0x021EA1F0>, <function <lambda> at 0x021EA270>] 

उन तीन अलग-अलग lambdas हैं।

और, आगे सबूत के रूप में:

>>> [id(m) for m in [lambda: m for m in range(3)]] 
[35563248, 35563184, 35563312] 

फिर, तीन अलग-अलग आईडी।

+0

लैम्ब्डा के आईडी() की जांच करें। वे सभी एक जैसे हैं। – Gerenuk

+0

तो इसका मतलब है कि लैम्ब्डा एम के मूल्य के बजाय एम के संदर्भ में है। क्या लैम्ब्डा को चर के मान को पकड़ना संभव है? धन्यवाद! – GeneralBecos

+0

@GeneralBecos - ऐसा नहीं है कि मुझे पता है, नहीं, हालांकि अगर कोई और किसी तरीके से जानता है और इसे एक अतिरिक्त टिप्पणी के रूप में जोड़ सकता है तो मुझे इसका ध्यान रखने में खुशी होगी। जब तक यह आपके अंतिम परिणाम को नुकसान पहुंचाता है, तब तक जनरेटर के साथ आपका दृष्टिकोण उचित नहीं होता है, और यह अपेक्षित आउटपुट उत्पन्न करेगा: x [x() x में (lambda: m के लिए m [1,2,3] में]] ' –

6

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

inner_list = [] 
for m in [1, 2, 3]: 
    def Lambda(): 
     return m 
    inner_list.append(Lambda) 

तो, इस बिंदु पर, inner_list उस में तीन कार्य करता है, और प्रत्येक समारोह, जब कहा जाता है, m के परिणाम प्रदान करेंगे। लेकिन मुख्य बिंदु यह है कि वे सभी m को देखते हैं, भले ही m बदल रहा है, फिर भी वे इसे बाद में कभी नहीं देख पाएंगे।

outer_list = [] 
for x in inner_list: 
    outer_list.append(x()) 

विशेष रूप से, के बाद से आंतरिक सूची पूरी तरह से पहले बाहरी सूची का निर्माण किया जा रहा शुरू होता है का निर्माण किया है, m पहले से ही 3 के अपने अंतिम मान तक पहुँच गया है, और तीनों कार्य एक ही मूल्य है कि देखते हैं।

3

कार्यों के __closure__ पर देखें। एक ही सेल वस्तु है, जो बाहरी दायरे से मीटर के लिए एक संदर्भ रहता है के लिए सभी 3 बिंदु:

>>> print(*[x.__closure__[0] for x in [lambda: m for m in [1,2,3]]], sep='\n') 
<cell at 0x00D17610: int object at 0x1E2139A8> 
<cell at 0x00D17610: int object at 0x1E2139A8> 
<cell at 0x00D17610: int object at 0x1E2139A8> 

आप अपने कार्यों एक कीवर्ड तर्क के रूप में मीटर लेने के लिए, unubtu के जवाब के अनुसार नहीं करना चाहते हैं आप कर सकते थे, इसके बजाय प्रत्येक पुनरावृत्ति पर एम का मूल्यांकन करने के लिए एक अतिरिक्त लैम्ब्डा का उपयोग करें:

>>> [x() for x in [(lambda x: lambda: x)(m) for m in [1,2,3]]] 
[1, 2, 3] 
0

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

>>> [ (lambda a:a)(i) for i in range(3)] 
[0, 1, 2] 
>>> 

यह भी तेज़ है।

>>> timeit.timeit('[(lambda a:a)(i) for i in range(10000)]',number=10000) 
9.231263160705566 
>>> timeit.timeit('[lambda a=i:a for i in range(10000)]',number=10000) 
11.117988109588623 
>>> 

लेकिन नहीं के रूप में तेजी से नक्शे के रूप में:

>>> timeit.timeit('map(lambda a:a, range(10000))',number=10000) 
5.746963977813721 

(मैं, इन परीक्षणों एक बार से अधिक भाग गया परिणाम एक ही था, इस अजगर 2.7 में किया गया था, परिणाम अजगर 3 में अलग हैं: दो सूची की समझ प्रदर्शन में बहुत करीब है और बहुत धीमी है, नक्शा बहुत तेज़ रहता है।)

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