How to pass self attributes to a decorator?



I have a decorator for an object method:

def wrapper(func):
  def _wrapper(self, *a, **kw):
    print(self.foo)
    return func(self, *a, **kw)
  return _wrapper


class Bar:
  foo = 1
  
  @wrapper
  def baz(self):
    print(2)

# This works as expected :)
In [2]: Bar().baz()
1
2

I want to add another level of wrapping around this to allow parameters:

def wrapper(bla):
  def _wrapper(func):
    def __wrapper(self, *a, **kw):
      print(self.foo + bla)
      return func(self, *a, **kw)
    return __wrapper
  return _wrapper

class Bar:
  foo = 1

  @wrapper(1)
  def baz(self):
    print(2)

# which also works great
In [4]: Bar().baz()
2
2

Now, what I want to be able to do is pass a self attribute as a wrapper parameter, i.e. call it with something like this

class Bar:
  def __init__(self):
    self._foo = 1

  @wrapper(self._foo)
  def baz(self):
    pass

I see 2 options:

  1. make self._foo global
  2. don’t pass self._foo, and make the decorator access self._foo

Both are bad. (global can be changed from another place without my knowledge, and making the decorator access self._... means the decorator knows the class it operates on) Is there a good way to do this?

Answer

This example works just fine (You can pick _foo or _bar) and the wrapper itself knows nothing of Bar nor its members:

def wrapper(bla):
  def _wrapper(func):
    def __wrapper(self, *a, **kw):
      print(getattr(self, bla))
      return func(self, *a, **kw)
    return __wrapper
  return _wrapper

class Bar:
  def __init__(self):
    self._foo = 1
    self._bar = 2

  @wrapper("_bar")  # pick _foo or _bar here
  def baz(self):
    pass


Bar().baz()

Output: 2

The wrapper decorator can be located in another module, or as part of a library and Bar as the client will be unknown to the decorator.



Source: stackoverflow