0

In a previous question, someone informed me that I could use the getattr method to pass instance variables to a decorator function. For example, here I should be able to pass c to some_wrapper.

Here is the original question if anyone was interested. Passing instance variables to a decorator

import functools 

def some_func(
   a = False
  ,b = None
):
      def some_decorator(func):
          @functools.wraps(func)
          def some_wrapper(self, *args, **kwargs):
              c = getattr(self, 'c', None)

              ...


class MyClass:
    def __init__(self, arg, c=None):
        self.arg = arg
        self.c = c

    @some_func(a=True)    
    def foo(self, x, y):
        ...

Edit:

What the above allows me to do is pass c to some_wrapper when foo is passed into the decorator. So if I have another method like:

    @some_func(a=True)    
    def bar(self, alpha, beta):
        ...

self.c gets passed onto the other instance methods as well which is what I want.

instance = MyClass(arg, c = 'Hi')

instance.foo(arg, blarg)
instance.bar(alpha, beta)

However, I decided to go a different route and converted the function decorator into a class decorator. i.e:

class SomeClass():
    def __init__(self, *args, **kwargs):
        self.a = None
        self.b = None
       
    def __get__(self, obj, objtype):
        import functools
        return functools.partial(self.__call__, obj)
                

    def __call__(self, *args, **kwargs):
        def some_wrapper(*args, **kwargs):
            c = getattr(self, 'c', None)

        ...
        self.func = args[0]
        return some_wrapper

getattr doesn't work in the same way as I showed in the first example. My stab in the dark guess is that in my second example, the function is no longer passed as as argument anymore, so the self in getattr(self, 'c', None) no longer reference to the instance from the decorated function.

Is there a work around this?

dyao
  • 983
  • 3
  • 12
  • 25

1 Answers1

0

Works for me :

import functools


def some_func(a=False, b=None):
    def some_decorator(func):
        @functools.wraps(func)
        def some_wrapper(self: "MyClass", *args, **kwargs):
            kwargs.pop("y")
            c = self.c
            return func(self, *args, **kwargs, y=c)
        return some_wrapper
    return some_decorator


class MyClass:
    def __init__(self, arg, c=None):
        self.arg = arg
        self.c = c

    @some_func(a=True)
    def foo(self, x, y):
        print(f"foo x={x} y={y}")


no_c = MyClass("arg")
no_c.foo(x=3, y=4)
yes_c = MyClass("blarg", c="I do")
yes_c.foo(x=3, y=4)

output :

foo x=3 y=None
foo x=3 y=I do
Lenormju
  • 4,078
  • 2
  • 8
  • 22