tl; dr
您可以通过将
Timed类作为描述符并返回部分应用的函数来解决此问题,该函数从中
__get__应用
Test对象作为参数之一,如下所示
class Timed(object): def __init__(self, f): self.func = f def __call__(self, *args, **kwargs): print(self) start = dt.datetime.now() ret = self.func(*args, **kwargs) time = dt.datetime.now() - start ret["time"] = time return ret def __get__(self, instance, owner): from functools import partial return partial(self.__call__, instance)
实际问题
引用Python文档作为 decorator ,
装饰器语法只是语法糖,以下两个函数定义在语义上是等效的:
def f(...): ...f = staticmethod(f)@staticmethoddef f(...): ...
所以,当你说
@Timeddef decorated(self, *args, **kwargs):
它实际上是
decorated = Timed(decorated)
只有函数对象传递给了
Timed, 实际绑定到的对象并没有传递给它 。因此,当您像这样调用它时
ret = self.func(*args, **kwargs)
self.func将引用未绑定的函数对象,并将其
Hello作为第一个参数调用。这就是为什么
self打印为的原因
Hello。
我怎样才能解决这个问题?
由于您在中没有引用
Test实例
Timed,因此唯一的方法是将其转换
Timed为 描述符类
。引用文档,“调用描述符”部分,
一般而言,描述符是与“结合行为”,一个属性的访问已被覆盖通过在描述符协议方法的对象属性:
__get__(),__set__(),和__delete__()。如果为对象定义了这些方法中的任何一种,则称其为描述符。属性访问的默认行为是从对象的字典中获取,设置或删除属性。例如,
a.x有一个查找链,从a.__dict__['x'],然后到type(a).__dict__['x'],并一直到type(a)排除元类的基类。但是, 如果查找到的值是定义描述符方法之一的对象,则Python可能会覆盖默认行为并改为调用描述符方法 。
我们可以
Timed通过简单地定义一个这样的方法来制作一个描述符
def __get__(self, instance, owner): ...
在这里,
self是指
Timed对象本身,
instance是指在其上发生属性查找的实际对象,并且
owner是指与对应的类
instance。
现在,在
__call__上调用时
Timed,该
__get__方法将被调用。现在,以某种方式,我们需要将第一个参数传递为
Testclass的实例(甚至在之前
Hello)。因此,我们创建了另一个部分应用的函数,其第一个参数将是
Test实例,如下所示
def __get__(self, instance, owner): from functools import partial return partial(self.__call__, instance)
现在,
self.__call__是一个绑定方法(绑定到
Timed实例),第二个参数
partial是
self.__call__调用的第一个参数。
所以,所有这些都像这样有效翻译
t.call_deco()self.decorated("Hello", world="World")
现在
self.decorated实际上是
Timed(decorated)(
TimedObject从现在开始将被称为)对象。每当我们访问它时,其中
__get__定义的方法都会被调用并返回一个
partial函数。您可以像这样确认
def call_deco(self): print(self.decorated) self.decorated("Hello", world="World")
会打印
<functools.partial object at 0x7fecbc59ad60>...
所以,
self.decorated("Hello", world="World")
被翻译成
Timed.__get__(TimedObject, <Test obj>, Test.__class__)("Hello", world="World")
由于我们返回了一个
partial函数,
partial(TimedObject.__call__, <Test obj>)("Hello", world="World"))
实际上是
TimedObject.__call__(<Test obj>, 'Hello', world="World")
因此,它
<Test obj>也成为的一部分
*args,并且在
self.func被调用时,第一个参数将是
<Test obj>。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)