Search code examples
pythonpython-3.xdecoratorscoping

Decorating a recursive function


Edit: I didn't do a good job explaining my question. I was confused about how with closures, the function seems to remember its previous environment, but with recursive calls it seems to find the updated value of the name.

My confusion was resolved perfectly by Thomas Ballinger "Finding closure with closures" talk:

Scope of a variable is determined at definition, value of a variable is determined at execution.

So with either recursion or closures, the binding of the name was defined at definition, but the value can be still updated afterwards.

Original question:

Decorators work on recursive functions without any extra effort:

def debug(f):
    def new_f(*args, **kwargs):
        print('arguments:', *args, **kwargs)
        return f(*args, **kwargs)
    return new_f

@debug
def f(n):
    if n > 1:
        return f(n-1)*n # f refers to the decorated version!
    else:
        return 1

What mechanism in python ensures that f in the line return f(n-1)*n points to the decorated version of f rather than to the original?

I thought a function remembers its context at the time it's defined (so that with closures, the inner function can use the objects from the outer function). But when f was defined, the decorator hadn't been applied yet, so shouldn't f inside the function f refer to the undecorated version forever? Obviously, I misunderstood something in function scoping / context rules, but what?


Solution

  • The fact that python will look up the name f when the function is executed (and not when it compiles it) and see that it is the decorated version:

    >>> f
    <function __main__.debug.<locals>.new_f>
    

    Since the name f is essentially re-binded by applying the decorator, that is the f that is going to be used whenever its name is looked-up.