लैम्ब्डा कार्यों और उनके मापदंडों का दायरा?


89

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

तो मेरे पास निम्न सरलीकृत कोड है:

def callback(msg):
    print msg

#creating a list of function handles with an iterator
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(lambda: callback(m))
for f in funcList:
    f()

#create one at a time
funcList=[]
funcList.append(lambda: callback('do'))
funcList.append(lambda: callback('re'))
funcList.append(lambda: callback('mi'))
for f in funcList:
    f()

इस कोड का आउटपुट है:

mi
mi
mi
do
re
mi

मैंने उम्मीद की:

do
re
mi
do
re
mi

क्यों एक iterator गड़बड़ चीजों का उपयोग कर रहा है?

मैंने एक डीपस्कोपी का उपयोग करने की कोशिश की है:

import copy
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(lambda: callback(copy.deepcopy(m)))
for f in funcList:
    f()

लेकिन इसमें भी यही समस्या है।


3
आपका प्रश्न शीर्षक कुछ भ्रामक है।
lispmachine

1
यदि आप उन्हें भ्रामक पाते हैं तो लंबोदर का उपयोग क्यों करें? फ़ंक्शंस को परिभाषित करने के लिए def का उपयोग क्यों नहीं किया जाता है? यह आपकी समस्या के बारे में क्या है जो लंबोदर को इतना महत्वपूर्ण बनाता है?
S.Lott

@ S.Lott नेस्टेड फ़ंक्शन के परिणामस्वरूप एक ही समस्या होगी (शायद अधिक स्पष्ट रूप से दिखाई दे)
lispmachine

1
@agartland: क्या तुम मेरे हो? मैं भी GUI घटनाओं पर काम कर रहा था, और मैंने पृष्ठभूमि अनुसंधान के दौरान इस पृष्ठ को खोजने से पहले निम्नलिखित लगभग समान परीक्षण लिखा: pastebin.com/M5jjHjFT
imallett

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

जवाबों:


79

यहां समस्या mचर (एक संदर्भ) है जो आसपास के दायरे से ली जा रही है। लैम्बडा के दायरे में केवल पैरामीटर होते हैं।

इसे हल करने के लिए आपको लैम्ब्डा के लिए एक और गुंजाइश बनानी होगी:

def callback(msg):
    print msg

def callback_factory(m):
    return lambda: callback(m)

funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(callback_factory(m))
for f in funcList:
    f()

ऊपर दिए गए उदाहरण में, लैम्ब्डा भी खोजने के लिए अधिशेष गुंजाइश का उपयोग करता है m, लेकिन इस बार यह callback_factoryगुंजाइश है जो हर callback_factory कॉल के अनुसार एक बार बनाई जाती है ।

या फंक्शनलूलिस्पार्टियल के साथ :

from functools import partial

def callback(msg):
    print msg

funcList=[partial(callback, m) for m in ('do', 're', 'mi')]
for f in funcList:
    f()

2
यह स्पष्टीकरण थोड़ा भ्रामक है। समस्या पुनरावृत्ति में मी के मूल्य का परिवर्तन है, न कि गुंजाइश।
Ixx

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

132

जब एक लैम्ब्डा बनाया जाता है, तो यह एनक्लोजिंग स्कोप में वैरिएबल की एक प्रति नहीं बनाता है जो इसका उपयोग करता है। यह पर्यावरण के लिए एक संदर्भ रखता है ताकि यह बाद में चर के मूल्य को देख सके। बस एक है m। यह लूप के माध्यम से हर बार सौंपा जाता है। लूप के बाद, चर mका मूल्य है 'mi'। इसलिए जब आप वास्तव में आपके द्वारा बाद में बनाए गए फ़ंक्शन को चलाते हैं, तो यह mउस वातावरण के मूल्य को देखेगा जिसने इसे बनाया है, जो तब तक मान होगा 'mi'

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

for m in ('do', 're', 'mi'):
    funcList.append(lambda m=m: callback(m))

मेरा मतलब है कि डिफ़ॉल्ट मान के साथ वैकल्पिक पैरामीटर
lispmachine

6
अच्छा समाधान! हालांकि मुश्किल, मुझे लगता है कि मूल अर्थ अन्य वाक्य रचनाओं की तुलना में स्पष्ट है।
क्वांटम 7

3
इस बारे में हैकिश या मुश्किल कुछ भी नहीं है; यह बिल्कुल वैसा ही उपाय है जैसा कि आधिकारिक पायथन एफएक्यू सुझाव देता है। देखें यहाँ
अबार्नेट

3
@abernert, "Hackish and tricky" जरूरी नहीं कि "आधिकारिक पायथन एफएक्यू सुझाव देता है" समाधान के साथ असंगत हो। संदर्भ के लिए धन्यवाद।
डॉन हैच

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

6

पायथन पाठ्यक्रम के संदर्भों का उपयोग करता है, लेकिन इस संदर्भ में कोई फर्क नहीं पड़ता।

जब आप लैम्बडा को परिभाषित करते हैं (या एक फ़ंक्शन, चूंकि यह ठीक वैसा ही व्यवहार है), यह रनटाइम के बाद लैम्ब्डा अभिव्यक्ति का मूल्यांकन नहीं करता है:

# defining that function is perfectly fine
def broken():
    print undefined_var

broken() # but calling it will raise a NameError

अपने लैम्ब्डा उदाहरण से भी अधिक आश्चर्यजनक:

i = 'bar'
def foo():
    print i

foo() # bar

i = 'banana'

foo() # you would expect 'bar' here? well it prints 'banana'

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

जब यह मेमने के निष्पादन में मी की तलाश करता है, तो मी को सबसे ऊपरी दायरे से लिया जाता है, जिसका अर्थ है कि, जैसा कि अन्य ने बताया; आप एक और गुंजाइश जोड़कर उस समस्या को दरकिनार कर सकते हैं:

def factory(x):
    return lambda: callback(x)

for m in ('do', 're', 'mi'):
    funcList.append(factory(m))

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

एक नोट के रूप में, मैं कारखाने को फैक्ट्री (एम) के रूप में परिभाषित कर सकता था [एम द्वारा एक्स बदलें], व्यवहार समान है। मैंने स्पष्टता के लिए एक अलग नाम का उपयोग किया :)

आप पा सकते हैं कि बोमर बाउर को लैम्ब्डा जैसी समस्याएं मिलीं। उस ब्लॉग पर क्या दिलचस्प है टिप्पणी है, जहां आप अजगर बंद होने के बारे में अधिक जानेंगे :)


1

सीधे हाथ में इस मुद्दे से संबंधित नहीं है, लेकिन ज्ञान का एक अमूल्य टुकड़ा फिर भी: फ्रेड्रिक लुंड द्वारा पायथन ऑब्जेक्ट्स


1
सीधे आपके उत्तर से संबंधित नहीं है, लेकिन बिल्ली के बच्चे की खोज: google.com/search?q=kitten
Singletoned

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

1

हां, यह गुंजाइश की समस्या है, यह बाहरी मीटर से बांधता है, चाहे आप लंबोदा या स्थानीय फ़ंक्शन का उपयोग कर रहे हों। इसके बजाय, एक फ़नकार का उपयोग करें:

class Func1(object):
    def __init__(self, callback, message):
        self.callback = callback
        self.message = message
    def __call__(self):
        return self.callback(self.message)
funcList.append(Func1(callback, m))

1

लैम्बडा के लिए सोल्यूटन अधिक लैम्ब्डा है

In [0]: funcs = [(lambda j: (lambda: j))(i) for i in ('do', 're', 'mi')]

In [1]: funcs
Out[1]: 
[<function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>]

In [2]: [f() for f in funcs]
Out[2]: ['do', 're', 'mi']

बाहरी lambdaका उपयोग वर्तमान मान को बांधने के iलिए किया j जाता है

हर बार बाहरी lambdaयह कहा जाता है आंतरिक का एक उदाहरण बना देता है lambdaके साथ jके वर्तमान मूल्य के लिए बाध्य iके रूप में iके मूल्य


0

पहला, जो आप देख रहे हैं वह कोई समस्या नहीं है, और कॉल-बाय-रेफरेंस या बाय-वैल्यू से संबंधित नहीं है।

आपके द्वारा निर्धारित लैम्ब्डा सिंटैक्स में कोई पैरामीटर नहीं है, और जैसे कि, आप जो पैरामीटर देख रहे हैं वह पैरामीटर है m वह लैम्ब्डा फ़ंक्शन के लिए बाहरी है। यही कारण है कि आप इन परिणामों को देख रहे हैं।

लैम्ब्डा सिंटैक्स, आपके उदाहरण में आवश्यक नहीं है, और आप एक साधारण फ़ंक्शन कॉल का उपयोग कर रहे हैं:

for m in ('do', 're', 'mi'):
    callback(m)

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

एक साइड नोट के रूप में, पैरामीटर पासिंग के बारे में। अजगर में पैरामीटर हमेशा वस्तुओं के संदर्भ में होते हैं। एलेक्स मार्टेली को उद्धृत करने के लिए:

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


0

वैरिएबल mको कैप्चर किया जा रहा है, इसलिए आपका लैम्ब्डा एक्सप्रेशन हमेशा "चालू" मान को देखता है।

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

def callback(msg):
    print msg

def createCallback(msg):
    return lambda: callback(msg)

#creating a list of function handles with an iterator
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(createCallback(m))
for f in funcList:
    f()

आउटपुट:

do
re
mi

0

पायथन में क्लासिक अर्थों में वास्तव में कोई चर नहीं हैं, बस नाम जो लागू वस्तु के संदर्भों से बंधे हैं। यहां तक ​​कि कार्य पायथन में किसी प्रकार की वस्तु हैं, और लैम्ब्डा नियम का अपवाद नहीं बनाते हैं :)


जब आप कहते हैं "क्लासिक अर्थ में," आपका मतलब है, "जैसे सी है।" पाइथन सहित बहुत सी भाषाएं, चर को अलग करती हैं। सी। से अलग
नेड बैचेल्ड डे

0

अलग नोट के रूप में, map , हालांकि कुछ प्रसिद्ध पायथन फिगर द्वारा तिरस्कृत किया गया, एक निर्माण को मजबूर करता है जो इस नुकसान को रोकता है।

fs = map (lambda i: lambda: callback (i), ['do', 're', 'mi'])

NB: lambda iअन्य उत्तरों में कारखाने की तरह पहला कार्य करता है।

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