如何将类属性传递给方法装饰器?

时间:2017-10-11 17:00:25

标签: python python-3.x

我正在尝试创建一个将发出api请求的类,根据传递给retrying.retry装饰器的配置选项重试,并以正确的方式为每个作业处理不同的错误代码。

这是我的代码:

from retrying import retry


class APIRequester:
    def __init__(self, url, **kwargs):
        self.url = url
        self.retry_kwargs = kwargs


    @retry(**self.retry_kwargs) # Obviously doesn't know what self is
    def make_request(self):
        pass

如何将参数传递给此方法装饰器?我试过让它们成为一个类属性,它也不起作用。

4 个答案:

答案 0 :(得分:3)

几个问题:

  1. 您是否知道@retry装饰器将在创建课程时应用于make_request方法 ,而retry_kwargs将会仅在创建类的实例时才可用?

    您是否知道前者必须先于后者?

    在哪种情况下,您是否看到前者不能依赖后者可用的信息? ... 只要您使用装饰器语法 ...

  2. 您是否知道装饰器语法

    @decorator
        def xxx(...):
        ...
    

    只是

    的语法糖
    def xxx(...):
        ...
    xxx = decorate(xxx)
    

    如果您意识到这一点,以及Python非常动态,您可以通过执行类似

    的操作来强制解决问题
    class APIRequester:
        def __init__(self, url, **kwargs):
            self.url = url
            self.retry_kwargs = kwargs
            APIRequester.make_request = retry(**kwargs)(APIRequester.make_request)
    
        def make_request(self):
            pass
    
  3. 这个特定的装饰者是否会在self参数上窒息,我不能告诉你。

    您是否有多个APIRequester个实例?如果是这样,请注意每次创建新实例时都会重新修饰该方法:这可以合理地工作吗? (我对此表示怀疑。)但请看下面的编辑......

    如果你没有更多的那个实例,那么你可能不需要依赖单身人士建造时可用的信息。

    以上是一些通用的Python原则。我怀疑你真的希望在这种情况下强制解决问题。在我看来,你试图以一种不被设计使用的方式使用装饰器。

    编辑:instancemethods

    如果用

    替换在构造函数中进行装饰的行
    self.make_request = retry(**kwargs)(self.make_request)
    

    然后每个实例都将获得自己的函数装饰版本。这应该避免重新装饰相同功能的任何问题。 self可能会遇到问题。在这种情况下,您可以从定义中删除self参数并使用staticmethod包装它:

    self.make_request = retry(**kwargs)(staticmethod(self.make_request))
    

    或者更好的是,使用装饰器语法将staticmethod应用于make_request到你定义它的地方,就像Guido所说的那样。

    像这样,它甚至有机会工作! : - )

答案 1 :(得分:1)

当然,在调用时可以在装饰器中使用self。查看How to decorate a method inside a class?的答案,我的答案就在这里:

def my_retry(fn):
    from functools import wraps
    @wraps(fn)
    def wrapped(self):
        print(self.retry_kwargs)
        for i in range(self.retry_kwargs["times"]):
            # you have total control
            fn(self)
            # around your method. Can even call it multiple times,
            # call with original retry: 
        retry(**self.retry_kwargs)(fn)(self)
        # check exceptions, return some value (here None), etc
        # 
    return wrapped

class APIRequester(object):
    def __init__(self, url, **kwargs):
        self.url = url
        self.retry_kwargs = kwargs

    @my_retry
    def make_request(self):
        print("method")

a = APIRequester('http://something', times=3)
a.make_request()

也就是说,原始装饰器被一个新的,配置感知的装饰器包裹起来。无需更改构造函数,语法仍然很简单。

答案 2 :(得分:1)

Decorator只是func=decorator(func)的语法糖。你可以自己完成任务:

class APIRequester:
    def __init__(self, url, **kwargs):
        self.url = url
        self.make_request = retry(**kwargs)(self.make_request)

    def make_request(self):
        pass

这将在内部用函数替换方法(描述符),但它将按预期工作。

答案 3 :(得分:0)

重试装饰器不支持类方法,因为类的实例被隐式传递给function。 请装饰正常功能。 如果要将函数包装到类中,请装饰静态方法。