[英]Decorate method with decorator class
我正在尝试为方法创建包装。 装饰器本身被实现为覆盖__call__
方法的类。
这适用于修饰函数,但是如果以这种方式修饰方法,则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不会像普通方法那样自动传递给__call__
调用:
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'
问题:如何将返回的可调用包装器对象绑定到self
? 将对象包装在函数中以让python进行绑定是一种选择,但是在我的情况下不是这样,因为需要实际的对象。
问题如何用装饰器类装饰实例方法? 类似于我要实现的目标。 如果仅需要绑定,则此处提供的解决方案有效。 我需要的是,访问属性时返回的对象实例是Wrapper
类型,而不是functools.partial
类型。
在第二个问题的答案也不起作用,因为通过返回的实例__get__
是类型的method
,而不是Wrapper
。
以下文章通过简单地将对象包装在函数中来解决该问题,以便python可以将self
与本机机制绑定: 装饰方法(类方法重载)
正如问题中提到的那样,这不是我的选择。 该帖子还提到了替代方法:为Wrapper
类实现描述符协议。 装饰方法的问题也解决了这个问题。 那里的问题是,那里使用的方法仅适用于该类的一个实例。
我想出了以下解决方案:
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)}")
区别在于,函数描述符的__get__
方法为每个(实例,所有者)对创建一个新的Wrapper实例。 通过这种方式,包装程序始终始终仅绑定到一个实例,甚至是类本身。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.