सूची बोध में लैम्ब्डा फ़ंक्शन


149

निम्न दो सूची के उत्पादन में भिन्नता क्यों है, भले ही fऔर lambdaफ़ंक्शन समान हों?

f = lambda x: x*x
[f(x) for x in range(10)]

तथा

[lambda x: x*x for x in range(10)]

आप दोनों का ध्यान रखें type(f)और type(lambda x: x*x)एक ही प्रकार से लौटें।


[lambda x: x*x for x in range(10)]पहले वाले की तुलना में तेज है, क्योंकि यह बार-बार बाहरी लूप फ़ंक्शन को कॉल नहीं करता है।
रिज़ा

@Selinap: ... नहीं, इसके बजाय आप लूप के माध्यम से हर बार एक नया स्पैंकिंग ब्रांड बना रहे हैं। ... और इस नए फ़ंक्शन को बनाने का ओवरहेड, फिर कॉलिंग थोड़ा धीमा है (वैसे भी मेरे सिस्टम पर)।
गेरेट

@ गुरट: ओवरहेड के साथ, यह अभी भी तेज है। लेकिन, निश्चित रूप [x*x for x in range(10)]से बेहतर है।
रिज़ा

34
मैंने अभी-अभी google फ़ॉबर एक्सेस प्राप्त करने के लिए यहाँ प्रवेश किया :)
Gal Margalit

जवाबों:


267

पहला एक एकल लंबो फ़ंक्शन बनाता है और इसे दस बार कॉल करता है।

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

[(lambda x: x*x)(x) for x in range(10)]

या बेहतर अभी तक:

[x*x for x in range(10)]

13
या map(lambda x: x*x, range(10)), जो शायद पहली बार में ओपी का मतलब था।
डैनियल रोज़मैन

हाँ, लैम्ब्डा x: x * x .. (x) टेनेट लगता है।
स्टेटिक

[लैम्ब्डा x: x * x फॉर x इन रेंज (10)] मूल रूप से हैसेल में एक फंक्शनल है
मोशे बीरी

@DanielRoseman, या सटीक होने के list(map(lambda x: x*x, range(10)))लिए आपको दे देंगे[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
rrlamichhane

108

यह प्रश्न "प्रसिद्ध" और "स्पष्ट" पायथन सिंटैक्स के बहुत ही बदबूदार हिस्से को छूता है - क्या पूर्वता, लंबोदर, या सूची समझ के लिए।

मुझे नहीं लगता कि ओपी का उद्देश्य 0 से 9 तक वर्गों की एक सूची तैयार करना था। यदि ऐसा होता तो हम इसे और अधिक दे सकते थे:

squares = []
for x in range(10): squares.append(x*x)
  • यह अनिवार्य वाक्यविन्यास का अच्छा ol 'तरीका है।

लेकिन बात यह नहीं है। बिंदु डब्ल्यू (हाई) टीएफ है यह अस्पष्ट अभिव्यक्ति इतनी जवाबी है? और मेरे पास अंत में आपके लिए एक मूर्खतापूर्ण मामला है, इसलिए मेरे उत्तर को बहुत जल्दी खारिज न करें (मैंने इसे नौकरी के लिए इंटरव्यू दिया था)।

इसलिए, ओपी की समझदारी ने लंबोदर की सूची लौटा दी:

[(lambda x: x*x) for x in range(10)]

यह निश्चित रूप से स्क्वेरिंग फ़ंक्शन की 10 अलग-अलग प्रतियां हैं, देखें:

>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]

मेमनों के स्मृति पतों पर ध्यान दें - वे सभी अलग हैं!

आप निश्चित रूप से इस अभिव्यक्ति का एक और "इष्टतम" (हाहा) संस्करण हो सकते हैं:

>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]

देख? 3 बार वही लंबोदर।

कृपया ध्यान दें, कि मैंने चर के _रूप में उपयोग किया है for। यह कोई लेना देना नहीं है xमें lambda(यह lexically भारी पड़ जाता है!)। उसे ले लो?

मैं चर्चा छोड़ रहा हूं, वाक्यविन्यास पूर्वता ऐसा क्यों नहीं है, इसका मतलब यह है कि:

[lambda x: (x*x for x in range(10))]

जो हो सकता है: [[0, 1, 4, ..., 81]]या [(0, 1, 4, ..., 81)], या जो मुझे सबसे तार्किक लगता है , यह list1 तत्व का होगा - generatorमूल्यों को लौटाने वाला। यह सिर्फ मामला नहीं है, भाषा इस तरह से काम नहीं करती है।

लेकिन क्या, अगर ...

क्या होगा यदि आप forचर का निरीक्षण नहीं करते हैं , और इसे अपने lambdas में उपयोग करते हैं ???

ठीक है, फिर बकवास होता है। इसे देखो:

[lambda x: x * i for i in range(4)]

बेशक इसका मतलब है:

[(lambda x: x * i) for i in range(4)]

लेकिन इसका मतलब यह नहीं है:

[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]

यह सिर्फ पागल है!

सूची की समझ के लंबोदर इस समझ के दायरे के करीब हैं। एक शाब्दिक बंद, इसलिए वे संदर्भ के iमाध्यम से संदर्भित करते हैं, और जब उनका मूल्यांकन किया गया था, तो इसका मूल्य नहीं!

तो, यह अभिव्यक्ति:

[(lambda x: x * i) for i in range(4)]

लगभग बराबर है:

[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]

मुझे यकीन है कि हम एक अजगर डिकम्पॉइलर (जिसके द्वारा मेरा मतलब है जैसे disमॉड्यूल) का उपयोग करके अधिक देख सकते हैं , लेकिन पायथन-वीएम-अज्ञेय चर्चा के लिए यह पर्याप्त है। नौकरी के लिए इंटरव्यू के लिए बहुत कुछ।

अब, listमल्टीपलियर लैम्ब्डा कैसे बनाएं , जो वास्तव में लगातार पूर्णांक से गुणा करें? ठीक है, स्वीकृत उत्तर के समान, हमें सीधे टाई को iदूसरे में लपेटकर तोड़ने की जरूरत है lambda, जिसे सूची समझ अभिव्यक्ति के अंदर कहा जा रहा है :

इससे पहले:

>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2

उपरांत:

>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1

(मेरे पास बाहरी लैम्ब्डा चर भी था = i, लेकिन मैंने तय किया कि यह स्पष्ट समाधान है - मैंने पेश किया yताकि हम सभी देख सकें कि कौन सा चुड़ैल है)।

2019-08-30 संपादित करें:

@Josoler द्वारा एक सुझाव के बाद, जो @sheridp द्वारा उत्तर में भी मौजूद है - सूची समझ "लूप वेरिएबल" का मान किसी ऑब्जेक्ट के अंदर "एम्बेडेड" किया जा सकता है - कुंजी इसके लिए सही समय पर एक्सेस करना है। ऊपर "बाद" अनुभाग इसे दूसरे में लपेटकर lambdaऔर वर्तमान मूल्य के साथ तुरंत कॉल करता है i। एक और तरीका (पढ़ने में थोड़ा आसान है - यह कोई 'वाट' प्रभाव पैदा नहीं करता है) iकिसी partialवस्तु के मूल्य को संग्रहीत करना है , और "आंतरिक" (मूल) lambdaको एक तर्क के रूप में लेना है ( partialवस्तु द्वारा आपूर्ति की गई पास) कॉल का समय), अर्थात:

2 के बाद:

>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)

महान, लेकिन आपके लिए अभी भी थोड़ा मोड़ है! मान लें कि हम कोड रीडर पर इसे आसान नहीं बनाना चाहते हैं, और नाम से कारक को पास करें (एक कीवर्ड तर्क के रूप में partial)। आइए कुछ नाम बदलें:

2.5 के बाद:

>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'

WAT?

>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'

रुको ... हम 1 से तर्क की संख्या बदल रहे हैं, और "बहुत से" से "बहुत कम" तक जा रहे हैं?

ठीक है, यह एक वास्तविक वॉट नहीं है, जब हम इस तरह coefसे गुजरते हैं partial, तो यह एक कीवर्ड तर्क बन जाता है, इसलिए इसे स्थितिगत तर्क के बाद आना चाहिए x, जैसे:

3 के बाद:

>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)

मैं नेस्टेड लैम्ब्डा के अंतिम संस्करण को पसंद करूंगा, लेकिन प्रत्येक को अपने ...


22
यह एक क्रूर और असामान्य नौकरी का साक्षात्कार प्रश्न है।
सेज़िट्लिन

1
अगर मेरे सहकर्मी ने नहीं पूछा, तो मैं शायद इस जवाब की खोज नहीं
करूंगा

8
वाह। मेरे इस बेतुके व्यवहार से मैं बुरी तरह से कट गया। आपके पोस्ट के लिए शुक्रिया!
चींटी

1
बहुत बढ़िया जवाब। मैं इस मुद्दे में भी भाग गया। एक तरफ यह पायथन सीमा की ओर इशारा करता है, लेकिन दूसरी तरफ यह एक कोड गंध संकेतक भी हो सकता है। मैं एक खिलौना परियोजना के लिए इस समाधान का उपयोग कर रहा हूं, लेकिन यह एक उत्पादन वातावरण में पुनर्गठन का संकेत हो सकता है।
अंता

2
स्पष्टता और पूर्णता की खातिर आप अंतिम सूची की समझ लिख सकते हैं जैसे:[partial(lambda i, x: i * x, i) for i in (1, 2)]
josoler

19

बड़ा अंतर यह है कि पहला उदाहरण वास्तव में लैम्ब्डा को आमंत्रित करता है f(x), जबकि दूसरा उदाहरण नहीं है।

आपका पहला उदाहरण बराबर है [(lambda x: x*x)(x) for x in range(10)]जबकि आपका दूसरा उदाहरण इसके बराबर है [f for x in range(10)]


11

पहले वाला

f = lambda x: x*x
[f(x) for x in range(10)]

f()रेंज में प्रत्येक मूल्य के लिए चलाता है तो यह f(x)प्रत्येक मूल्य के लिए करता है

दूसरा एक

[lambda x: x*x for x in range(10)]

सूची में प्रत्येक मान के लिए लैम्ब्डा चलाता है, इसलिए यह उन सभी कार्यों को उत्पन्न करता है।


11

लोग अच्छा जवाब दिया था, लेकिन मेरी राय में सबसे महत्वपूर्ण हिस्सा उल्लेख करना भूल गया: दूसरे उदाहरण में Xसूची समझ के रूप में एक ही नहीं है Xकी lambdaसमारोह, किसी असंबद्ध हैं। तो दूसरा उदाहरण वास्तव में जैसा है:

[Lambda X: X*X for I in range(10)]

आंतरिक पुनरावृत्तियों range(10)केवल एक सूची में 10 समान लैंबडा फ़ंक्शन बनाने के लिए जिम्मेदार हैं (10 अलग-अलग फ़ंक्शंस लेकिन पूरी तरह समान - प्रत्येक इनपुट की शक्ति 2 लौटाते हुए)।

दूसरी ओर, पहला उदाहरण पूरी तरह से अलग काम करता है, क्योंकि पुनरावृत्तियों के एक्स परिणाम के साथ बातचीत करते हैं, प्रत्येक पुनरावृत्ति के लिए मूल्य है X*Xइसलिए परिणाम होगा[0,1,4,9,16,25, 36, 49, 64 ,81]


यह एक महत्वपूर्ण मुद्दा है। मैंने आपको उत्तर दिया और अपने उत्तर में इस पर विस्तार से बताया।
टॉमस गैंडर

6

अन्य उत्तर सही हैं, लेकिन यदि आप कार्यों की एक सूची बनाने की कोशिश कर रहे हैं, प्रत्येक एक अलग पैरामीटर के साथ, जिसे बाद में निष्पादित किया जा सकता है , तो निम्न कोड ऐसा करेगा:

import functools
a = [functools.partial(lambda x: x*x, x) for x in range(10)]

b = []
for i in a:
    b.append(i())

In [26]: b
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

जब उदाहरण से वंचित किया जाता है, तो मुझे यह उपयोगी लगता था जब मुझे उन कार्यों की एक सूची चाहिए थी, जिनमें से प्रत्येक में कुछ अलग हो, अर्थात

import functools
a = [functools.partial(lambda x: print(x), x) for x in range(10)]

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