Why do different variable names get different results(python2.7)?

Question:

in this code:

results = []
for i in [1, 2, 3, 4]:
    def inner(y):
        return i
    results.append(inner)

for i in results:
    print i(None)  

the output is “function inner at 0x107dea668”

if i change i to other letter, for example:

results = []
for i in [1, 2, 3, 4]:
    def inner(y):
        return i
    results.append(inner)

for j in results:
    print j(None)

the output is “4”


Answer

results = []
for i in [1, 2, 3, 4]:
    def inner(y):
        print "in inner:%s " % id(i)
        return i
    results.append(inner)

# i -> 4
for i in results:
    # i -> func inner
    print "i: %s" % i
    print "in loop: %s " % id(i)

    # func inner <===>  A
    # i == A  ->  return i -> return A, so when call funtion inner, will return itself
    # print "call: %s" % i(None)

    print "call: %s" % i(None)(None)(None)
    print "------------------------------"

i: function inner at 0x101344d70
in loop: 4315172208
in inner:4315172208
in inner:4315172208
in inner:4315172208
call: function inner at 0x101344d70

i: function inner at 0x101344de8
in loop: 4315172328
in inner:4315172328
in inner:4315172328
in inner:4315172328
call: function inner at 0x101344de8

i: function inner at 0x101344e60
in loop: 4315172448
in inner:4315172448
in inner:4315172448
in inner:4315172448
call: function inner at 0x101344e60

i: function inner at 0x101344ed8
in loop: 4315172568
in inner:4315172568
in inner:4315172568
in inner:4315172568
call: function inner at 0x101344ed8

Asked By: QuantumEnergy

||

Answers:

Actually, when you use j in results, the variable i remains 4, due to after the first-loop, the i stays number 4;

But if you use i in results, i refer to functions stored inside of results. The i is not fixed until you start to call it. as @Jean-François Fabre said, lazy evaluation.

Additionally, if you wanna inner function to store each i when loop from 1-4, you should store i in the related scope, use partial function;

from functools import partial

results = []
for i in [1, 2, 3, 4]:
    def inner(i, y):
        return i


    results.append(partial(inner, i))

for i in results:
    print(i(None))

this will give you results of

1
2
3
4

which is more meaningful.

Answered By: Menglong Li

The inner function you have defined contains a free variable referring to the global variable i. This is perhaps clearer in an example like this:

def inner(y):
    return i

i = 1
print inner(None)

i = 2
print inner(None)

which prints 1 and then 2

In your first example, at the time of the call to inner, i has the value which is the function and so that is what is printed when i (which is inner) is called.

In the second example, at the time of the call to inner, i has the value 4 and so that is what is printed when j (which is inner) is called.

A clear way to express what you presumably wanted here is to use a partially evaluated function, as recommended in another answer. Another way is to use an enclosing function to create a closure. Like this:

results = []
for i in [1, 2, 3, 4]:
    def outer(k):
        def inner(y):
            return k
        return inner
    results.append(outer(i))

for i in results:
    print i(None)  

which will print 1 to 4 as presumably you want.

A little trick sometimes used in Python is to use the default value of a variable as a cell to contain a value:

results = []
for i in [1, 2, 3, 4]:
    def inner(y, i = i):
        return i
    results.append(inner)

for i in results:
    print i(None)  

which also prints 1 to 4.

Answered By: strubbly

Variable i returned in the inner function keeps its value from the last context it was assigned.

If you debug the code, using a breakpoint inside inner function, picture will become clearer if select the previous frame/context (bottom left in figures) before calling the function.

When you use i, it is assigned inside the second for, so it will have a function as its value (highlighted in yellow in Figure 1).

Figure 1

Now, if you use j, variable i will keep its last value from the previous context: the for over the list (Figure 2).

Figure 2

Answered By: y.luis
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.