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.
L
inf
is 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.
i
inf
is 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.i
inn
is 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