栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何用装饰器类装饰实例方法?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何用装饰器类装饰实例方法?

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__
方法将被调用。现在,以某种方式,我们需要将第一个参数传递为
Test
class的实例(甚至在之前
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>



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/648921.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号