Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 用可调用实例装饰类函数_Python_Class_Decorator - Fatal编程技术网

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>>