用可调用实例装饰类函数

2024-09-28 03:15:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用一个可调用类的实例替换它来装饰函数:

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

这和预期的一样。在

但是,只要我尝试装饰一个类方法:

^{pr2}$

我得到一个TypeError: dosomething() takes exactly 1 argument (0 given)。在

现在,我想I can answer the why

To support method calls, functions include the __get__() method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound or unbound methods depending whether they are invoked from an object or a class.

因此,FunctionFaker不是一个函数,没有所述描述符,因此不会弄乱参数。在

如何实现能够替换实例方法的可调用类?在


Tags: 实例函数selfobjectdefargsfunction装饰
2条回答

I just realized I can simply implement __get__ and return a types.MethodType, but I don't really understand how doing so still enables you to call empty_function.

这是因为MethodType有一个__getattribute__方法,该方法将未知属性委托给其im_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>>

当然,在CPython中,capi并没有完全反映出__getattribute__和{}之间的Python级别的区别,因此它的真正的实现方式是使用一个定制的^{}槽。您可以阅读详细信息in the source。在

Does it simply become an attribute of the MethodType instance?

是的,但只是动态的,通过给你底层可调用的属性。在

我不认为它们是专门打算用类实例来代替具有自己的方法描述符的函数。但即使是将属性附加到方法的简单情况,也需要这种支持。例如,对于一个独立的函数,您可以使用函数属性,例如,记忆缓存,或延迟初始化第一次调用设置。如果MethodType没有将属性访问委托给它的im_func对象,那么将这样一个函数移动到一个类中会破坏它,并且开发人员将无法修复它,除非他知道描述符是如何工作的,并且以一种难看的方式重写了方法。在

事实上,在2.3之前,方法甚至没有__dict__;正如您从the source看到的那样,除了C槽之外的所有属性都被委托给im_func(通过有效地复制正常的机制,将所有的东西都委托给im_func,但会出现包装错误)。关于这个问题有一些争论,你可以通过搜索python dev档案找到christismer在2.4之前的时期发表的一篇文章,文章主题相关(可能是this thread,但我没有读到全部内容…)。从2.4开始,方法现在执行正常的查找机制(除了__doc__的特殊情况),并且只有在失败时才委托给im_func。在

And is this a sane thing to do?

这有点奇怪,也许empty_function属性添加到function对象中会更简单,而不是将其包装在一个类中……但我认为这并不太合理。(我假设您询问的是您的代码,而不是MethodType和描述符是如何实现的。)

你在正确的轨道上。您希望您的类也是descriptor

import types

class FunctionFaker( object ):
    def __init__(self, f):
        self.f= f

    def empty_function(self):
        pass

    def __call__(self, *args, **kwargs):
        self.empty_function()
        self.f(*args, **kwargs)

    def __get__(self, instance, cls=None):
        # see https://docs.python.org/2/howto/descriptor.html#functions-and-methods
        return types.MethodType(self, instance, cls)

@FunctionFaker
def foo(arg1):
  print "in foo", arg1

class Bar(object):
  @FunctionFaker
  def bar(self, arg1):
    print "in bar", self, arg1

foo('hello')  # in foo hello
Bar().bar('world')  # in bar <__main__.Bar object at 0x7fc50b90fb10> world
Bar().bar.empty_function()

现在,如果您的decorator绑定到一个类,它的行为类似于一个描述符(在decorated函数中将适当的实例绑定到self),如果它没有绑定到一个类,它的行为就像一个普通的装饰器。整洁。在

相关问题 更多 >

    热门问题