Skip to content Skip to sidebar Skip to footer

Scope Variable With A Loop

I read a sentence in the official handbook of Python. If a lambda or def defined within a function is nested inside a loop, and the >nested function references an enclosing sco

Solution 1:

To clarify a bit more on the subject, the reference to i does not change within the lambda - you see that each iteration of i generates a new object:

def makeActions():
  acts=[]
  for i in range(5):
    print('i:', i, 'object id:', id(i))
    acts.append(lambda: id(i))
  print('final i:', i, 'object id:', id(i))
  return acts
acts=makeActions()
print([fn() for fn in acts])

Returns

i: 0objectid: 140418781832928
i: 1objectid: 140418781832960
i: 2objectid: 140418781832992
i: 3objectid: 140418781833024
i: 4objectid: 140418781833056
final i: 4objectid: 140418781833056
[140418781833056, 140418781833056, 140418781833056, 140418781833056, 140418781833056]

The reference from i always points to its last assigned object, thus the id of i will not change after its last alteration.

If you want to keep the value at the time of the lambda creation, you need to "trap" the value of the reference at the desired place and time, for instance you could delegate the lambda creation to a function:

defdelegated(d):
  returnlambda: id(d)

defmakeDelegatedActions():
  acts=[]
  for i inrange(5):
    print('i:', i, 'object id:', id(i))
    acts.append(delegated(i))
  print('final i:', i, 'object id:', id(i))
  return acts
acts=makeDelegatedActions()
print([fn() for fn in acts])

Which returns

i: 0objectid: 140418781832928
i: 1objectid: 140418781832960
i: 2objectid: 140418781832992
i: 3objectid: 140418781833024
i: 4objectid: 140418781833056
final i: 4objectid: 140418781833056
[140418781832928, 140418781832960, 140418781832992, 140418781833024, 140418781833056]

Online demo here

Solution 2:

Each lambda will just know to fetch i from its enclosing scope, which is the makeActions call. They don't actually do so until they're called themselves. i is the same name within the same scope, it just happens to take different values during the loop, and will simply hold the last value after the loop finishes. So the lambdas just ended up creating 5 identical functions.

A variation that can work:

defexpgen(n):
    defmyexp(x):
        return n**x
    return myexp

acts = [expgen(i) for i inrange(5)]

In this case, the scope that n is fetched from is that of expgen, which had distinct calls in the list comprehension, and each function will work independently.

Post a Comment for "Scope Variable With A Loop"