def f(x):
return f_norec(f,x)
return memoize(f)
Here, the returned function is the one produced by memoized
, but the local name f
still refers to the non-memoized function defined in the above snippet, hence none of the recursive calls benefit from memoization. The call graph goes like this:
<memoized f>
f
f_noref
f
f_norec
...
Here on the other hand,
@memoize
def f(x):
return f_norec(f,x)
return f
The local name f
refers to the memoized function, so you get a call graph like this:
<memoized f>
f
f_norec
<memoized f>
f
f_norec
<memoized f>
...
(It looks like more calls, and it is. I only show the first of the two recursive calls per level, so you don't see how the memoization shortens the second.)
If you manually wrote that the decorator syntax actually desugars to (f = memoize(f); return f
), you'd see identical behavior and performance.