Factory Functions: Closures
Consider the code below
>>> def maker(N): def action(X): return X ** N return action
This defines an outer function that simply generates and returns a nested function, without calling it—maker makes action, but simply returns action without running it.
I have written about how objects are created in python - if you want to take a quick detour, you can find it here
If we call the outer function:
>>> f = maker(2) # Pass 2 to argument N >>> f <function maker.<locals>.action at 0x0000000002A4A158>
what we get back is a reference to the generated nested function—the one created when the nested def runs.
If we now call what we got back from the outer function:
>>> f(3) # Pass 3 to X, N remembers 2: 3 ** 2 9 >>> f(4) # 4 ** 2 16
we invoke the nested function—the one called action within maker. In other words, we’re calling the nested function that maker created and passed back.
To get a conceptual understanding of how python handles functions, you can read this before proceeding ahead.
Perhaps the most unusual part of this, though, is that the nested function remembers integer 2, the value of the variable N in maker, even though maker has returned and exited by the time we call action. Usually a variables name should be destroyed once the function is exited. I have written about it here if you want to read.
In effect, N from the enclosing local scope is retained as state information attached to the generated action, which is why we get back its argument squared when it is later called.
Just as important, if we now call the outer function again, we get back a new nested function with different state information attached. That is, we get the argument cubed instead of squared when calling the new function, but the original still squares as before:
>>> g = maker(3) # g remembers 3, f remembers 2 >>> g(4) # 4 ** 3 64 >>> f(4) # 4 ** 2 16
This works because each call to a factory function like this gets its own set of state-information. In our case, the function we assign to name g remembers 3, and f remembers 2, because each has its own state information retained by the variable N in maker.
This is a quick short article giving an intuitive understanding of factory functions. This is used extensively in python.