这是 Python 2.5,也是 GAE,但这并不重要。
我有以下代码。我正在 bar 中装饰 foo() 方法,使用
dec_check
类作为装饰器。
class dec_check(object):
def __init__(self, f):
self.func = f
def __call__(self):
print 'In dec_check.__init__()'
self.func()
class bar(object):
@dec_check
def foo(self):
print 'In bar.foo()'
b = bar()
b.foo()
执行此操作时,我希望看到:
In dec_check.__init__()
In bar.foo()
但是我得到
TypeError: foo() takes exactly 1 argument (0 given)
作为 .foo()
,作为一个对象方法,将 self
作为参数。我猜问题是,当我执行装饰器代码时,bar
的实例实际上并不存在。
那么如何将
bar
的实例传递给装饰器类?
你需要将装饰器变成一个描述符——要么确保它的(元)类有一个
__get__
方法,或者,way更简单,使用装饰器function而不是装饰器class (因为函数已经是描述符)。例如:
def dec_check(f):
def deco(self):
print 'In deco'
f(self)
return deco
class bar(object):
@dec_check
def foo(self):
print 'in bar.foo'
b = bar()
b.foo()
这个打印
In deco
in bar.foo
随心所欲。
当函数足够时,Alex 的答案就足够了。但是,当您需要一个类时,您可以通过将以下方法添加到装饰器类来使其工作。
def __get__(self, obj, objtype):
"""Support instance methods."""
import functools
return functools.partial(self.__call__, obj)
要理解这一点,您需要了解描述符协议。描述符协议是将事物绑定到实例的机制。它由
__get__
、__set__
和 __delete__
组成,当从实例字典中获取、设置或删除事物时会调用它们。
在这种情况下,当从实例获取事物时,我们使用partial将其
__call__
方法的第一个参数绑定到实例。对于成员函数,这是在构造类时自动完成的,但对于像这样的合成成员函数,我们需要显式地执行此操作。
如果你想将装饰器写成一个类,你可以这样做:
from functools import update_wrapper, partial
class MyDecorator(object):
def __init__(self, func):
update_wrapper(self, func)
self.func = func
def __get__(self, obj, objtype):
"""Support instance methods."""
return partial(self.__call__, obj)
def __call__(self, obj, *args, **kwargs):
print('Logic here')
return self.func(obj, *args, **kwargs)
my_decorator = MyDecorator
class MyClass(object):
@my_decorator
def my_method(self):
pass