Search code examples
pythonpython-decorators

Initialize Decorator Variable in Class


I'm working with a decorator within a class that can accept a variable and I'd like to initialize that variable within the class.

Below is an example of what I'm currently working with (which is currently working).

_RETRIES = 3

class MyClass(object):
  def __init__(self, foo):
    self._foo=foo

  @retry.FuzzedIntervalsOnException(num_retries=_RETRIES)
  def my_method(self):
    return some_data

However I'd like to change this to accept something like def __init__(self, foo, retries=3) to allow clients to decide the number of retries. However implementing this is throwing an undefined variable error.

I'm hoping to get to something like the below, however it seems like the correct answer will be more complicated than this:

class MyClass(object):
  def __init__(self, foo, retries=3):
    self._foo=foo
    self.retries=retries

  @retry.FuzzedIntervalsOnException(num_retries=self.retries)
  def my_method(self):
    return some_data

Is there an ideal way to initialize a class variable to be used within a decorator?

Many thanks in advance

EDIT: For additional information on the retry, below is the initialization.

class FuzzedIntervalsOnException(object):
   """Retry on exception 

   args:
     delay: Time delay in seconds for each retry.
     num_retries: Total number of retries.
   """
   def __init__(self, delay, num_retries):
     ...

Solution

  • Your current retry probably looks something like this:

    def retry(num_retries=0):
        def decorator(fn):
            def wrapper(*args, **kwargs):
                # ...
            return wrapper
        return decorator
    

    You want to get self.retries, but you won't have a reference to self until wrapper is called. So the only way to do that is to get self.retries inside of wrapper. Since you aren't passing any arguments to the decorator, you can also get rid of a layer and do something like this:

    def retry(fn):
        def wrapper(self, *args, **kwargs):
            num_retries = self.retries
            # ...
        return wrapper