我在包装器类上遇到问题,无法弄清楚我在做什么错。 我该如何使该包装器与带有'self'参数的任何类函数一起使用?
这是针对Python 3.7.3的。 事情是我记得包装器以前工作过,但是似乎有些变化了……也许我只是做错了,因为我以前没做过。
class SomeWrapper:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# this fails because self is not passed
# ERROR: __init__() missing 1 required positional argument: 'self'
func_ret = self.func(*args, **kwargs)
# this is also wrong, because that's the wrong "self"
# ERROR: 'SomeWrapper' object has no attribute 'some_func'
# func_ret = self.func(self, *args, **kwargs)
return func_ret
class SomeClass:
SOME_VAL = False
def __init__(self):
self.some_func()
print("Success")
@SomeWrapper
def some_func(self):
self.SOME_VAL = True
def print_val(self):
print(self.SOME_VAL)
SomeClass().print_val()
答案 0 :(得分:0)
因此,在python 3中发生的事情是,对于方法声明而言,方法声明作为方法工作,当它们仅在类体内定义为函数时,发生的事情是该语言使用了“描述符协议”。
简单地说,普通方法只是一个函数,直到从实例中检索它为止:由于函数具有__get__
方法,因此它们被重新构造为描述符,而__get__
方法被重新定义为描述符。是负责返回“部分函数”(即“绑定方法”)的函数,将在调用时插入self
参数。如果没有__get__
方法,则从实例中检索SomeWrapper
的实例时,将不会获得该实例的信息。
简而言之,如果要对方法使用基于类的装饰器,则不仅必须编写__call__
,还必须编写__get__
方法。这样就足够了:
from copy import copy
class SomeWrapper:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
func_ret = self.func(self.instance, *args, **kwargs)
return func_ret
def __get__(self, instance, owner):
# self here is the instance of "somewrapper"
# and "instance" is the instance of the class where
# the decorated method is.
if instance is None:
return self
bound_callable = copy(self)
bould_callable.instance = instance
return self
代替复制装饰器实例,这也可以:
from functools import partial
class SomeWrapper:
...
def __call__(self, instance, *args, **kw):
...
func_ret = self.func(instance, *args, **kw)
...
return func_ret
def __get__(self, instance, owner):
...
return partial(self, instance)
“部分”和“自我”副本都是可调用的,它们“知道”它们来自哪个实例的“ __got__
”。
只需在装饰器实例中设置self.instance
属性,然后返回self
即可,但仅限于一次使用的方法的单个实例。在具有某种程度的并行性的程序中,或者即使代码将检索延迟地调用它的方法(例如,将其用于回调),它也会以引人注目的且难以调试的方式失败,因为该方法将收到 参数中的另一个实例。