पायथन 3.0 में पेश किए गए फ़ंक्शन एनोटेशन एक ऐसी सुविधा जोड़ते हैं जो आपको फ़ंक्शन पैरामीटर और रिटर्न वैल्यू में मनमाना मेटाडेटा जोड़ने की अनुमति देता है। अजगर 3 के बाद से, फ़ंक्शन एनोटेशन को आधिकारिक तौर पर अजगर (PEP-3107) में जोड़ा गया है। प्राथमिक उद्देश्य मेटाडेटा को फ़ंक्शन पैरामीटर और रिटर्न वैल्यू से जोड़ने का एक मानक तरीका होना था।
फ़ंक्शन एनोटेशन की मूल बातें
आइए फ़ंक्शन एनोटेशन की कुछ बुनियादी बातों को समझते हैं -
-
पैरामीटर और वापसी मान दोनों के लिए फ़ंक्शन एनोटेशन पूरी तरह से वैकल्पिक हैं।
-
फ़ंक्शन एनोटेशन किसी फ़ंक्शन के विभिन्न भागों को संकलन समय पर मनमाने ढंग से पायथन अभिव्यक्तियों के साथ जोड़ने का एक तरीका प्रदान करते हैं।
-
PEP-3107 बिल्ट-इन प्रकारों के लिए भी, किसी भी प्रकार के मानक शब्दार्थ को पेश करने का कोई प्रयास नहीं करता है। यह सारा काम तीसरे पक्ष के पुस्तकालयों पर छोड़ दिया गया है।
सिंटैक्स
साधारण मापदंडों की व्याख्या
पैरामीटर के लिए एनोटेशन निम्नलिखित रूप लेते हैं -
def foo(x: expression, y: expression = 20): ….
जबकि अतिरिक्त पैरामीटर के लिए एनोटेशन इस प्रकार हैं -
def foo(**args: expression, **kwargs: expression): …..
नेस्टेड पैरामीटर के मामले में, एनोटेशन हमेशा पैरामीटर के नाम का पालन करते हैं न कि अंतिम कोष्ठक तक। नेस्टेड पैरामीटर के सभी पैरामीटर को एनोटेट करने की आवश्यकता नहीं है।
def foo(x1, y1: expression), (x2: expression, y2: expression)=(None, None)): ……
यह समझना महत्वपूर्ण है कि पायथन एनोटेशन के साथ कोई शब्दार्थ प्रदान नहीं करता है। यह मेटाडेटा को जोड़ने के साथ-साथ इसे एक्सेस करने का एक आसान तरीका के लिए केवल अच्छा वाक्य रचनात्मक समर्थन प्रदान करता है। साथ ही, एनोटेशन अनिवार्य नहीं हैं।
>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z)
उपरोक्त उदाहरण में, फ़ंक्शन func () x, y और z नामक तीन पैरामीटर लेता है, अंत में उनके योग को प्रिंट करता है। पहला तर्क x को 'एनोटेटिंग x' स्ट्रिंग के साथ एनोटेट किया गया है, दूसरा तर्क y को 'एनोटेटिंग y' स्ट्रिंग के साथ एनोटेट किया गया है, और तीसरा तर्क z टाइप int के साथ एनोटेट किया गया है। रिटर्न वैल्यू को फ्लोट प्रकार के साथ एनोटेट किया जाता है। यहाँ '->' सिंटेक्स रिटर्न वैल्यू को एनोटेट करने के लिए है।
आउटपुट
>>> func(2,3,-4)
1
>>> func('Function','-','Annotation')
Function-Annotation ऊपर हम func() को दो बार कॉल करते हैं, एक बार int तर्कों के साथ और एक बार स्ट्रिंग तर्कों के साथ। दोनों ही मामलों में, func() सही काम करता है और एनोटेशन को केवल अनदेखा किया जाता है। इसलिए, हम देखते हैं कि फ़ंक्शन func() के निष्पादन पर एनोटेशन का कोई प्रभाव नहीं पड़ता है।
फ़ंक्शन एनोटेशन एक्सेस करना
सभी एनोटेशन को __annotations__ नामक एक शब्दकोश में संग्रहीत किया जाता है, जो स्वयं फ़ंक्शन की एक विशेषता है -
>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z)
>>> func.__annotations__
{'x': 'annotating x', 'y': 'annotating y', 'z': <class 'int'>, 'return': <class 'float'>} जैसा कि हम पिछले कोड उदाहरण में देख सकते हैं, एनोटेशन टाइप की गई घोषणाएं नहीं हैं, हालांकि वे निश्चित रूप से उस उद्देश्य के लिए उपयोग किए जा सकते हैं और वे कुछ अन्य भाषाओं में उपयोग किए जाने वाले टाइपिंग सिंटैक्स से मिलते जुलते हैं, जैसा कि नीचे दिखाया गया है -
>>> def func(a: 'python', b: {'category: ' 'language'}) -> 'yep':
pass
>>> func.__annotations__
{'a': 'python', 'b': {'category: language'}, 'return': 'yep'}
>>> वे मनमानी अभिव्यक्ति हैं, जिसका अर्थ है कि मनमानी मूल्यों को __annotations__ शब्दकोश में संग्रहीत किया जा सकता है। हालाँकि, वे स्वयं अजगर के लिए बहुत महत्व नहीं जोड़ते हैं, सिवाय इसके कि इसे मूल्यों को संग्रहीत करना चाहिए। उस ने कहा, पैरामीटर और रिटर्न प्रकारों को परिभाषित करना फ़ंक्शन एनोटेशन का एक सामान्य उपयोग है।
@no_type_check डेकोरेटर
यदि आप अपने आप को एक ऐसे टूल का उपयोग करते हुए पाते हैं जो मानता है कि एनोटेशन टाइप डिक्लेरेशन हैं, लेकिन आप उनका उपयोग किसी अन्य उद्देश्य के लिए करना चाहते हैं, तो अपने फ़ंक्शन को इस तरह की प्रोसेसिंग से छूट देने के लिए मानक @no_type_check डेकोरेटर का उपयोग करें, जैसा कि यहां दिखाया गया है -
>>> from typing import no_type_check
>>> @no_type_check
def func(a: 'python', b: {'category: ' 'language'}) -> 'yep':
pass
>>> आम तौर पर, इसकी आवश्यकता नहीं होती है क्योंकि अधिकांश उपकरण जो एनोटेशन का उपयोग करते हैं, उनके पास उनके लिए बनाए गए एनोटेशन को पहचानने का एक तरीका होता है। डेकोरेटर कोने के मामलों की सुरक्षा के लिए है जहां चीजें अस्पष्ट हैं।
फ़ंक्शन डेकोरेटर्स के इनपुट के रूप में एनोटेशन
एनोटेशन डेकोरेटर के साथ अच्छी तरह से मेल खाते हैं क्योंकि एनोटेशन वैल्यू डेकोरेटर को इनपुट प्रदान करने का एक अच्छा तरीका है, और डेकोरेटर-जनरेटेड रैपर कोड डालने के लिए एक अच्छी जगह है जो एनोटेशन को अर्थ देता है।
from functools import wraps
def adapted(func):
@wraps(func)
def wrapper(**kwargs):
final_args = {}
for name, value in kwargs. items():
adapt = func.__annotations__.get(name)
if adapt is not None:
final_args[name] = adapt(value)
else:
final_args[name] = value
result = func(**final_args)
adapt = func.__annotations__.get('result')
if adapt is not None:
return adapt(result)
return result
return wrapper
@adapted
def func(a: int, b: repr) -> str:
return a तो, अनुकूलित डेकोरेटर फ़ंक्शन को एक आवरण में संलग्न करता है। यह रैपर केवल कीवर्ड तर्कों को स्वीकार करता है, जिसका अर्थ है कि यदि मूल फ़ंक्शन स्थितिगत तर्कों को स्वीकार कर सकता है, तो भी उन्हें नाम से निर्दिष्ट करना होगा।
एक बार फंक्शन रैप हो जाने के बाद, रैपर फंक्शन के पैरामीटर एनोटेशन में एडेप्टर की तलाश भी करता है और तर्कों को वास्तविक फंक्शन में पास करने से पहले उन्हें लागू करता है।
एक बार जब फ़ंक्शन वापस आ जाता है, तो रैपर रिटर्न वैल्यू एडॉप्टर के लिए जाँच करता है; अगर उसे कोई मिल जाता है, तो वह अंत में उसे वापस करने से पहले उसे वापसी मूल्य पर लागू कर देता है।
जब हम यहां जो हो रहा है उसके निहितार्थों पर विचार करते हैं, तो वे बहुत प्रभावशाली होते हैं। हमने वास्तव में संशोधित किया है कि किसी फ़ंक्शन को पैरामीटर पास करने या मान वापस करने का क्या अर्थ है।
कीवर्ड तर्क
कभी-कभी, एक या अधिक विधि के मापदंडों को किसी भी प्रसंस्करण की आवश्यकता नहीं होती है, सिवाय उन्हें स्वयं की विशेषता के लिए निर्दिष्ट करने के। क्या हम इसे स्वचालित रूप से करने के लिए डेकोरेटर और एनोटेशन का उपयोग कर सकते हैं? बेशक, हम कर सकते हैं।
from functools import wraps def store_args(func): @wraps(func) def wrapper(self, **kwargs): for name, value in kwargs.items(): attrib = func.__annotations__.get(name) if attrib is True: attrib = name if isinstance(attrib, str): setattr(self, attrib, value) return func(self, **kwargs) return wrapper class A: @store_args def __init__(self, first: True, second: 'example'): pass a = A(first = 5, second = 6) assert a.first == 5 assert a.example == 6