0

I have created below code which uses lambda inside a for loop:

adders = []
for n in range(1, 4):
    adders.append(lambda x: x + n)

I am getting below output for adders[0](10)

13

My understanding is that x + n will be evaluated at the compile time. So, the output for adders[0](10) should be :

11

My understanding is that python should be adding lambda x: x + 1, lambda x: x + 2 and lambda x: x + 3 to adders. Please help me understand why python is not doing that?

Please let me know if I am misunderstanding something.

meallhour
  • 13,921
  • 21
  • 60
  • 117
  • 4
    All your `lambda` functions add `n` to their argument. After the loop finishes, `n` is 3. So all your lambda functions add 3 to their argument. – khelwood Dec 23 '21 at 22:07
  • My understanding is that python should be adding `lambda x: x + 1`, `lambda x: x + 2` and `lambda x: x + 3` to `adders`. Please help me understand why python is not doing that? – meallhour Dec 23 '21 at 22:18
  • Since you are creating a list of anonymous functions, the value of the global variable n is used. In this situation you should add n as an input variable. `lambda x,n=n: x + n` – josephvictory Dec 23 '21 at 22:18
  • 2
    No, for the function `lambda x: x + n` `n` is a *free variable*. Python has *lexically scoped closures*, so it refers the the `n` in scope when the function is defined. At the end fo the for loop, `n == 3`. This is no more mysterious than `def foo(): print(x)`; then `x = 1; foo(); x = 2; foo()` – juanpa.arrivillaga Dec 23 '21 at 22:22
  • 1
    Your understanding is only partially correct. The function will be compiled to "fetch the value of x, fetch the value of n, add them, return the result". The actual value of `n` won't be fetched until the lambda actually executes, at which time `n` will be 3. This is just a trap in Python that you need to be aware of. – Tim Roberts Dec 23 '21 at 22:23
  • 1
    @TimRoberts eh, this is pretty typical behavior in most modern languages. – juanpa.arrivillaga Dec 23 '21 at 22:24
  • Is it because python resolves value of variables in lambda, when we execute it, not when we define it? – meallhour Dec 23 '21 at 22:41
  • Note that there is a really hacky but easy fix for this: `lambda x, n=n: x + n`. This creates, as part of the lambda, a **new** variable `n` that is bound to the current value of the *old* variable. – Frank Yellin Dec 23 '21 at 23:03
  • @meallhour that is *always how variables in functions work*. Always. Again, `x = 1` then `def foo(): print(x)` then `foo(); x = 2; foo()`. It's the *exact same phenomenon*, free variables are resolved when the function is *executed*. Note, this is how things work in, say, JS as well. – juanpa.arrivillaga Dec 23 '21 at 23:08
  • @FrankYellin yes, the more "principled" way is to use a factory function, `def adder_maker(n): return lambda x: x + n` and then in the loop, `adder.append(adder_maker(n))`, or as one whole lambda expression(which I wouldn't recommend) `adders.append((lambda n: lambda x: x + n)(n))` – juanpa.arrivillaga Dec 23 '21 at 23:09
  • @juanpa.arrivillaga. I agree 100% with the factory function. I did say hacky! If I were writing production code, then absolutely a factory function. If I'm racing a clock, then yeah, I'm just going to add `n=n` to the lambda. – Frank Yellin Dec 23 '21 at 23:21
  • @juanpa.arrivillaga Here, `n` is a global variable because it is defined at the module level. Can we still call `n` as `free variable` when it is referred inside the `lambdas`? – meallhour Dec 23 '21 at 23:28
  • @meallhour yes, the fact that it is global is not relevant. This works this way in *any* enclosing scope (note, class blocks are an exception -- they don't create an enclosing scope!). But try this in a function, and you will get the exact same behavior. – juanpa.arrivillaga Dec 24 '21 at 00:01

0 Answers0