来自此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)
然而,注释掉类装饰器运行没有错误。有什么我想念的吗?
答案 0 :(得分:1)
问题是@classmethod
不再是a()
的顶级装饰器。 RetryClass.a
目前按此顺序装饰@classmethod
和@retry
。 RetryClass
相当于:
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)
classmethod
和staticmethod
必须是方法的最后一个装饰器,因为它们返回descriptors而不是函数。 (装饰比较棘手)。您可以检测方法是否已经是classmethod
或staticmethod
,然后您的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