2017-10-11 182 views
0

我想寫兩個單獨但可堆棧的裝飾器,一個用於在方法之前和之後打印對象的狀態,另一個用於在方法之後運行一些內部類測試其中也有爭論)。python - 通過堆棧裝飾器傳遞函數參數

這裏我現在嘗試的例子:如果裝飾的排序如上應用

import functools 

class Dog: 

    def __init__(self): 
     self.happy = False 
     self.has_stick = False 

    def __str__(self): 
     n = ' ' if self.happy else ' not ' 
     return "I'm%sa happy dog" % n 

    def _verbose(func): 
     fname = func.func_name 
     argnames = func.func_code.co_varnames[:func.func_code.co_argcount] 
     @functools.wraps(func) 
     def decorator(*args, **kwargs): 
      print "Before %s(%s):" % (fname, ', '.join(
       '%s=%r' % entry 
       for entry in zip(argnames, args)[1:] + kwargs.items())) 
      print args[0] 
      result = func(*args, **kwargs) 
      print "After %s(%s):" % (fname, ', '.join(
       '%s=%r' % entry 
       for entry in zip(argnames, args)[1:] + kwargs.items())) 
      print args[0] 
      return result 
     return decorator 

    def _test(printout): 
     def actual_decorator(func): 
      @functools.wraps(func) 
      def wrapper(*args, **kwargs): 
       self = args[0] 
       output = func(*args, **kwargs) 
       self._test_not_happy_without_stick(printout) 
       return output 
      return wrapper 
     return actual_decorator 

    def _test_not_happy_without_stick(self, printout): 
     if printout: 
      print "Is happy:", self.happy 
      print "Has stick:", self.has_stick 
     if self.happy and not self.has_stick: 
      print "ERROR" 

    @_test(True) 
    @_verbose 
    def finds_stick(self, good_stick): 
     print "I found a stick!" 
     self.happy = good_stick 
     self.has_stick = True 

if __name__ == '__main__': 
    fido = Dog() 
    fido.finds_stick(False) 

,輸出爲:

Before finds_stick(good_stick=True): 
I'm not a happy dog 
I found a stick! 
After finds_stick(good_stick=True): 
I'm a happy dog 
Is happy: True 
Has stick: True 

但是,如果是相反的(如我想要做的),傳遞給修飾函數的參數的名稱和值將丟失,如下所示:

Before finds_stick(): 
I'm not a happy dog 
I found a stick! 
Is happy: True 
Has stick: True 
After finds_stick(): 
I'm a happy dog 

如何確保裝飾器的這種堆疊不會阻止參數通過裝飾器傳遞?

另外,我會很樂意提供更多pythonic方法來解決這個問題。

回答

0

原來,這個問題是由於@functools.wrap()的向後兼容性造成的,它不保留簽名以實現我上面需要的功能(就像它在Python 3.4+中那樣)。

但是,您可以通過使用decorator程序包獲得所需的功能,如概述here所述。

用於上述正確的代碼,則:

import decorator 

class Dog: 

    def __init__(self): 
     self.happy = False 
     self.has_stick = False 

    def __str__(self): 
     n = ' ' if self.happy else ' not ' 
     return "I'm%sa happy dog" % n 

    @decorator.decorator 
    def _verbose(func, *args, **kwargs): 
     fname = func.func_name 
     argnames = func.func_code.co_varnames[:func.func_code.co_argcount] 
     print "Before %s(%s):" % (fname, ', '.join(
      '%s=%r' % entry 
      for entry in zip(argnames, args)[1:] + kwargs.items())) 
     print args[0] 
     result = func(*args, **kwargs) 
     print "After %s(%s):" % (fname, ', '.join(
      '%s=%r' % entry 
      for entry in zip(argnames, args)[1:] + kwargs.items())) 
     print args[0] 
     return result 

    def _test(printout): 
     @decorator.decorator 
     def wrapper(func, *args, **kwargs): 
      self = args[0] 
      output = func(*args, **kwargs) 
      self._test_not_happy_without_stick(printout) 
      return output 
     return wrapper 

    def _test_not_happy_without_stick(self, printout): 
     if printout: 
      print "Is happy:", self.happy 
      print "Has stick:", self.has_stick 
     if self.happy and not self.has_stick: 
      print "ERROR" 

    @_verbose 
    @_test(True) 
    def finds_stick(self, good_stick): 
     print "I found a stick!" 
     self.happy = good_stick 
     self.has_stick = True 

if __name__ == '__main__': 
    fido = Dog() 
    fido.finds_stick(True) 

隨着輸出:

Before finds_stick(good_stick=True): 
I'm not a happy dog 
I found a stick! 
Is happy: True 
Has stick: True 
After finds_stick(good_stick=True): 
I'm a happy dog