0

I am trying to define a python decorator (my_decorator) for a class method (f), shown below in a simplified scenario. my_decorator is parametrized by param, which depends on the class attribute (in this case level).

class my_decorator:
    def __init__(self, param):
        self.param = param

    def __call__(self, f):
        def f_decorated(instance, c):
            print("decorated with param = %d" % self.param)
            return f(c)

        return f_decorated


class A:
    def __init__(self, level):
        self.level = level

    @my_decorator(param=self.level)   # Here is the problematic line!
    def f(x):
        return x

if __name__ == "__main__":
    a = A(level=2)
    a.f(1)   # name "self" is not defined 

The above code does not work, and I get a "self" is not defined error. So my question is, is there any way to achieve the goal of context-parametrized decorator?

BTW, the use case is: I am trying to achieve persistent memoization technique (described at memoize to disk - python - persistent memoization) The file where the cache persists to depends on the class A, specifically 'level'. For instance, I would like to persist to the file cache_%d.txt % self.level .

Community
  • 1
  • 1
K.Chen
  • 1,166
  • 1
  • 11
  • 18

3 Answers3

1

Chen,

Decorator are executed during the compiled time or during the import as the class body is executed during import. So, if you execute your snippet without creating an instance of that class also will throw error.

And more over that parameter self.level inside decorator doesn't make much sense to me as its an instance variable so you can directly use inside the function f(x):

Here is some more details:

Python decorator function called at compile time

Community
  • 1
  • 1
James Sapam
  • 16,036
  • 12
  • 50
  • 73
1

As the error says, self doesn't exist at that point. That should be clear to you: self only exists as a parameter to a method, and you're not even in a method at that time. Decorators, like all class-level code are evaluated at define time.

I'm not totally sure what you want to achieve, but you could use a string along with getattr:

class my_decorator:
    def __init__(self, param_name):
        self.param_name = param_name

    def __call__(self, f):
        def f_decorated(instance, c):
            param = getattr(instance, self.param_name)
            print("decorated with param = %d" % param)
            return f(c)

...

class A:
    def __init__(self, level):
        self.level = level

    @my_decorator(param_name='level')
    def f(x):
        return x
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
1

self is a variable as any. It's only defined inside of methods, the decorator is outside. If you need attributes of a object inside an decorator, you have the possibility to access it by string-name:

class my_decorator:
    def __init__(self, param):
        self.param = param

    def __call__(self, f):
        def f_decorated(instance, c):
            print("decorated with param = %d" % getattr(instance, self.param))
            return f(instance, c)

        return f_decorated


class A:
    def __init__(self, level):
        self.level = level

    @my_decorator(param='level')   # Here is the problematic line!
    def f(self, x):
        return x

if __name__ == "__main__":
    a = A(level=2)
    a.f(1)   # name "self" is not defined 
Daniel
  • 42,087
  • 4
  • 55
  • 81