得到自我的装饰者

时间:2017-09-20 21:12:31

标签: python

我试图编写一个可以添加到实例方法和非实例方法的装饰器。我已将代码缩减到最低限度的示例,以证明我的观点

def call(fn):
    def _impl(*args, **kwargs):
        return fn(*args, **kwargs)

    fn.call = _impl

    return fn

class Foo(object):
    @call
    def bar(self):
        pass

Foo().bar.call()

这给出了漂亮的错误

Traceback (most recent call last):
  File "/tmp/511749370/main.py", line 14, in <module>
    Foo().bar.call()
  File "/tmp/511749370/main.py", line 3, in _impl
    return fn(*args, **kwargs)
TypeError: bar() missing 1 required positional argument: 'self'

是否可以在不诉诸

的情况下做这样的事情
Foo.bar.call(Foo())

或者这是我唯一的选择吗?

1 个答案:

答案 0 :(得分:2)

您必须将装饰器实现为类并实现descriptor protocol。基本上,描述符__get__函数负责创建绑定方法。通过覆盖此函数,您可以访问self并可以创建call函数的绑定副本。

以下实现就是这样做的。 Foo实例保存在__self__属性中。装饰器有一个调用装饰函数的__call__方法,以及一个执行相同操作的call方法。

import inspect
import functools
from copy import copy

class call:
    def __init__(self, func):
        self.func = func
        self.__self__ = None # "__self__" is also used by bound methods

    def __call__(self, *args, **kwargs):
        # if bound to on object, pass it as the first argument
        if self.__self__ is not None:
            args = (self.__self__,) + args

        return self.func(*args, **kwargs)

    def call(self, *args, **kwargs):
        self(*args, **kwargs)

    def __get__(self, obj, cls):
        if obj is None:
            return self

        # create a bound copy of the decorator
        bound = copy(self)
        bound.__self__ = obj

        # update __doc__ and similar attributes
        functools.wraps(bound.func)(bound)
        bound.__signature__ = inspect.signature(bound.func)

        # add the bound instance to the object's dict so that
        # __get__ won't be called a 2nd time
        setattr(obj, self.func.__name__, bound)

        return bound

测试:

class Foo(object):
    @call
    def bar(self):
        print('bar')

@call
def foo():
    print('foo')

Foo().bar.call() # output: bar
foo() # output: foo