如何让基于类的装饰器用于实例方法?

时间:2017-03-21 05:15:32

标签: python python-2.7 python-3.x decorator

有两种方法可以调用装饰器:1)通过传递装饰器函数/类

class Foo(object):
    def __init__(self):
        self.x = 1

    @foo_decorator
    def bar(self, x):
        return self.x + x

或2)通过传递装饰器函数/类实例的返回值。

class Foo(object):
    def __init__(self):
        self.x = 1

    @foo_decorator("args")
    def bar(self, x):
        return self.x + x

问题是:如果我需要使用基于类的方法实现装饰器,我如何让装饰器适用于这两种情况?

此类实施的一个例子是

import functools

class FooDecorator(object):
    def __init__(self, *args):
        if len(args) == 1 and callable(args[0]):
            self.f = args[0]
            functools.update_wrapper(self, args[0])
        else:
            self.f = None

    def __call__(self, *args, **kwargs):
        if self.f is not None:
            return self.f(*args, **kwargs)
        else:
            assert len(args) >= 1 and callable(args[0])
            f = args[0]
            self.f = f
            functools.update_wrapper(self, f)

            return self

foo_decorator = FooDecorator

但是,使用以下代码测试这个简单的实现会产生一些错误:

class Foo1(object):
    def __init__(self):
        self.x = 1

    @foo_decorator
    def bar(self, x):
        return self.x + x

class Foo2(object):
    def __init__(self):
        self.x = 1

    @foo_decorator("args")
    def bar(self, x):
        return self.x + x


Foos = [Foo1, Foo2]

for i in range(2):
    print(Foos[i].__name__)
    f = Foos[i]()
    print(f.bar(10))

Traceback (most recent call last):
  File "python", line 47, in <module>
  File "python", line 13, in __call__
TypeError: bar() missing 1 required positional argument: 'x'

已在python 2.7和3.5上测试过。

感谢任何帮助。请注意,我已经彻底搜索了整个网络,并且已经阅读了有关该主题的以下文章:

另请注意,根据我的知识,decorator包不支持引发装饰器的第二种方法(预初始化装饰器对象)。

1 个答案:

答案 0 :(得分:1)

参考和学分:http://www.ianbicking.org/blog/2008/10/decorators-and-descriptors.html

类方法是(非数据)描述符。如果方法在装饰期间被替换,则必须恢复描述符函数。

这适用于Python3。 kwargs未实现简单。

class Deco:
    def __init__(self, *args):
        if len(args) == 1 and callable(args[0]):
            self._func = args[0]
            self._dargs = None
        else:
            self._func = None
            self._dargs = args

    def __call__(self, *args):
        if self._func is None:
            self._func = args[0]
            return self
        print("decorated with: {}".format(self._dargs))
        return self._func(*args)

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        def bound_decorated(*args):
            return self.__call__(obj, *args)
        return bound_decorated

(无论如何,我更喜欢任何更简单的解决方案。)