在父类外部使用可调用对象装饰Python方法
假设我有一个类,想用它来做装饰器,像这样:
class specialmethod:
def __init__(self,func):
self.func = func
def __call__(self,arg1,arg2):
print arg1,arg2
self.func(arg1,arg2)
但是,被装饰的方法是在另一个类里面,这个类也需要用到 self
(因为 self
里面的值对装饰后的函数很重要)
class A:
@specialmethod
def dosomething(self,data,otherdata):
#this guy down here isn't getting the right value for self, an instance of A
print type(self)
Traceback (most recent call last):
File "deco.py", line 18, in <module>
a.dosomething('foo','bar')
File "deco.py", line 7, in __call__
self.func(arg1,arg2)
TypeError: dosomething() takes exactly 3 arguments (2 given)
有没有办法保持 self
的工作方式,就像没有被装饰一样?我试着显式地传递它,但还是期望的参数数量不对。
编辑
我需要澄清一下,最终的 dosomething
函数体中的 self
应该指向 A
的实例……我并不是想把装饰器的实例传到被装饰的函数里。
1 个回答
4
当你把一个类当作装饰器使用时,你需要明白,通常把函数变成方法的那种“魔法”(也就是描述符协议,特别是 __get__
方法)在你的类上是不会自动生效的,除非你自己去实现它。你可以手动实现一个 __get__
方法,这个方法会返回一个新的类型,这个类型会记住它是从哪个实例中获取的,以及实际要调用的函数,但这样做会很麻烦,而且收获不大。
更常见的做法是,不直接使用一个类作为装饰器,而是让装饰器返回一个真正的 Python 函数。在这种情况下,实际上根本没有必要保留这个类,所以你可以直接这样做:
def specialmethod(f):
@functools.wraps(f)
def wrapper(arg1, arg2):
print arg1, arg2
f(arg1, arg2)
return wrapper
... 但是如果你真的想保留这个类,你可以这样做:
class _specialmethod:
def __init__(self,func):
self.func = func
def __call__(self,arg1,arg2):
print arg1,arg2
self.func(arg1,arg2)
def specialmethod(f):
wrapper_class = _specialmethod(f)
@functools.wraps(f)
def wrapper(arg1, arg2):
wrapper_class(arg1, arg2)
return wrapper