在函数装饰器中调用Python实例方法
有没有什么简单的方法,让一个装饰器在类的实例被创建时,才调用这个类的一个实例方法呢?
class C:
def instance_method(self):
print('Method called')
def decorator(f):
print('Locals in decorator %s ' % locals())
def wrap(f):
print('Locals in wrapper %s' % locals())
self.instance_method()
return f
return wrap
@decorator
def function(self):
pass
c = C()
c.function()
我知道这样做不行,因为在调用decorator
的时候,self
是未定义的(因为它不是作为实例方法被调用的,类的引用在那时是不可用的)。于是我想出了这个解决方案:
class C:
def instance_method(self):
print('Method called')
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped_f(*args):
print('Locals in wrapper %s' % locals())
args[0].instance_method()
return f
return wrapped_f
return wrap
@decorator()
def function(self):
pass
c = C()
c.function()
这个方法利用了一个事实:我知道任何实例方法的第一个参数都是self
。不过,这个包装器的定义有个问题,就是每次执行这个函数时,实例方法都会被调用,这正是我不想要的。于是我又想出了一个稍微修改过的版本,这个版本可以工作:
class C:
def instance_method(self):
print('Method called')
def decorator(called=[]):
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped_f(*args):
print('Locals in wrapper %s' % locals())
if f.__name__ not in called:
called.append(f.__name__)
args[0].instance_method()
return f
return wrapped_f
return wrap
@decorator()
def function(self):
pass
c = C()
c.function()
c.function()
现在这个函数只会被调用一次,但我不喜欢每次调用这个函数时都要进行这个检查。我猜可能没有办法避免这种情况,但如果有人有建议,我很乐意听听!谢谢 :)
4 个回答
0
这可以通过使用可调用对象作为装饰器来实现。
class ADecorator(object):
func = None
def __new__(cls, func):
dec = object.__new__(cls)
dec.__init__(func)
def wrapper(*args, **kw):
return dec(*args, **kw)
return wrapper
def __init__(self, func, *args, **kw):
self.func = func
self.act = self.do_first
def do_rest(self, *args, **kw):
pass
def do_first(self, *args, **kw):
args[0].a()
self.act = self.do_rest
def __call__(self, *args, **kw):
return self.act(*args, **kw)
class A(object):
def a(self):
print "Original A.a()"
@ADecorator
def function(self):
pass
a = A()
a.function()
a.function()
0
我觉得你问的问题有点根本上不可能实现。装饰器是在创建类的时候就被创建了,但实例方法在实例存在之前是不存在的,也就是说,实例是后来的事情。所以,装饰器无法处理特定于实例的功能。
你可以把装饰器想象成一个函数转换器:它把一个函数变成另一个函数。但它并不关心这些函数的参数;它是在更高的层面上工作的。所以,在function
的参数上调用实例方法并不是装饰器应该做的事情,而是function
自己应该做的事情。
想要解决这个问题的方法有点像是变通。你的方法看起来还不错,算是个变通办法。
1
我想出了一个可能的替代方案。我喜欢这个方法,因为在定义函数的时候只调用了一次,在创建类的时候也只调用了一次。唯一的缺点就是这个方法会多占用一点点内存,用来存储函数的属性。
from types import FunctionType
class C:
def __init__(self):
for name,f in C.__dict__.iteritems():
if type(f) == FunctionType and hasattr(f, 'setup'):
self.instance_method()
def instance_method(self):
print('Method called')
def decorator(f):
setattr(f, 'setup', True)
return f
@decorator
def function(self):
pass
c = C()
c.function()
c.function()