Python 用decorator类修饰方法

Python 用decorator类修饰方法,python,Python,我正在尝试为方法创建一个包装器。 装饰器本身被实现为一个覆盖\uuuu调用\uuu方法的类 这适用于修饰函数,但如果以这种方式修饰方法,self不会绑定到修饰函数 以下是一个简单的示例: import functools class Wrapper: def __init__(self, func) -> None: super().__init__() self.__func = func functools.update_w

我正在尝试为方法创建一个包装器。 装饰器本身被实现为一个覆盖
\uuuu调用\uuu
方法的类

这适用于修饰函数,但如果以这种方式修饰方法,
self
不会绑定到修饰函数

以下是一个简单的示例:

import functools


class Wrapper:

    def __init__(self, func) -> None:
        super().__init__()
        self.__func = func
        functools.update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        print("PreBar")
        result = self.__func(*args, **kwargs)
        print("PostBar")
        return result


def MyDecorator(func):
    return Wrapper(func)


class Foo:
    baz = "Baz"

    @MyDecorator
    def bar(self):
        print(self.baz)
        return self.baz


if __name__ == '__main__':
    foo = Foo()
    print(foo.bar)
    print(f"Result of foo.bar(): {foo.bar()}")

    print(Foo.bar)
    print(f"Result of Foo.bar(foo): {Foo.bar(foo)}")
这会产生以下错误,这意味着self不会像普通方法那样自动传递到
\uuuu调用\uuuu
调用:

Traceback (most recent call last):
  File "/mypath/test.py", line 35, in <module>
    print(f"Result of foo.bar(): {foo.bar()}")
  File "/mypath/test.py", line 14, in __call__
    result = self.__func(*args, **kwargs)
TypeError: bar() missing 1 required positional argument: 'self'
回溯(最近一次呼叫最后一次):
文件“/mypath/test.py”,第35行,在
打印(f“foo.bar()的结果:{foo.bar()}”)
文件“/mypath/test.py”,第14行,在调用中__
结果=自身函数(*args,**kwargs)
TypeError:bar()缺少1个必需的位置参数:“self”
问题:如何将返回的可调用包装器对象绑定到
self
? 将对象包装在函数中以让python进行绑定是一种选择,但在我的情况下不是这样,因为实际的对象是必需的

这个问题与我想要实现的目标类似。如果只需要绑定,那里提供的解决方案就可以工作。但我需要的是,访问属性时返回的对象实例的类型是
Wrapper
,而不是
functools.partial


由于
\uuuu get\uuuu
返回的实例属于
方法类型,而不是
包装器
,因此,下面的文章通过简单地将对象包装在函数中来解决问题,这样python就可以用本机机制绑定
self

正如问题中提到的,这不是我的选择。文章还提到了另一种选择:为
包装器
类实现描述符协议。这个问题也解决了这个问题。
问题是,这里使用的方法只适用于类的一个实例

我提出了以下解决方案:

import functools


class Wrapper:

    def __init__(self, func, instance=None) -> None:
        super().__init__()
        self.__func = func
        self.__instance = instance
        self.__bindings = {}
        functools.update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        print("PreBar")
        if self.__instance is not None:
            args =(self.__instance, ) + args
        result = self.__func(*args, **kwargs)
        print("PostBar")
        return result

    def __get__(self, instance, owner):
        try:
            return self.__bindings[(instance, owner)]
        except KeyError:
            binding = self.__bindings[(instance, owner)] = Wrapper(self.__func, instance)
            return binding


def MyDecorator(func):
    return Wrapper(func)


class Foo:
    baz = "Baz"

    @MyDecorator
    def bar(self):
        print(self.baz)
        return self.baz


if __name__ == '__main__':
    foo = Foo()
    print(foo.bar)
    print(f"Result of foo.bar(): {foo.bar()}")

    print(Foo.bar)
    print(f"Result of Foo.bar(foo): {Foo.bar(foo)}")
不同之处在于,函数描述符的
\uuuu get\uuu
方法为每个(实例、所有者)对创建一个新的包装器实例。
这样,包装器总是只绑定到一个实例,甚至类本身。

可能重复
MyDecorator
的要点是什么?你可以直接用
Wrapper
装饰
bar
。@chepner你说得对。我本可以省略这个函数作为我的最小示例。原始代码使用MyDecorator类,因为decorator接受参数。