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.