只有在实例化类的实例时,是否有一种干净的方法让装饰器在类上调用实例方法?
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()
我知道这不起作用,因为self
未被定义decorator
被调用(因为它没有被调用为实例方法,因为没有对该类的可用引用)。然后我提出了这个解决方案:
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()
现在该函数只被调用一次,但我不喜欢每次调用函数时都要进行此检查的事实。我猜它没有办法绕过它,但如果有人有任何建议,我很乐意听到它们!谢谢:))
答案 0 :(得分: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()
答案 1 :(得分:0)
我认为你在问一些根本不可能的事情。装饰器将与类同时创建,但实例方法在实例执行之前不存在,稍后才会存在。因此,装饰器无法处理特定于实例的功能。
另一种思考方式是装饰器是一个仿函数:它将函数转换为其他函数。但它没有说明这些函数的参数;它的工作水平高于此水平。因此,在function
的参数上调用实例方法不应该由装饰器完成;这应该由function
完成。
解决这个问题的方法必然是hackish。你的方法看起来不错,就像黑客一样。
答案 2 :(得分:0)
可以通过使用callables作为装饰器来实现。
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()
答案 3 :(得分:0)
类C
的多个实例应如何表现? instance_method
只应调用一次,无论哪个实例调用function
?或者每个实例应该调用一次instance_method
吗?
您的called=[]
默认参数使装饰器记住调用了字符串名function
的内容。如果decorator
用于两个具有名为function
的方法的不同类,该怎么办?然后
c=C()
d=D()
c.function()
d.function()
只会调用c.instance_method
并阻止调用d.instance_method
。很奇怪,可能不是你想要的。
下面,我使用self._instance_method_called
来记录是否已调用self.instance_method
。这会使C
的每个实例最多调用instance_method
一次。
如果您希望instance_method
最多被调用一次,而不管C
调用function
的哪个实例,那么只需将_instance_method_called
定义为类属性而不是实例属性。
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped(self,*args):
print('Locals in wrapper %s' % locals())
if not self._instance_method_called:
self.instance_method()
self._instance_method_called=True
return f
return wrapped
return wrap
class C:
def __init__(self):
self._instance_method_called=False
def instance_method(self): print('Method called')
@decorator()
def function(self):
pass
c = C()
# Locals in decorator {}
c.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
# Method called
c.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
d = C()
d.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
# Method called
d.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
编辑:删除if
声明:
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def rest(self,*args):
print('Locals in wrapper %s' % locals())
return f
def first(self,*args):
print('Locals in wrapper %s' % locals())
self.instance_method()
setattr(self.__class__,f.func_name,rest)
return f
return first
return wrap
class C:
def instance_method(self): print('Method called')
@decorator()
def function(self):
pass