使用类作为装饰器?

2024-04-28 22:13:20 发布

您现在位置: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() missing 1 required positional argument: 'x'

发生了什么,有没有一种方法可以像这样使用类作为装饰器


Tags: selfreturninitdefrequiredargspasscall
1条回答
网友
1楼 · 发布于 2024-04-28 22:13:20

问题是,除了decorator机制之外,Python还使用了一种机制,使类主体内的函数像实例方法一样工作:它是"descriptor protocol"。这实际上很简单:所有函数对象都有一个__get__方法(而不是__set____del__)方法,这使它们成为“非数据描述符”。当Python从实例检索属性时,将使用实例作为参数调用__get__,即__get__方法。它们必须返回一个可调用的对象,该对象将用作方法,并且必须知道调用的实例是哪一个:


# example only - DO NOT DO THIS but for learning purposes,
#  due to concurrency problems:

class D:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        
        return self.func(self.instance, *args, **kwargs)

    def __get__(self, instance, owner):
        self.instance = instance
        return self

class A:
    @D
    def f(self, x):
        print(self, x)

a=A()
a.f(1)

这将打印“<;main。0x…>;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()”

为了避免这种情况,您必须返回一个可从__get__调用的,它不需要将实例作为类属性检索。一种方法是创建一个partial可调用的函数并将其包含在内-但是,请注意,由于这是一个必要的步骤,因此装饰器类本身不需要在__call__方法中具有“run wrapper+original code”函数-它可以有任何名称:

from functools import partial

class D:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, _instance=None, **kwargs):
        if _instance:
            return self.func(_instance, *args, **kwargs)
        else: 
            return self.func(*args, **kwargs)
        

    def __get__(self, instance, owner):
        return partial(self.__call__, _instance=instance)
      

class A:
    @D
    def f(self, x):
        print(self, x)

a=A()
a.f(1)

相关问题 更多 >