मैं पायथन डेकोरेटर को अतिरिक्त तर्क कैसे दे सकता हूं?


99

मेरे पास नीचे की तरह एक डेकोरेटर है।

def myDecorator(test_func):
    return callSomeWrapper(test_func)
def callSomeWrapper(test_func):
    return test_func
@myDecorator
def someFunc():
    print 'hello'

मैं नीचे की तरह एक और तर्क स्वीकार करने के लिए इस डेकोरेटर को बढ़ाना चाहता हूं

def myDecorator(test_func,logIt):
    if logIt:
        print "Calling Function: " + test_func.__name__
    return callSomeWrapper(test_func)
@myDecorator(False)
def someFunc():
    print 'Hello'

लेकिन यह कोड त्रुटि देता है,

TypeError: myDecorator () बिल्कुल 2 तर्क देता है (1 दिया गया)

क्यों फ़ंक्शन स्वचालित रूप से पारित नहीं होता है? मैं फ़ंक्शन डेकोरेटर फ़ंक्शन को स्पष्ट रूप से कैसे पास करूं?


3
बाल्की: कृपया अपने तर्क के रूप में बूलियन का उपयोग करने से बचें, यह एक जीडी दृष्टिकोण नहीं है और कोड की पठनीयता को कम करता है
किट हो

8
@ किथो - यह एक बूलियन ध्वज है, इसलिए बूलियन मूल्य का उपयोग करना सही दृष्टिकोण है।
AKX

2
@ किथो - "जीडी" क्या है? अच्छी है"?
रोब बेडनार्क

जवाबों:


172

चूंकि आप डेकोरेटर को एक फंक्शन की तरह बुला रहे हैं, इसलिए उसे एक और फंक्शन लौटाना होगा, जो कि वास्तविक डेकोरेटर है:

def my_decorator(param):
    def actual_decorator(func):
        print("Decorating function {}, with parameter {}".format(func.__name__, param))
        return function_wrapper(func)  # assume we defined a wrapper somewhere
    return actual_decorator

बाहरी फ़ंक्शन को कोई भी तर्क दिया जाएगा जिसे आप स्पष्ट रूप से पास करते हैं, और आंतरिक फ़ंक्शन को वापस करना चाहिए। आंतरिक फ़ंक्शन को सजाने के लिए फ़ंक्शन को संशोधित किया जाएगा, और संशोधित फ़ंक्शन लौटाएगा।

आमतौर पर आप चाहते हैं कि डेकोरेटर एक रैपर फ़ंक्शन में लपेटकर फ़ंक्शन व्यवहार को बदल दे। यहां एक उदाहरण दिया गया है जब फ़ंक्शन को वैकल्पिक रूप से लॉगिंग कहा जाता है:

def log_decorator(log_enabled):
    def actual_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if log_enabled:
                print("Calling Function: " + func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return actual_decorator

functools.wrapsनाम और आवरण समारोह के लिए docstring की तरह कॉल प्रतियां बातें, यह अधिक मूल कार्य के समान बनाने के लिए।

उदाहरण उपयोग:

>>> @log_decorator(True)
... def f(x):
...     return x+1
...
>>> f(4)
Calling Function: f
5

11
और उपयोग functools.wrapsकरना उचित है - यह लिपटे फ़ंक्शन के मूल नाम, डॉकस्ट्रिंग आदि को बरकरार रखता है।
AKX

@AKX: धन्यवाद, मैंने इसे दूसरे उदाहरण में जोड़ा।
इंटरजिह

1
इसलिए मूल रूप से डेकोरेटर हमेशा केवल एक तर्क लेता है जो फ़ंक्शन है। लेकिन डेकोरेटर एक फ़ंक्शन का रिटर्न मान हो सकता है जो तर्क ले सकता है। क्या ये सही है?
बाल्की

2
@ बाल्की: हाँ, यह सही है। चीजों को क्या भ्रमित करता है कि कई लोग बाहरी फ़ंक्शन ( myDecoratorयहां) को एक डेकोरेटर भी कहेंगे । यह डेकोरेटर के उपयोगकर्ता के लिए सुविधाजनक है, लेकिन जब आप एक लिखने की कोशिश कर रहे हैं तो भ्रमित हो सकते हैं।
interjay

1
छोटा विवरण जो मुझे भ्रमित करता है: यदि आपका log_decoratorडिफ़ॉल्ट तर्क लेता है, तो आप इसका उपयोग नहीं कर सकते @log_decorator, यह होना चाहिए@log_decorator()
स्टारडीडी

46

बस एक अलग दृष्टिकोण प्रदान करने के लिए: वाक्यविन्यास

@expr
def func(...): #stuff

के बराबर है

def func(...): #stuff
func = expr(func)

विशेष रूप से, exprजब तक आप किसी कॉल करने योग्य का मूल्यांकन करते हैं, तब तक आप कुछ भी पसंद कर सकते हैं। में विशेष रूप से विशेष रूप से, exprएक डेकोरेटर कारखाने हो सकता है: आप इसे कुछ मानकों देने के लिए और यह आप एक डेकोरेटर देता है। तो शायद आपकी स्थिति को समझने का एक बेहतर तरीका है

dec = decorator_factory(*args)
@dec
def func(...):

जिसे तब छोटा किया जा सकता है

@decorator_factory(*args)
def func(...):

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


धन्यवाद, यह वास्तव में मुझे समझने में मदद करता है कि क्या चल रहा है।
आदित्य श्रीराम

25

बस कुछ उपयोगी ट्रिक जोड़ना चाहते हैं जो डेकोरेटर तर्कों को वैकल्पिक बनाने की अनुमति देगा। यह डेकोरेटर के पुन: उपयोग और घोंसले के शिकार को कम करने के लिए भी बदल जाएगा

import functools

def myDecorator(test_func=None,logIt=None):
    if not test_func:
        return functools.partial(myDecorator, logIt=logIt)
    @functools.wraps(test_func)
    def f(*args, **kwargs):
        if logIt==1:
            print 'Logging level 1 for {}'.format(test_func.__name__)
        if logIt==2:
            print 'Logging level 2 for {}'.format(test_func.__name__)
        return test_func(*args, **kwargs)
    return f

#new decorator 
myDecorator_2 = myDecorator(logIt=2)

@myDecorator(logIt=2)
def pow2(i):
    return i**2

@myDecorator
def pow3(i):
    return i**3

@myDecorator_2
def pow4(i):
    return i**4

print pow2(2)
print pow3(2)
print pow4(2)

16

डेकोरेटर करने का एक और तरीका। मुझे इस तरह से अपने सिर को लपेटने में सबसे आसान लगता है।

class NiceDecorator:
    def __init__(self, param_foo='a', param_bar='b'):
        self.param_foo = param_foo
        self.param_bar = param_bar

    def __call__(self, func):
        def my_logic(*args, **kwargs):
            # whatever logic your decorator is supposed to implement goes in here
            print('pre action baz')
            print(self.param_bar)
            # including the call to the decorated function (if you want to do that)
            result = func(*args, **kwargs)
            print('post action beep')
            return result

        return my_logic

# usage example from here on
@NiceDecorator(param_bar='baaar')
def example():
    print('example yay')


example()

धन्यवाद! लगभग 30 मिनट के लिए कुछ दिमाग झुकने वाले "समाधान" को देख रहा है और यह पहला ऐसा है जो वास्तव में समझ में आता है।
कान्हाबिट्स

0

अब अगर आप function1डेकोरेटर के साथ फंक्शन को कॉल करना चाहते हैं decorator_with_argऔर इस मामले में फंक्शन और डेकोरेटर दोनों ही तर्क देते हैं,

def function1(a, b):
    print (a, b)

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