9

निम्न परीक्षण में विफल रहता है:नक्शा() और सूची समझ का परिणाम अलग क्यों हैं?

#!/usr/bin/env python 
def f(*args): 
    """ 
    >>> t = 1, -1 
    >>> f(*map(lambda i: lambda: i, t)) 
    [1, -1] 
    >>> f(*(lambda: i for i in t)) # -> [-1, -1] 
    [1, -1] 
    >>> f(*[lambda: i for i in t]) # -> [-1, -1] 
    [1, -1] 
    """ 
    alist = [a() for a in args] 
    print(alist) 

if __name__ == '__main__': 
    import doctest; doctest.testmod() 

दूसरे शब्दों में:

>>> t = 1, -1 
>>> args = [] 
>>> for i in t: 
... args.append(lambda: i) 
... 
>>> map(lambda a: a(), args) 
[-1, -1] 
>>> args = [] 
>>> for i in t: 
... args.append((lambda i: lambda: i)(i)) 
... 
>>> map(lambda a: a(), args) 
[1, -1] 
>>> args = [] 
>>> for i in t: 
... args.append(lambda i=i: i) 
... 
>>> map(lambda a: a(), args) 
[1, -1] 
+3

मेरे जैसे लोगों ने प्रश्न पढ़ा लेकिन पहले किसी भी समस्या का ध्यान न दें: ध्यान दें ' 1, -1] '! अनिवार्य रूप से 'lambda i: ...' एक लूप में i के वर्तमान मूल्य को कैप्चर नहीं करता है। पाइथन से संबंधित –

+0

अक्सर पूछे जाने वाले प्रश्न: [विभिन्न मूल्यों के साथ लूप में परिभाषित लैम्बडास एक ही परिणाम लौटाते हैं?] (Https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined -इन-ए-लूप-साथ-अलग-मूल्य-सभी-वापसी-एक-परिणाम) – jfs

उत्तर

9

वे अलग हैं, क्योंकि दोनों जनरेटर अभिव्यक्ति में i का मूल्य और सूची कंप का आलस्य मूल्यांकन किया जाता है, यानी जब अज्ञात कार्यों को f में बुलाया जाता है।
उस समय तक, i अंतिम मूल्य से जुड़ा हुआ है यदि t, जो -1 है।

तो बुनियादी तौर पर, यह क्या सूची समझ करता है (वैसे ही genexp के लिए) है:

x = [] 
i = 1 # 1. from t 
x.append(lambda: i) 
i = -1 # 2. from t 
x.append(lambda: i) 

अब lambdas को बंद करने का संदर्भ देता है i चारों ओर ले जाने के लिए, लेकिन i दोनों ही मामलों में -1 के लिए बाध्य है, क्योंकि यह आखिरी मूल्य है जिसे इसे सौंपा गया था।

आप यह सुनिश्चित करें कि लैम्ब्डा i के वर्तमान मूल्य प्राप्त करता है बनाना चाहते हैं,

f(*[lambda u=i: u for i in t]) 

इस तरह, आप समय बंद बनाई गई है पर i के मूल्यांकन के लिए मजबूर करते हैं।

संपादित करें: जनरेटर अभिव्यक्तियों और सूची समझों के बीच एक अंतर है: बाद वाले लूप चरम को आसपास के दायरे में रिसाव करें।

+0

लैम्बडास बुरा हैं क्योंकि यह स्पष्ट नहीं है कि रनटाइम संदर्भ वास्तव में क्या है। –

+4

@ एसएलओटी: पायथन में सामान्य कार्य अलग नहीं हैं। 'def f(): वापसी i' आप नहीं जानते कि मैं वास्तव में क्या काम करता हूं या लैम्ब्डा माना जाता है। – jfs

+1

पायथन 3 में, ["लूप नियंत्रण चर अब आसपास के दायरे में नहीं लीक हैं।"] (Https://docs.python.org/3.0/whatsnew/3.0.html#changed-syntax) – unutbu

5

लैम्ब्डा चर, मान नहीं है, इसलिए कोड कब्जा

lambda : i 

हमेशा मान प्रदान करेंगे मैं है वर्तमान में बंद होने के लिए बाध्य है। जब तक इसे बुलाया जाता है, यह मान -1 पर सेट किया गया है।

आप क्या चाहते हैं पाने के लिए आपको द्वारा, वास्तविक बाध्यकारी कब्जा करने के लिए समय लैम्ब्डा बनाई गई है पर की आवश्यकता होगी:

>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1] 
[1, -1] 
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1] 
[1, -1] 
3

अभिव्यक्ति f = lambda: i के बराबर है:

def f(): 
    return i 

अभिव्यक्ति g = lambda i=i: i के बराबर है:

def g(i=i): 
    return i 

i पहले मामले में एक free variable है और यह दूसरे मामले में समारोह पैरामीटर के लिए बाध्य है यानी, यह उस मामले में एक स्थानीय चर है। फ़ंक्शन परिभाषा के समय डिफ़ॉल्ट पैरामीटर के मानों का मूल्यांकन किया जाता है।

जेनरेटर अभिव्यक्ति, निकटतम संलग्न गुंजाइश lambda अभिव्यक्ति में i नाम के लिए (जहां i परिभाषित किया गया है) है इसलिए i कि ब्लॉक में हल हो गई है:

f(*(lambda: i for i in (1, -1)) # -> [-1, -1] 

ilambda i: ... ब्लॉक की एक स्थानीय चर रहा है, इसलिए जिस ऑब्जेक्ट को संदर्भित किया गया है उसे उस ब्लॉक में परिभाषित किया गया है:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1] 
संबंधित मुद्दे