0

I saw this code on the internet, and I don't know how it works.

def multipliers():
    return [lambda x : i * x for i in range(4)]

print([m(2) for m in multipliers()])

Here are some questions:

  1. What is the output of the list which is returned by the multipliers function?
  2. What is meant by m(2) here? Since it didn't mentioned what is m before anywhere in the code. But still the code works without any error.
dejdej
  • 2,590
  • 4
  • 16
  • 28
Sri Ram
  • 21
  • 1
  • 4
    Does this answer your question? [What does "list comprehension" mean? How does it work and how can I use it?](https://stackoverflow.com/questions/34835951/what-does-list-comprehension-mean-how-does-it-work-and-how-can-i-use-it) – MendelG Aug 16 '20 at 19:39
  • 1
    2. ‘m’ is mentioned in the list comprehension. – quamrana Aug 16 '20 at 19:43
  • Huh. This code has surprising behavior to me. I would expect that multipliers() would return a list of four different lambdas, which would return different values when evaluated with x = 2. But instead it prints [6, 6, 6, 6]. What am I not following about how these lambdas are created? – Nathan Pierson Aug 16 '20 at 19:48

2 Answers2

4

multipliers function which returns a list of functions (more exactly some anonymous functions aka lambdas) :


multipliers()

# the return of multipliers function
[<function multipliers.<locals>.<listcomp>.<lambda> at 0x11243c048>, <function multipliers.<locals>.<listcomp>.<lambda> at 0x11243c0d0>, <function multipliers.<locals>.<listcomp>.<lambda> at 0x11243c158>, <function multipliers.<locals>.<listcomp>.<lambda> at 0x11243c1e0>]

[m(2) for m in multipliers()] list comprehension in which you iterate and call each function from the list returned by multipliers .

The catch is that python closures are late-binding , which means that the values of variables used in closures are looked up at the time the inner function it is called (lambda x : i * x for i in range(4))

That is why for your case the printed list is [6, 6, 6, 6] because the functions from the list (returned by multipliers) are actually looking something like this lambda x : 3 * x and when you are iterating and calling each lambda in your list comprehension [m(2) for m in multipliers()] you are actually calling more or less the same function

If you want to "fix" the behaviour of multipliers function you can do something like this:


def fix_multipliers():
    return [ lambda x, i=i: i*x for i in range(4)]

Afterwards you will see the "expected" behaviours:

print([m(2) for m in fix_multipliers()]) [0, 2, 4, 6]

PS : Closure is a function object that remembers values in enclosing scope even if they are not present in memory aka extend the scope of the inner function.

dejdej
  • 2,590
  • 4
  • 16
  • 28
1

The output of the above code will be [6, 6, 6, 6] (not [0, 2, 4, 6]).

The reason for this is that Python’s closures are late binding. This means that the values of variables used in closures are looked at the time the inner function is called. So, when any of the functions returned by multipliers() are called, the value of i is looked up in the surrounding scope at that time. By then, regardless of which of the returned functions is called, the for loop has completed and i is left with its final value of 3. Therefore, every returned function multiplies the value it is passed by 3, so since a value of 2 is passed in the above code, they all return a value of 6 (i.e., 3 x 2).

in other terms lambda returns the value of i when called. The lambda is called after the loop has finished, so i is 3 by the time it's called.

Gautamrk
  • 657
  • 2
  • 11