Python 使用类作为装饰器?
我想下面这些可以作为一个装饰师Python 使用类作为装饰器?,python,Python,我想下面这些可以作为一个装饰师 class D: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) class A: @D def f(self, x): pass a=A() a.f(1) 但是我得到了TypeError:f()缺
class D:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
class A:
@D
def f(self, x):
pass
a=A()
a.f(1)
但是我得到了TypeError:f()缺少1个必需的位置参数:“x”
发生了什么?有没有一种方法可以像这样使用类作为装饰器?问题是,除了装饰器机制之外,还有Python使用的机制,以便类主体中的函数作为实例方法:它是。这实际上很简单:所有函数对象都有一个
\uuuu get\uuuu
方法(但不是\uuuu set\uuuuu
或\uu del\uuuu
)方法,该方法使它们成为“非数据描述符”。当Python从实例检索属性时,\uuuuuu get\uuuu
将以实例作为参数进行调用,-\uuuu get\uuuu
方法必须返回一个可调用的对象,该对象将作为方法使用,并且必须知道调用的实例是哪一个:
#仅示例-不要这样做,而是出于学习目的,
#由于并发问题:
D类:
定义初始化(self,func):
self.func=func
定义调用(self,*args,**kwargs):
返回self.func(self.instance,*args,**kwargs)
定义获取(自身、实例、所有者):
self.instance=instance
回归自我
A类:
@D
def f(自身,x):
打印(self,x)
a=a()
a、 f(1)
这将打印“1”
然而,由于很容易理解,这只允许一次在单个实例中调用修饰的方法——即使是拥有多个“a”实例的非并行代码,也可能在调用方法时考虑到错误的实例。也就是说,这个序列:
In[127]:a1=A()
In[128]:a2=A()
In[129]:f1=a1.f
In[130]:f2=a2.f
In[131]:f1()
最终将调用“a2.f()”而不是“a1.f()”
为了避免这种情况,您必须返回一个可调用的from\uuuu get\uuuu
,它不需要将实例作为类属性检索。一种方法是创建一个可调用的partial
并将其包括在内-但是,请注意,由于这是一个必要的步骤,因此装饰器类本身不需要在\uuuuuu call\uuuuu
方法中具有“run wrapper+original code”函数-它可以有任何名称:
从functools导入部分
D类:
定义初始化(self,func):
self.func=func
定义调用(self,*args,\u实例=None,**kwargs):
如果_实例:
返回self.func(_实例,*args,**kwargs)
其他:
返回self.func(*args,**kwargs)
定义获取(自身、实例、所有者):
返回部分(self.\u调用\u,\u实例=实例)
A类:
@D
def f(自身,x):
打印(self,x)
a=a()
a、 f(1)
Related:问题是您试图修饰一个实例方法,而不是用类实现修饰器。确切的问题是self.func(*args,**kwargs)
没有将self
参数传递给self.func
(即a.f
)。如果添加self
(self.func(self,*args,**kwargs)
)您将遇到另一个问题:self
这里指的是D
实例,而不是a
实例。事实上,我不确定是否可以用实现为类的装饰器装饰实例方法。如果D
是一个描述符,您可能需要在功能上实现它。查看我标记的副本“副本”不是。处理这个问题可能还有其他问题,但您标记的问题是关于用普通函数装饰一个方法,并且根本没有提到使用类时需要进行的调整。D
,作为一个类,是可调用的,但它不是您定义的装饰器。这就是D()
(除了将函数修饰为方法所引起的描述符问题)。