2

I'm writing some code and I have a dictionary where the key is any string, and the value is a function. I then loop through each key in the dictionary and call the functions, like so:

class SomeClass:

    dictionary = {}

    # Not sure how to use this decorator function
    def decorator(key):
        def wrapper(funct):
            self.dictionary[key] = funct
            return funct
        return wrapper


    @decorator("some_val1")
    def function_1(self):
       ...

    @decorator("some_val2")
    def function_2(self):
       ...

    @decorator("some_val3")
    def function_3(self):
       ...

    def execute_all_functions(self):
        for key, _ in self.dictionary.items():
            self.dictionary[key]()


if __name__ == "__main__":
    sclass = SomeClass()
    sclass.execute_all_functions()

So this should populate dictionary with:

{
   "some_val1": function_1(),
   "some_val2": function_2(),
   "some_val3": function_3()
}

I'm getting this error though

self.dictionary[key] = funct
NameError: name 'self' is not defined

How would I be able to do this. Help appreciated.

user1960118
  • 367
  • 6
  • 17
  • Have you tried adding `self,` to the definitions of `decorator` and `wrapper` within the class? `def decorator(self,key):`, `def wrapper(self,funct):` – Alan Hoover Feb 28 '18 at 18:13

2 Answers2

0

functools.wraps might be useful. In the bellow trivial example, without wraps the decorator will not function correctly.

from functools import wraps

class SomeClass:
    var = 1

    @wraps
    def decorator(self, fn):
        return fn

    @decorator
    def return_value(self):
        print(self.var)
        return self.var


if __name__ == "__main__":
    sclass = SomeClass()
    sclass.return_value()
jpp
  • 159,742
  • 34
  • 281
  • 339
  • I think the problem I'm having is accessing `dictionary` in the decorator and the fact that when I run `execute_all_functions`, `dictionary` is still empty. So it seems like the `dictionary` variable never gets updated – user1960118 Feb 28 '18 at 18:00
  • The problem seems to be that your decorator can't see class variables, i.e. `self` is not recognised. – jpp Feb 28 '18 at 18:07
  • I think the key is let your decorator has a `cls` variable. Tried last night but it wouldn't work : ( – scriptboy Mar 01 '18 at 01:33
0

I don't think it's possible.

First you should read this: https://docs.python.org/3.3/howto/descriptor.html , to know the difference of function vs method.

In your code, the key equals self of a method.

def decorator(key):
    def wrapper(funct):
        self.dictionary[key] = funct
        return funct
    return wrapper

If you want to use a class's property, you should refer by cls. Correct code might be:

@classmethod
def decorator(cls, key):
    def wrapper(funct):
        self.dictionary[key] = funct
        return funct
    return wrapper

So let's say if you want to update a class property, you must have cls reference. I have tried the code below, make the decorator_maker a classmethod.

class SomeClass:

    dictionary = {}

    @classmethod
    def decorator_maker(cls, key):
        print(cls, key)
        def decorator(funct):
            cls.dictionary[key] = funct
            return funct
        return decorator

    @decorator_maker("some_val1")
    def function_1(self):
       ...

    @decorator_maker("some_val2")
    def function_2(self):
       ...

    @decorator_maker("some_val3")
    def function_3(self):
       ...

    def execute_all_functions(self):
        for key, _ in self.dictionary.items():
            self.dictionary[key]()

And you will get an error like TypeError: 'classmethod' object is not callable. Same as this question: 'classmethod' object is not callable. AKA, you can't call a classmethod until the class is defined.

So you may want to make the decorator outside the class. But for the same reason, you can't get a reference of cls until the classmethod. method is also an attribute of class, you can't dynamic change an attribute while define another. see Python class method run when another method is invoked.

It would be easier if you move dictionary outside the class.

scriptboy
  • 777
  • 8
  • 25