我正在尝试编写两个独立但可堆叠的装饰器,一个用于在方法之前和之后打印对象的状态,另一个用于在方法之后运行一些内部类测试(也有参数)。
这是我当前尝试的一个例子:
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 :(得分: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