2

If I execute this code:

fs = []
for i in [1, 2, 3, 4]:
  def f():
    print(i)

  fs += [f]

for f in fs:
  f()

It prints:

4
4
4
4

I don't understand why. And how to make it print this:

1
2
3
4
Lucas Willems
  • 6,673
  • 4
  • 28
  • 45
  • Loop iterations don't create new scopes. So all the functions are closed over the same scope of `i`. – Barmar Oct 06 '22 at 23:43

3 Answers3

2

to follow up @AirSquid answer, if you execute the following code, you will see the id(i) indicating the same address.

fs = []

for i in [1, 2, 3, 4]:
    def f():
        print(id(i))
        print(i)
    fs += [f]

for f in fs:
    f()
    print(f)

Dummy
  • 51
  • 1
  • 7
1

when your for-loop over i completes, the last value of i that was assigned is 4. So that is the value of i when you loop over fs

Going back to your fs variable... You are just appending the originally empty list with 4 references to the (oddly placed) function, so it is just a list that has 4 things in it. What they are is immaterial in this case.

So, in your last for-loop, it iterates 4 times (the length of fs) and each time it calls f() which internally assumes that i is a global variable and it prints the last value.

If you want to make it print 1, 2, 3, 4, delete everything and write a better structured chunk of code and a function that takes a parameter i that you pass in or such.

AirSquid
  • 10,214
  • 2
  • 7
  • 31
  • Thanks @airsquid for your answer! I simplified as much as possible my example so it might be weird to have such a complicated code to just print 1, 2, 3, 4, but in my case, I really need to have a list containting X functions that each depend on a different value of i. – Lucas Willems Oct 08 '22 at 11:01
1

hence its a pointer passed to function it need to be evaluated before being appended to list

my functional approach to this would be following:

fs = []
loop = [1, 2, 3, 4]
for i in [1, 2, 3, 4]:
  def f():
    string = f'print({i})'
    def f2():
        eval(string)
    return f2

  fs += [f()]

for _f in fs:
  _f()

output:

1
2
3
4

UPDATE:

you could also do this, makes more sense to me:

creating tuples of function and i instances then calling them with each other:

fs = []

for i in [1, 2, 3, 4]:
    def f(j):
        print(j)

    fs += [(f, i)]

for i in fs:
    i[0](i[1])