Search code examples
pythonglobal-variablesdecorator

Python decorator does not recognize global variable


The mwe of my problem I just coded:

from decorator import decorator


@decorator
def deco(func, deco_name=None, *args, **kwargs):
    print(f"DECORATOR {deco_name} APPLIED")
    return func(*args, **kwargs)


deco_name = None


class Greeter:
    def __init__(self, _deco_name):
        global deco_name
        deco_name = _deco_name

    @deco(deco_name=deco_name)
    def hello(self, name):
        print(f"Hello {name} :)")
        print(deco_name)


g = Greeter("MYDECO")
g.hello("Yoshi")

The console output:

DECORATOR None APPLIED
Hello Yoshi :)
MYDECO

I had a similar set-up in my project and I fail to understand why the decorator function deco() does not know the value of the updated value of the global variable deco_name (it prints DECORATOR None APPLIED instead of DECORATOR MYDECO APPLIED). The decorated function hello() does know the update value as seen by the MYDECO generated by the last print statement. I need some way to set a global variable on runtime and pass it to the decorator and would be happy if someone can a) explain to me why my approach is wrong and b) give me a fix/alternative solution.

Thanks in advance.


Solution

  • The main issue is the decorator is created when the class is made. So the outside decorator is called before you set the variable. You can put all this inside the wrapper function so it only gets called when hello() is called.

    I'm not certain why you are using a global variable here. You can access the instance variable in the wrapper function, which to me makes more sense. Here's an example that sets and accesses both a global variable and an instance variable. Maybe it will point things in a helpful direction:

    def deco(f):
        def wrapper(*args):
            instance = args[0]
            print(f"DECORATOR Instance {instance.deco_name} APPLIED")
            print(f"GLOBAL NAME {global_deco_name} APPLIED")
            f(*args)
        return wrapper
    
    global_deco_name = "global_deco"
    
    class Greeter:
        def __init__(self, _deco_name):
            global global_deco_name
            self.deco_name = _deco_name
            global_deco_name = _deco_name
    
        @deco
        def hello(self, name):
            print(f"Hello {name} :)")
            print(self.deco_name)
            print(global_deco_name)
            
    
    
    g = Greeter("MYDECO")
    g.hello("Yoshi")
    

    Prints

    DECORATOR Instance MYDECO APPLIED
    GLOBAL NAME MYDECO APPLIED
    Hello Yoshi :)
    MYDECO
    MYDECO