Python 用可调用实例装饰类函数
我试图用一个可调用类的实例替换函数来修饰它:Python 用可调用实例装饰类函数,python,class,decorator,Python,Class,Decorator,我试图用一个可调用类的实例替换函数来修饰它: class FunctionFaker( object ): def __init__( self, f ): self.f= f def empty_function( self ): pass def __call__( self, *args, **kwargs ): self.f( *args, **kwargs) def fakefunction( f ):
class FunctionFaker( object ):
def __init__( self, f ):
self.f= f
def empty_function( self ):
pass
def __call__( self, *args, **kwargs ):
self.f( *args, **kwargs)
def fakefunction( f ):
'''a decorator that transforms a function into a FunctionFaker'''
return FunctionFaker(f)
@fakefunction
def dosomething():
pass
dosomething.empty_function()
dosomething()
这正如预期的那样有效
但是,只要我尝试装饰一个类方法:
class Test( object ):
@fakefunction
def dosomething(self):
pass
t=Test()
t.dosomething.empty_function()
t.dosomething()
我得到一个TypeError:dosomething()正好接受一个参数(给定0)
现在,我想:
为了支持方法调用,函数包括用于在属性访问期间绑定方法的\uuu get\uuu()
方法。这意味着所有函数都是非数据描述符,它们返回绑定或未绑定的方法,这取决于它们是从对象还是从类调用的
因此,FunctionFaker不是一个函数,没有上述描述符,因此不会弄乱参数
如何实现一个能够替换实例方法的可调用类?您的思路是正确的。您希望您的班级也成为:
导入类型
类FunctionFaker(对象):
定义初始化(self,f):
self.f=f
def清空功能(自身):
通过
定义调用(self,*args,**kwargs):
self.empty_函数()
self.f(*args,**kwargs)
def uuu get uuu(self,instance,cls=None):
#看https://docs.python.org/2/howto/descriptor.html#functions-方法
返回类型.MethodType(self、instance、cls)
@假货
def foo(arg1):
打印“在foo中”,arg1
类栏(对象):
@假货
def栏(自身,arg1):
打印“条内”,自我,arg1
foo(“hello”)#在foo hello中
Bar().Bar(“世界”)#在酒吧世界中
Bar().Bar.empty_函数()
现在,如果您的装饰器绑定到一个类,那么它的行为就像一个描述符(将适当的实例绑定到装饰函数中的self
),如果它没有绑定到一个类,那么它的行为就像一个普通的装饰器。整洁
我刚刚意识到我可以简单地实现
\uuu get\uu
并返回types.MethodType
,但我不太明白这样做如何仍然可以让您调用empty\u函数
这是因为MethodType
有一个\uuuu getattribute\uuuu
方法将未知属性委托给它的im\u func
:
>>> t.dosomething
<bound method Test.? of <__main__.Test object at 0x107639550>>
>>> 'empty_function' in dir(t.dosomething)
False
>>> t.dosomething.__getattribute__
<method-wrapper '__getattribute__' of instancemethod object at 0x109583aa0>
>>> t.dosomething.__getattribute__('empty_function')
<bound method FunctionFaker.empty_function of <__main__.FunctionFaker object at 0x1095f2510>>
>t.dosomething
>>>目录中的“空函数”(t.dosomething)
假的
>>>t.dosomething.\uu getattribute__
>>>t.dosomething.\uuuu getattribute\uuuu('空函数')
当然,在CPython中,C API并不完全反映\uuuu getattribute\uuuuu
和\uuuu getattr\uuuuu
之间Python级别的区别,因此它真正的实现方式是使用自定义插槽。您可以阅读详细信息
它只是成为MethodType
实例的一个属性吗
是的,但只有动态地,通过给您底层callable的属性
我认为它们并不是专门用来让类实例用自己的方法描述符代替函数的。但即使是将属性附加到方法的简单情况也需要这种支持。例如,对于独立函数,您可以使用函数属性,例如,记忆缓存或第一次调用时惰性初始化设置。如果MethodType
没有将属性访问权委托给它的im_func
对象,将这样的函数移动到类中会破坏它,开发人员将无法修复它,除非他知道描述符是如何工作的,并以一种丑陋的方式重写方法
事实上,在2.3之前,方法甚至没有一个\uuu dict\uuuu
;从中可以看到,除了C插槽之外的所有属性都被委托给了im_func
(通过有效地复制正常机制,将所有属性委托给im_func
,但包装错误)。关于这一点有一些争论,你可以在python开发文档中搜索Christian Tismer在2.4版本之前发表的一篇文章,其中有一个相关的主题(可能是,但我没有阅读全部内容……)。从2.4开始,方法现在执行正常的查找机制(除了\uuuuu doc\uuuuu
的特殊情况),并且只有在失败时才委托给im\u func
这是明智的做法吗
这有点奇怪,将
empty_function
属性添加到function对象可能更简单,而不是将其包装到类中……但我认为这并不太不合理。(我假设你问的是你的代码,而不是MethodType
和描述符是如何实现的。)我刚刚意识到我可以简单地实现\uuuuu get\uuuuu
并返回类型。MethodType
,但我真的不明白这样做怎么还能让你调用空函数
。它只是成为MethodType
实例的一个属性吗?这是明智的做法吗?签名是types.MethodType(callable,instance,class)
,您可以传递任何类型的callable,包括具有\uuu call\uuu
函数的类的实例。不,这实际上不起作用;如果他返回一个partial
而不是MethodType
,empty_函数
将引发一个AttributeError
@abarnert——我不确定OP从哪里调用empty_函数,但它似乎在我的测试中起作用。他已经在创建一个描述符,返回一个types.MethodType
,与普通函数一样,与文档明确建议的一样。因为MethodType
有一个委托\uuu getattribute\uuuu
,t.dosomething.empty\u函数()
起作用。通过您的更改,t.dosomething
现在是一个partial
,因此t.dosomething.empty_函数()
将在上引发一个AttributeError
@abanert点。但在这一点上,我真的想知道这是否会work@abarnert--啊,现在我知道奥普想要什么了。我没有意识到目标是为函数添加属性。感谢“正如文件明确推荐的那样”。在描述文档中我不记得了——遗憾的是
>>> t.dosomething
<bound method Test.? of <__main__.Test object at 0x107639550>>
>>> 'empty_function' in dir(t.dosomething)
False
>>> t.dosomething.__getattribute__
<method-wrapper '__getattribute__' of instancemethod object at 0x109583aa0>
>>> t.dosomething.__getattribute__('empty_function')
<bound method FunctionFaker.empty_function of <__main__.FunctionFaker object at 0x1095f2510>>