4

I am obviously misunderstanding how lambda works here:

def get_res_lambda():
    res = []
    for v in ['one', 'two', 'three']:
        res.append(lambda: v)
    return res


def get_res():
    res = []
    for v in ['one', 'two', 'three']:
        res.append(v)
    return res

print ">>> With list"
res = get_res()
print(res[0])
print(res[1])
print(res[2])

print ">>> With lambda"
res = get_res_lambda()
print(res[0]())
print(res[1]())
print(res[2]())

I get:

>>> With list
one
two
three
>>> With lambda
three
three
three

I was expecting:

>>> With list
one
two
three
>>> With lambda
one
two
three

Why is the lambda version returning always the last value? What am I doing wrong?

blueFast
  • 41,341
  • 63
  • 198
  • 344
  • 2
    By using a `lambda`, you implement *lazy* evaluation: the result is only evaluated when you call the function, and at that stage, `v` has value `three`. – Willem Van Onsem Jan 09 '17 at 12:05
  • 1
    @WillemVanOnsem A generator implements lazy evaluation, lambda implements an anonymous function. Nothing lazy about it. – blueFast Jan 09 '17 at 12:12
  • This question is asked approximately every day. – TigerhawkT3 Jan 09 '17 at 12:13
  • 1
    @delavnog: Of course this is lazy. Say you would have written `lambda: very_expensive_operation()` and `very_expensive_operation` either does a lot of calculations or sleeps for 300 seconds, do you think it will be executed before you call it with `res[0]()`? – Willem Van Onsem Jan 09 '17 at 12:15
  • @WillemVanOnsem Calling the lambda function happens when you call the lambda function, of course, like every function. Your hint about lazy seems to imply that the value returned by the function (v) is computed when the function is called. This is not true: the value is already set when the function is defined. It is the way in which the value is set in the closure where the lambda function is defined that affects the result. You'll get the exact same problem by using a def instead of lambda, and you will agree with me that def is not lazy. – blueFast Jan 09 '17 at 12:19
  • 2
    @delavnog: indeed the function *construction* is not lazy, but the function *execution* is lazy, and meanwhile the value of `v` has changed. – Willem Van Onsem Jan 09 '17 at 12:22
  • @WillemVanOnsem Then we agree. Your original comment was "by using a lambda", which seems to imply that lambda has implicit lazyness. It is just the fact that it is a function, which is evaluated on call. The strange result is coming from how closures and loops are managed in python, not directly due to a strange lambda side-effect, which was my first thought. – blueFast Jan 09 '17 at 12:25

1 Answers1

4

Your lambda function returns the value of v at the point the lambda function is executed. The lambda function is executed when you reach print(res[0]()) etc.

By that time for v in ['one', 'two', 'three'] has already completely finished its loop, and v has its final value, 'three'.

khelwood
  • 55,782
  • 14
  • 81
  • 108