Home > Blockchain >  when is a local namespace to be destructed in python?
when is a local namespace to be destructed in python?

Time:10-09

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:

  1. Local: used in the function only. Values may be returned, but binding to local name is destroyed when function exits. L in f is an example.
  2. 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 in f is an example.
  3. 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 in n is a nonlocal free variable.
  4. 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
  • Related