Search code examples
pythonfunctionscopedecoratorpython-decorators

Confusion with scope in Python decorators


I'm trying to make a simple Python decorator, below is a simplified example of my use case.

def decorator(x):
    def inner(f):
        if x == 2:
            x = 1
        return f
    return inner

@decorator(2)
def f():
    pass

But I get this error

if x == 2:
UnboundLocalError: local variable 'x' referenced before assignment

Why is x not available? It's confusing because if I use print(x) instead of if x == 2 it works as expected.


Solution

  • Python can't determine xs actual scope, so before it runs, in the inner function it would raise the UnboundLocalError.

    The way to fix it is modify the arguments of inner a bit, instead of:

        def inner(f):
    

    Make it to be:

        def inner(f, x=x):
    

    Then your code would work fine.

    Or you could also use nonlocal, by adding nonlocal x in the beginning of the function inner.

    def inner(f):
        nonlocal x
        if x == 2:
            x = 1
        return f