Function scope

Take a moment to study the below code and predict the result before running it.

def outer_func():
    def inner_func():
        a = 9
        print('inside inner_func, a is {:d} (id={:d})'.format(a, id(a)))
        print('inside inner_func, b is {:d} (id={:d})'.format(b, id(b)))
        print('inside inner_func, len is {:d} (id={:d})'.format(len,id(len)))

    len = 2
    print('inside outer_func, a is {:d} (id={:d})'.format(a, id(a)))
    print('inside outer_func, b is {:d} (id={:d})'.format(b, id(b)))
    print('inside outer_func, len is {:d} (id={:d})'.format(len,id(len)))
    inner_func()

a, b = 6, 7
outer_func()
print('in global scope, a is {:d} (id={:d})'.format(a, id(a)))
print('in global scope, b is {:d} (id={:d})'.format(b, id(b)))
print('in global scope, len is', len, '(id={:d})'.format(id(len)))

This program defines a function, inner_func nested inside another, outer_func. After these definitions, the execution proceeds as follows:

  1. Global variables a=6 and b=7 are initialized.
  2. outer_func is called:
    1. outer_func defines a local variable, len=2.
    2. The values of a and b are printed; they don't exist in local scope and there isn't any enclosing scope, so Python searches for and finds them in global scope: their values (6 and 7) are output.
    3. The value of local variable len (2) is printed.
    4. inner_func is called:
      1. A local variable, a=9 is defined.
      2. The value of this local variable is printed.
      3. The value of b is printed; b doesn't exist in local scope so Python looks for it in enclosing scope, that of outer_func. It isn't found there either, so Python proceeds to look in global scope where it is found: the value b=7 is printed.
      4. The value of len is printed: len doesn't exist in local scope, but it is in the enclosing scope since len=2 is defined in outer_func: its value is output
  3. After outer_func has finished execution, the values of a and b in global scope are printed.
  4. The value of len is printed. This is not defined in global scope, so Python searches its own built-in names: len is the built-in function for determining the lengths of sequences. This function is itself an object and it provides a short string description of itself when printed.

Here is the output:

inside outer_func, a is 6 (id=232)
inside outer_func, b is 7 (id=264)
inside outer_func, len is 2 (id=104)
inside inner_func, a is 9 (id=328)
inside inner_func, b is 7 (id=264)
inside inner_func, len is 2 (id=104)
in global scope, a is 6 (id=232)
in global scope, b is 7 (id=264)
in global scope, len is <built-in function len> (id=977)

Note that in this example outer_func has (perhaps unwisely) redefined ("re-bound") the name len to the integer object 2. This means that the original len built-in function is not available within this function (and neither is it avaiable within the enclosed function, inner_func).