The addition of nested function scopes made the variable lookup rules more complex:
- A reference
xlooks for the name
xfirst in the current local scope (function), second in the local scopes of the enclosing functions (inner to outer), third in the current global scope (the module file), and fourth in the built-in scope (Note: global declarations make the search begin in the global scope).
- An assignment,
x = 2for example, creates or changes the name
xin the local scope. If
xis declared global within the function, the assignment creates or changes the name
xin the enclosing module’s scope. If
xis declared nonlocal within the function the assignment changes the name
xin the closest enclosing function’s local scope (applies for Python3).
>>> x = 2 >>> def function1(): ... x = 3 ... def function2(): ... print(x) ... function2() ... >>> function1() 3
function2() refers to the
x that is in the
function1() function’s local scope. Functions can access names in all physically enclosing
def statements, therefore the
function2() is automatically mapped to the
function1() (LEGB lookup rule).
A better code will be a function that makes and returns another function:
>>> def function1(): ... x = 2 ... def function2(): ... print(x) ... return function2 ... >>> todo = function1() >>> todo() 2
Functions are objects in Python and can be passed back as return values from other functions. Moreover, the
function2() remembers the enclosing scope’s
function1() is no longer active.
This is called “factory functions” or “closures” and are used when you need to generate event handlers on the fly in response to conditions at runtime.
>>> def M1(m): ... def A1(a): ... return a * m ... return A1 ... >>> b = M1(5) >>> b
.A1 at 0x7feadd074b70> >>> >>> b(3) 15 >>> b(4) 20
The first part of the code defines an outer function that simply generates and returns a nested function,
without calling it.
In the second part of the code we call the outer function and we get back a reference to the generated nested function.
In the third part of the code we are calling the nested function that
M1 created and passed back.
If we call the outer function again, we get back a new nested function with different state information attached.
>>> c = M1(6) >>> c
.A1 at 0x7feadd730378> >>> c(5) 30 >>> b(5) 25
It works this way because each call to a factory function gets its own set of state information. This technique is popular among programmers with backgrounds in functional programming languages.
Of course, you can avoid nesting functions within functions (flat is generally better than nested).
>>> def function1(): ... a = 3 ... function2(a) ... >>> def function2(a): ... print(a) ... >>> function1() 3
However, factory functions are common enough in modern Python code, so using them is nothing but beneficial (especially when you start coding lambda expressions).
def defined within a function is nested inside a loop, and the nested function references an enclosing scope variable that is changed by that loop, all functions generated within the loop will have the same value.
>>> def Actions(): ... a =  ... for i in range(3): ... a.append(lambda x: i * x) ... return a ... >>> a = Actions() >>> a
. at 0x7feadcfc2d90> >>> a . at 0x7feadcfc2e18> >>> a . at 0x7feadcfc2ea0>
This doesn’t work! When we pass an argument of 2 in each of the following calls, we get back 2 multiply by 2 for each function in the list, because
i is the same in all of them, 2.
>>> a(2) 4 >>> a(2) 4 >>> a(2) 4
To solve the issue you must use defaults to save the variable’s current value.
>>> def Actions(): ... a =  ... for i in range(3): ... a.append(lambda x, i = i: i * 2) ... return a ... >>> a = Actions() >>> a(2) 0 >>> a(2) 2 >>> a(2) 4