क्या एक लंबोदा फ़ंक्शन को संकेत देना संभव है?


93

वर्तमान में, पायथन में, एक फ़ंक्शन के पैरामीटर और रिटर्न प्रकार निम्न प्रकार से संकेत दिए जा सकते हैं:

def func(var1: str, var2: str) -> int:
    return var1.index(var2)

जो इंगित करता है कि फ़ंक्शन दो स्ट्रिंग्स लेता है, और एक पूर्णांक देता है।

हालाँकि, यह वाक्यविन्यास लंबोदर के साथ बहुत उलझा हुआ है, जो इस तरह दिखता है:

func = lambda var1, var2: var1.index(var2)

मैंने दोनों मापदंडों और वापसी प्रकारों में टाइप संकेत देने की कोशिश की है, और मैं एक तरह से यह पता नहीं लगा सकता कि वाक्यविन्यास त्रुटि का कारण नहीं है।

क्या एक लंबोदा फ़ंक्शन को संकेत देना संभव है? यदि नहीं, तो टाइपिंग लैंबडास, या किसी भी कारण (स्पष्ट वाक्यविन्यास संघर्ष के अलावा) के लिए योजनाएं क्यों नहीं हैं?


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

आप ऐसा क्यों करना चाहते हो? लैम्ब्डा सिंटेक्स "थ्रू-दूर" कार्यों के लिए है, जो बहुत विवश संदर्भों में उपयोग के लिए है, उदाहरण के लिए, बिल्ट-इन के keyतर्क के रूप sortedमें। मैं वास्तव में इस तरह के सीमित संदर्भों में टाइप संकेत जोड़ने का एक बिंदु नहीं देखता हूं। इसके अलावा, lambdaबिंदु, IMHO को पूरी तरह से याद करने के लिए टाइप संकेत जोड़ने के लिए PEP-526 चर एनोटेशन का उपयोग करना । lambdaवाक्य रचना को परिभाषित करने का इरादा है गुमनाम कार्य करता है। उपयोग करने की बात क्या है lambdaऔर इसे तुरंत एक चर में बाँध दें? बस उपयोग करें def!
लुसियानो रामालो

जवाबों:


101

आप Python 3.6 में और PEP 526 वैरिएबल एनोटेशन का उपयोग कर सकते हैं । आप जेनेरिक केlambda साथ परिणाम को असाइन करने वाले चर का एनोटेट कर सकते हैं :typing.Callable

from typing import Callable

func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)

यह फ़ंक्शन ऑब्जेक्ट के लिए टाइप की गई जानकारी को स्वयं संलग्न नहीं करता है, केवल उस नामस्थान पर जिसे आपने ऑब्जेक्ट को संग्रहीत किया है, लेकिन यह आमतौर पर आपको टाइपिंग उद्देश्यों के लिए आवश्यक है।

हालाँकि, आप इसके बजाय केवल एक फ़ंक्शन स्टेटमेंट का उपयोग कर सकते हैं; एक ही लाभ है कि एक lambdaप्रस्ताव है कि आप एक बड़ी अभिव्यक्ति के अंदर एक सरल अभिव्यक्ति के लिए एक फ़ंक्शन परिभाषा रख सकते हैं । लेकिन उपरोक्त लैंबडा एक बड़ी अभिव्यक्ति का हिस्सा नहीं है, यह केवल एक असाइनमेंट स्टेटमेंट का हिस्सा है, इसे एक नाम के लिए बाध्य करता है। यह वास्तव में एक def func(var1: str, var2: str): return var1.index(var2)बयान क्या हासिल होगा।

ध्यान दें कि आप राज्यों के लिए प्रलेखन के रूप में या तो अलग से टिप्पणी *argsया **kwargsतर्क नहीं कर सकते Callable:

वैकल्पिक या कीवर्ड तर्क को इंगित करने के लिए कोई सिंटैक्स नहीं है; ऐसे फ़ंक्शन प्रकार शायद ही कभी कॉलबैक प्रकार के रूप में उपयोग किए जाते हैं।

एक विधि के साथ पीईपी 544 प्रोटोकॉल पर__call__ यह सीमा लागू नहीं होती है ; इसका उपयोग करें यदि आपको एक स्पष्ट परिभाषा की आवश्यकता है कि क्या तर्क स्वीकार किए जाने चाहिए। आपको Python 3.8 की आवश्यकता है या एक बैकपोर्ट के लिए typing-extensionsप्रोजेक्ट स्थापित करें :

from typing-extensions import Protocol

class SomeCallableConvention(Protocol):
    def __call__(var1: str, var2: str, spam: str = "ham") -> int:
        ...

func: SomeCallableConvention = lambda var1, var2, spam="ham": var1.index(var2) * spam

के लिए lambdaअभिव्यक्ति ही , आप किसी भी एनोटेशन (वाक्य रचना जिस पर पायथन के प्रकार हिंट बनाया गया है) का उपयोग नहीं कर सकते हैं। सिंटैक्स केवल defफ़ंक्शन स्टेटमेंट के लिए उपलब्ध है ।

से - पीईपी 3107 समारोह एनोटेशन :

लैम्ब्डा का सिंटैक्स एनोटेशन का समर्थन नहीं करता है। पैरामीटर सूची के चारों ओर कोष्ठक की आवश्यकता के आधार पर, लैम्ब्डा के सिंटैक्स को एनोटेशन का समर्थन करने के लिए बदला जा सकता है। हालाँकि यह निर्णय लिया गया कि यह परिवर्तन नहीं किया जाएगा क्योंकि:

  • यह एक असंगत परिवर्तन होगा।
  • लैम्ब्डा वैसे भी neutered हैं।
  • लंबोदर को हमेशा एक फ़ंक्शन में बदला जा सकता है।

आप अभी भी एनोटेशन को सीधे ऑब्जेक्ट में संलग्न कर सकते हैं, function.__annotations__विशेषता एक लेखन शब्दकोश है:

>>> def func(var1: str, var2: str) -> int:
...     return var1.index(var2)
...
>>> func.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
>>> lfunc = lambda var1, var2: var1.index(var2)
>>> lfunc.__annotations__
{}
>>> lfunc.__annotations__['var1'] = str
>>> lfunc.__annotations__['var2'] = str
>>> lfunc.__annotations__['return'] = int
>>> lfunc.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}

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


1
यदि उत्तर है func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)तो बेहतर उत्तर क्यों नहीं है def func(var1: str, var2: str) -> int: return var1.index(var2)???
गुइडो वैन रोसुम

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

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

समाधान यह देखकर भी विचलित करता है कि यह एक चर को एक निश्चित Callable प्रकार के साथ परिभाषित कर रहा है जिसे अपने जीवनकाल के दौरान अलग-अलग लंबोदर को सौंपा जा सकता है। यह एक फंक्शन को परिभाषित करने से अलग है (कम से कम मैपी बक्स यदि आप किसी डिफाइन को फिर से परिभाषित या पुन: असाइन करते हैं, लेकिन कॉल करने योग्य प्रकार का वैरिएबल नहीं है) और मूल प्रश्न से दूर "मैं सिर्फ टाइप एनोटेशन के साथ एक लैम्ब्डा चाहता हूं।" मैं यह दावा करना जारी रखता हूं कि इस मामले में डीफ फॉर्म के बजाय लैम्बडा फॉर्म रखने का कोई लाभ नहीं है। (और मैं कहूंगा कि भले ही कोई भी प्रकार के एनोटेशन नहीं थे।)
गुइडो वैन रोसम

1
@GuidovanRossum: क्या मैं आपको अपना जवाब लिखने के लिए आमंत्रित कर सकता हूं? भाषा के पिता के रूप में आपकी विशिष्ट स्थिति के साथ, आपके द्वारा दिए गए उत्तर को आसानी से इस एक को रेखांकित किया जाएगा, समय दिया जाएगा, और आपको ठीक वही लिखना होगा जो आपको लगता है कि इसमें होना चाहिए।
मार्टिन पीटर्स

47

अजगर 3.6 के बाद से, आप कर सकते हैं ( पीईपी 526 देखें ):

from typing import Callable
is_even: Callable[[int], bool] = lambda x: (x % 2 == 0)

मुझे यह उत्तर समझ में नहीं आता: आप किस प्रकार का सेट करते हैं x?
स्टेंकी

3
@stenci is_evenफ़ंक्शन वह है Callableजो एक intतर्क की अपेक्षा करता है, इसलिए x एक है int
जान

मैं अब समझता हूँ। पहली नज़र में मुझे लगा कि आप केवल लंबोदर द्वारा दिए गए प्रकार की व्याख्या कर रहे हैं, न कि उसके तर्कों के प्रकार।
स्टेंकी

4
यह वास्तव में स्वयं लैम्ब्डा की व्याख्या नहीं कर रहा है: आप लैम्बडा ऑब्जेक्ट से इन एनोटेशन को पुनः प्राप्त नहीं कर सकते क्योंकि आप एनोटेट फ़ंक्शन के लिए कर सकते हैं
cz

3
Python 3.7 के साथ mypy 0.701 इसे सही ढंग से टाइप करता है: is_even('x')एक प्रकार की त्रुटि का कारण बनता है।
कोनराड रुडोल्फ

-2

नहीं, यह संभव नहीं है, इसे बदलने की कोई योजना नहीं है, और कारण मुख्य रूप से वाक्यात्मक हैं।

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