Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用类作为装饰器?_Python - Fatal编程技术网

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()
(除了将函数修饰为方法所引起的描述符问题)。