2012-04-16 3 views
64

मैं की तरहपायथन सजावट के लिए अतिरिक्त तर्क कैसे पारित करें?

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() takes exactly 2 arguments (1 given) 

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

+2

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

+7

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

+0

@ किटहो - "जीडी" क्या है? अच्छी है"? –

उत्तर

107

आप एक समारोह की तरह डेकोरेटर बुला रहे हैं के बाद से, यह एक और समारोह जो वास्तविक डेकोरेटर है वापस जाने के लिए की जरूरत है:

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 

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

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

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

और ['functools.wraps'] (http://docs.python.org/library/functools.html#functools.wraps) का उपयोग करके सलाह दी जाती है - यह लपेटा हुआ फ़ंक्शन के मूल नाम, डॉकस्ट्रिंग इत्यादि को बरकरार रखता है। – AKX

+0

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

+1

तो मूल रूप से सजावटी हमेशा केवल एक तर्क लेता है जो कार्य है। लेकिन सजावटी एक समारोह का वापसी मूल्य हो सकता है जो तर्क ले सकता है। क्या ये सही है? – balki

34

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

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

विशेष रूप से, expr को

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

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

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

तो

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

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

14

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

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) 
1

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

import functools 

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): 
     @functools.wraps(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()