Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 762
QuantumEnergy Avatar asked Sep 14 '17 15:09

QuantumEnergy


1 Answers

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.

like image 124
strubbly Avatar answered Nov 07 '22 12:11

strubbly