2017-10-11 184 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