Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does python remember values of enclosing scope's variable when nested function in called?

I read an example on closure

def outer():
        x = 1
        def inner():
           print x # 1
        return inner

The life of our variable x is only till the time the function outer runs . When i call outer and it returns the adress of inner , the variable x should have died because the function outer has exited but still how can i acess it through the returned inner function ?

python variable scope in nested functions

This link answered that the value of variables used by inner is stored when outer returns inner function to caller , but

Now see this code

 def outer():
            x = 1
            def inner():
               print x*n # n not defined anywhere
            return inner

I ran this code and This piece of code doesn't give any error when i call outer , that means that python doesn't checks the variables used by inner function while running outer . so how does it know it has to retain 'x' , after exiting ?

like image 306
Shikhar Srivastava Avatar asked Sep 14 '25 22:09

Shikhar Srivastava


2 Answers

The life of our variable x is only till the time the function outer runs

No. x lives as long as it's reachable from somewhere. And, it is — from the inner function.

This piece of code doesn't give any error when i call outer , that means that python doesn't checks the variables used by inner function while running outer

n could be defined after calling outer, but before calling its result, inner. In that case inner's body if perfectly legitimate.

If you didn't return inner, then there'd be no reason to keep x, because you can't reach inner, thus can't somehow involve x into computation.

like image 154
Artem Sobolev Avatar answered Sep 17 '25 12:09

Artem Sobolev


The way x is remembered is because python has a special __closure__ attribute that remembers the object from the enclosing scope that the local function needs and keeps a reference to it so it can be used in the local function:

def outer():
    x = 1
    def inner():
        print(x)
    return inner

o = outer()

print(o.__closure__)
(<cell at 0x7fa18a2fc588: int object at 0x9f8800>,)

If you assign o = outer() and then call o() you are then calling the inner function and you will get an error, calling outer() does not call the inner function.

If you were to declare a function f and put a print(n) in the body then unless you actually call f you won't get an error so the same logic applies to your inner function, you are not actually calling it with outer() so you don't get an error:

def f():
    print(n) # will only error when we actually call `f()`

If calling the inner function happened when you called outer it would defeat the purpose, consider the following function factory where we take in an exponent e to raise i to:

def raise_exp(e):
    def power(i):
        return  i ** e # n not defined anywhere
    return power

sq = raise_exp(2) # set exponent but does not call power 
print(sq(2)) # now we actually all power
cube = raise_exp(3)
print(cube(3))

25
27
like image 35
Padraic Cunningham Avatar answered Sep 17 '25 12:09

Padraic Cunningham