फ़ंक्शन को प्रथम-श्रेणी ऑब्जेक्ट के रूप में इस्तेमाल करके मज़बूत, पैरामीटर-योग्य और पारदर्शी डेकोरेटर लिखें।
इस पाठ को Kodokon में खोलेंPython में, फ़ंक्शन बाकी सबकी तरह एक ऑब्जेक्ट है: आप उसे किसी वेरिएबल को असाइन कर सकते हैं, आर्गुमेंट के रूप में पास कर सकते हैं, लौटा सकते हैं। एक उच्च-क्रम फ़ंक्शन कोई फ़ंक्शन लेता है या लौटाता है - sorted(data, key=...) ऐसा ही एक है। इसमें क्लोज़र जोड़ दीजिए, जहाँ भीतरी फ़ंक्शन बाहरी फ़ंक्शन के वेरिएबल को कैप्चर करता है और उसके लौटने के बाद भी उन्हें ज़िंदा रखता है, और आपके पास डेकोरेटर की सारी सामग्री आ जाती है।
def make_multiplier(factor):
def multiply(value):
return value * factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(10), triple(10)) # 20 30एक डेकोरेटर ऐसा फ़ंक्शन है जो कोई फ़ंक्शन लेता है और दूसरा लौटाता है - आमतौर पर एक wrapper, जो कॉल के इर्द-गिर्द व्यवहार जोड़ता है। def slow_sum के ऊपर लिखा @timed सिंटैक्स ठीक-ठीक slow_sum = timed(slow_sum) का सिंटैक्टिक शुगर है। रैपर सिग्नेचर से बेपरवाह रहने के लिए *args, kwargs स्वीकार करता है, और डेकोरेट किए गए फ़ंक्शन का परिणाम लौटाता** है - इसे भूल जाना एक क्लासिक गलती है जो हर रिटर्न वैल्यू को None में बदल देती है।
import functools
import time
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
delay = time.perf_counter() - start
print(f"{func.__name__}: {delay:.4f}s")
return result
return wrapper
@timed
def slow_sum(n):
return sum(range(n))
print(slow_sum(1_000_000))किसी डेकोरेटर को पैरामीटर देने के लिए (@repeat(times=3)), आपको एक और स्तर चाहिए: एक फैक्ट्री जो पैरामीटर लेती है और डेकोरेटर लौटाती है, जो बदले में रैपर लौटाता है। तीन नेस्टेड फ़ंक्शन - लचीलेपन की यही कीमत है। यदि स्टेट जटिल हो जाए, तो __call__ मेथड वाली एक क्लास अक्सर अधिक पठनीय होती है। डेकोरेटर को अनुप्रस्थ सरोकारों के लिए बचाकर रखें: लॉगिंग, कैशिंग, पुनःप्रयास, एक्सेस नियंत्रण। जो डेकोरेटर बिज़नेस लॉजिक बदल देता है, वह पाठक से सबसे ज़रूरी बात छिपा देता है।
import functools
def repeat(times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello {name}")
greet("Ada")