def f():
L = []
for i in range(5):
def n():
return i
L.append(n)
return L
L = f()
print(L[0]())
print(L[1]())
print(L[2]())
print(L[3]())
print(L[4]())
the output of the above codes is
4
4
4
4
4
my questions is: after L=f(), is the local namespace of f destructed? if so, how is the i in function n associated with i in the namespace? if not, when is a local namespace to be destructed?
CodePudding user response:
There are a number of "local" namespaces in a function call that make things like your example behave. Specifically, there are three types of variables in a function:
- Local: used in the function only. Values may be returned, but binding to local name is destroyed when function exits.
Linfis an example. - Cell: used in function and some nested scope as well. These names are recreated whenever the function is called, but persist afterwards as a closure in the nested scope.
iinfis an example. - Free: not defined in the function. These are divided into two types, global and non-global. Global variables do not get placed in a closure. Instead they are accessed through the
__globals__attribute of the function. Non-globals persist in the cells of the__closure__attribute when the enclosing namespace is gone.iinnis a nonlocal free variable. - There are also builtin names, but we don't talk about those.
When you call f, L and i are local names. When f ends, the object created for L is passed out of the function, but the binding to the local name L is destroyed. However, the binding to i is not destroyed because it is a cell variable. The binding lives in the __closure__ attribute of each element of L.
If you look at L[0].__closure__, you will find i in a cell there. L[1].__closure__ will have a reference to the same cell, also with i in it. The cell is the same because it comes from the same invocation of f.
When you call n via the elements of L, the last value of i is printed because that's what the cell was bound to when f exited.
You could get different behavior by binding the value of i as a local variable in n. For example:
def n(i=i):
print(i)
Now i in f becomes local because it's no longer used as a nonlocal in n. In n, i is now a local variable that gets its value from the default argument. Default arguments are bound when the function object is created, so will reference the local value of i at the time the list elements are made. This contrasts with free variables, which are looked up in the closure when the function runs. The output of this version will be
0
1
2
3
4
