首页 > 解决方案 > 如何动态修饰类方法,同时不丢失方法绑定状态?

问题描述

假设我有一个这样的装饰器:

def repeat(repeat_count):
    def decorator(func):
        def wrapped(self):
            for X in range(repeat_count):
                 func() # Do Function
        return wrapped
    return decorator

和这样的课

class SomeClass(object):
    def __init__(self, do_count):
        self.some_method = repeat(do_count)(self.some_method)

    def some_method(self): 
        print("I'm Doing Something")

因为装饰器只返回一个方法,所以很明显这是可行的。但是它从类实例中取消绑定 some_method 函数,所以我不能再做类似的事情:

>>> sc = SomeClass(10)
>>> sc.some_method()
# TypeError: wrapped() missing 1 required positional argument: 'self'

我得到一个例外,因为 self 不再自动传递。为了完成这项工作,我可以简单地这样做:

sc.some_method(sc)

但我宁愿不这样做。有没有办法将方法重新绑定到实例,最好没有任何额外的导入(如 TypeMethod)就可以完成。

标签: pythonpython-3.x

解决方案


我得到一个例外,因为 self 不再自动传递。

实际上,它仍然是自动通过的。你得到这个异常是因为你定义装饰器的方式要求它被传递两次。

从包装的运行时内,func已经绑定(即它已经自我传递)。通过定义wrapped接受一个位置参数,您需要self再次传入,这就是sc.some_method(sc)正常工作的原因。根据self需要传递了两次 - 一次是隐式的,一次是显式的。

对代码的最小修复是self从 的签名中删除wrapped,因为它已经根据绑定中的描述符协议隐式传递self.some_method

def repeat(repeat_count):
    def decorator(func):
        def wrapped():
            for X in range(repeat_count):
                 func() # Do Function
        return wrapped
    return decorator

然而,这并不是最好的解决方案。您会想要接受*args**kwargs因此无论装饰函数的签名如何,都可以应用您的装饰器:

def repeat(repeat_count):  # <-- the "decorator maker"
    def decorator(func):  # <-- the decorator
        def wrapped(*args, **kwargs):  # <-- this will replace "func"
            for X in range(repeat_count):
                 func(*args, **kwargs)  # <-- note: pass along the arguments!
        return wrapped  # <-- note: no call here!
    return decorator

推荐阅读