I'm trying to learn about Python decorators, and I'd like to understand in more detail exactly how closure applies, for example in this memoization context:
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
@memoize
def fib(n):
if n in (0,1):
return n
return fib(n - 1) + fib(n - 2)
I understand that memoize returns functions that are bound to memo values in the enclosing scope of helper, even when the program flow is no longer in that enclosing scope. So, if memoize is called repeatedly it will return a varying sequence of functions depending upon the current values of memo. I also understand that @memoize is syntactic sugar that causes calls to fib(n) to be replaced with calls to memoize(fib(n)).
Where I am struggling is how the called value of n in fib(n) is effectively converted to the x in helper(x). Most tutorials on closures don't seem to make this point explicit, or they rather vaguely say that one function ‘closes over’ the other, which just makes it sound like magic. I can see how to use the syntax, but would like to have a better grasp of exactly what is happening here and what objects and data are being passed around as the code executes.