如何在不丢失方法绑定状态的情况下动态修饰类方法?

时间:2018-07-27 16:44:23

标签: python python-3.x

说我有一个这样的装饰器:

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'

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

sc.some_method(sc)

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

2 个答案:

答案 0 :(得分:2)

  

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

实际上,它仍然会自动传递。之所以会出现此异常,是因为您定义装饰器的方式要求将其传递两次。

在包装的运行时中,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

答案 1 :(得分:1)

在一个非常简单的情况下,您不需要从装饰器本身访问self,您可以简单地使用以下命令(这几乎就是您已经做的事,减去包装函数的调用,以及self的通过)。 分配时,传递给装饰器的方法已经绑定到self

self.some_method = repeat(do_count)(self.some_method)

完整代码:

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


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

    def some_method(self): print("Accesing my a from inside: %d" % self.a)


sc = SomeClass(5)
sc.some_method()

输出:

Accesing my a from inside: 3
Accesing my a from inside: 3
Accesing my a from inside: 3
Accesing my a from inside: 3
Accesing my a from inside: 3