编写一个装饰器来应用另一个带有参数的装饰器,用于类

时间:2017-02-26 21:53:05

标签: python python-2.7 python-decorators

来自此post。接受的答案适用于不带参数的装饰器。我正在尝试扩展此解决方案,使其为应用装饰器提供参数。

详细地说,我有进行外部api调用的函数。因为这些调用经常失败,所以我将此library的重试装饰器应用于所有函数。为了避免一次又一次地为所有函数添加@retry(...)行,我决定将它们集中在一个类中。我创建了RetryClass并将所有函数作为classmethod放在类中。现在,我正在寻找一种方法来为类的所有方法应用retry装饰器,这样我就可以继续在类中添加新方法,它将自动应用retry装饰器对于新方法。

注意:重试装饰器接受参数。

@retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3)

这是我的代码:

from retrying import retry


def for_all_methods(decorator):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)):
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate


@for_all_methods(retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3))
class RetryClass(object):

    @classmethod
    def a(cls):
        pass


def test():
    RetryClass.a()
    return

这会引发以下错误:

Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1596, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 974, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Users/gyoho/Datatron/Dev/class-decorator/main.py", line 26, in <module>
    test()
  File "/Users/gyoho/Datatron/Dev/class-decorator/main.py", line 22, in test
    RetryClass.a()
TypeError: unbound method a() must be called with RetryClass instance as first argument (got nothing instead)

然而,注释掉类装饰器运行没有错误。有什么我想念的吗?

2 个答案:

答案 0 :(得分:1)

问题是@classmethod不再是a()的顶级装饰器。 RetryClass.a目前按此顺序装饰@classmethod@retryRetryClass相当于:

class RetryClass(object):

    @retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3)
    @classmethod
    def a(cls):
        pass

你的课程必须等同于:

class RetryClass(object):

    @classmethod
    @retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3)
    def a(cls):
        pass

答案 1 :(得分:0)

classmethodstaticmethod必须是方法的最后一个装饰器,因为它们返回descriptors而不是函数。 (装饰比较棘手)。您可以检测方法是否已经是classmethodstaticmethod,然后您的decorate函数看起来会像这样:

def decorate(cls):
    for attr in cls.__dict__:
        possible_method = getattr(cls, attr)
        if not callable(possible_method):
            continue

        if not hasattr(possible_method, "__self__"):
            raw_function = cls.__dict__[attr].__func__
            decorated_method = decorator(raw_function)
            decorated_method = staticmethod(decorated_method)

        if type(possible_method.__self__) == type:
            raw_function = cls.__dict__[attr].__func__
            decorated_method = decorator(raw_function)
            decorated_method = classmethod(decorated_method)

        elif possible_method.__self__ is None:
            decorated_method = decorator(possible_method)

        setattr(cls, attr, decorated_method)

    return cls