1

I've stumbled upon something that hurts my brain. Consider the following

fs = []
for i in range(3):
    fs.append(lambda : normal(i, .001))
    print mean([fs[-1]() for j in range(1000)])
print [mean([fs[i]() for j in range(1000)]) for i in range(3)]

which returns, e.g.,

-1.38651717873e-05                                                                                                                    |
1.00003545604                                                                                                                         |
1.99999343229                                                                                                                         |
[2.0530899455777235e-05, 0.99995283932453838, 2.0000045292311737] 

as expected. Now modify things slightly

fs = []
for i in range(3):
    mu = i
    fs.append(lambda : normal(mu, .001))
    print mean([fs[-1]() for j in range(1000)])
print [mean([fs[i]() for j in range(1000)]) for i in range(3)]

and we get

5.56620751611e-06                                                                                                                     |
0.999999076252                                                                                                                        |
1.99995161119                                                                                                                         |
[2.0000016397219098, 1.9999853403014964, 2.0000209870831158] 

i.e. 2 for each of the ones outside the loop. After kerploding my brain, I remembered that references/scoping in python are evil, and it appears the answer is that the mu passed to normal is being stored by reference rather than by value (even though mu should be scoping out in a normal language). Is that what is happening? How can I avoid similar madness in future coding?

andyInCambridge
  • 1,215
  • 2
  • 13
  • 27

1 Answers1

1

Closures in Python are by name (well, not technically, but you can think of them that way). So, every lambda has a value of 2 for mu, as that is the last value given to mu in the loop. To fix the value of mu at function definition time, just use a default argument value like so:

lambda mu=mu: normal(mu, .001)

(Scoping rules don't really have anything to do with this.)

kindall
  • 178,883
  • 35
  • 278
  • 309