2015-02-01 14 views
15

के साथ पायथन सूची समझ मैं पाइथन 3.4.2 चला रहा हूं, और मैं अपने कोड के व्यवहार में उलझन में हूं। मैं बढ़ती डिग्री के साथ प्रतिदेय बहुपद कार्यों की एक सूची बनाने के लिए कोशिश कर रहा हूँ:लैम्बडास

bases = [lambda x: x**i for i in range(3)] 

लेकिन किसी कारण से यह इस करता है:

print([b(5) for b in bases]) 
# [25, 25, 25] 

क्यों bases प्रतीत होता है पिछले लैम्ब्डा अभिव्यक्ति की एक सूची है, सूची समझ में, दोहराया?

+6

आप इस सहायक हो सकते हैं: [पकड़ लिया: अजगर, scoping, और संवृति] (http://eev.ee/blog/2011/04/24/gotcha-python-scoping-closures/) – unutbu

+0

unutbu : इसे कैसे काम करना है इस पर कोई विचार? – L3viathan

+0

शायद आप जो चाहते हैं वह है 'बेस = लैम्ब्डा x: [x ** मैं रेंज में (3)] '? – user3467349

उत्तर

16

समस्या, जो classic "gotcha" है, है कि i लैम्ब्डा कार्यों में संदर्भित not looked up until the lambda function is called है। उस समय, i का मान अंतिम मूल्य है for-loop समाप्त होने पर बाध्य था, यानी 2

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

इस प्रकार, जब लैम्ब्डा कहा जाता है, i अब स्थानीय गुंजाइश में देखा जाता है, और उसके डिफ़ॉल्ट मान प्रयोग किया जाता है:

In [177]: bases = [lambda x, i=i: x**i for i in range(3)] 

In [178]: print([b(5) for b in bases]) 
[1, 5, 25] 

संदर्भ के लिए:

+2

नीट (+1)। यह आश्चर्यजनक होगा अगर उत्तर समझाया गया कि मूल कोड क्यों काम नहीं करता है, और यह क्यों करता है। धन्यवाद। – NPE

+0

क्या इस तरह के कोड की व्याख्या करने के लिए अजगर का कोई अच्छा कारण है? यह बदले में है ... – user3467349

+0

@ user3467349: ठीक है, अभी के लिए, पायथन [गतिशील नाम संकल्प] (http://en.wikipedia.org/wiki/Name_resolution#Static_versus_dynamic) का उपयोग करता है। लेकिन [दस्तावेज़ राज्य] (https://docs.python.org/3/tutorial/classes.html), "भाषा परिभाषा स्थिर नाम समाधान के प्रति विकसित हो रही है", इसलिए शायद भविष्य में नियम बदल जाएंगे। – unutbu

2

वैकल्पिक समाधान के रूप में, आप आंशिक फ़ंक्शन का उपयोग कर सकते हैं:

>>> bases = [(lambda i: lambda x: x**i)(i) for i in range(3)] 
>>> print([b(5) for b in bases]) 
[1, 5, 25] 

@unutbu द्वारा दिए गए क्लासिक समाधान से अधिक है कि निर्माण के केवल लाभ यह है कि जिस तरह से है, तो आप तर्कों की गलत संख्या के साथ अपने कार्यप्रणाली को कॉल करके डरपोक बग प्रस्तुत नहीं कर सकते हैं:

>>> print([b(5, 8) for b in bases]) 
#    ^^^ 
#    oups 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 1, in <listcomp> 
TypeError: <lambda>() takes 1 positional argument but 2 were given 

एक टिप्पणी bellow में एडम स्मिथ ने सुझाव दिया, "नेस्ट लैम्ब्डा" उपयोग करने के बजाय आप एक ही लाभ के साथ functools.partial इस्तेमाल कर सकते हैं के रूप में:

>>> import functools 
>>> bases = [functools.partial(lambda i,x: x**i,i) for i in range(3)] 
>>> print([b(5) for b in bases]) 
[1, 5, 25] 
>>> print([b(5, 8) for b in bases]) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 1, in <listcomp> 
TypeError: <lambda>() takes 2 positional arguments but 3 were given 
+2

यदि आप आंशिक कार्यों का निर्माण करने जा रहे हैं, तो आपको 'functools.partial' का उपयोग करना चाहिए! –

+0

@ एडम हां। तुम सही हो। मैंने तदनुसार अपना जवाब संपादित कर लिया है। 'आंशिक' का उपयोग करते समय मुझे परेशान करने वाली एक बात यह है कि त्रुटि संदेश आवश्यक स्थितित्मक तर्कों की "गलत" संख्या रिपोर्ट करता है। लेकिन यह एक वाक्यविन्यास के लिए एक मामूली गेटचा है जो समझने के लिए और अधिक स्पष्ट है। –

4

एक अधिक 'pythonic' दृष्टिकोण:
नेस्टेड कार्यों का उपयोग कर:

def polyGen(degree): 
    def degPolynom(n): 
     return n**degree 
    return degPolynom 

polynoms = [polyGen(i) for i in range(5)] 
[pol(5) for pol in polynoms] 

उत्पादन:

>> [1, 5, 25, 125, 625]

1

मैं नहीं था एनके "सवाल क्यों होता है" प्रश्न के पहलू का अभी तक जवाब दिया गया है।

किसी फ़ंक्शन में गैर-स्थानीय नामों का नाम स्थिरता नहीं माना जाता है ताकि ये गैर-स्थानीय नाम वैश्विक नामों के व्यवहार से मेल खाते हों। फ़ंक्शन के बाद किसी वैश्विक नाम में परिवर्तन किए जाने पर फ़ंक्शन को कॉल किया जाता है।

उदाहरण के लिए।

# global context 
n = 1 
def f(): 
    return n 
n = 2 
assert f() == 2 

# non-local context 
def f(): 
    n = 1 
    def g(): 
     return n 
    n = 2 
    assert g() == 2 
    return g 
assert f()() == 2 

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

उदाहरण के लिए।

def create_constant_getter(constant): 
    def constant_getter(): 
     return constant 
    return constant_getter 

getters = [create_constant_getter(n) for n in range(5)] 
constants = [f() for f in getters] 
assert constants == [0, 1, 2, 3, 4] 

अंत में, परिशिष्ट के रूप में, कार्य गैर स्थानीय नाम (यदि नाम इस तरह के रूप में चिह्नित किया जाता है) को संशोधित बस के रूप में वे वैश्विक नाम संशोधित कर सकते हैं कर सकते हैं। जैसे।

def f(): 
    n = 0 
    def increment(): 
     nonlocal n 
     n += 1 
     return n 
    return increment 
g = f() 
assert g() + 1 == g() 
+0

मुझे 'nonlocal' के बारे में बताने के लिए अच्छा स्पष्टीकरण और धन्यवाद! – Jasper

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