Deferred evaluation with lambda in Python [duplicate]
Asked Answered
F

3

13

In a loop, I am trying to defer the comparison the two value()s of two Nodes to a later time.

class Node():
    def __init__(self, v):
        self.v = v
    def value(self):
        return self.v

nodes = [Node(0), Node(1), Node(2), Node(3), Node(4), Node(2)]
results = []
for i in [0, 1, 2]:
    j = i + 3
    results.append(lambda: nodes[i].value() == nodes[j].value())

for result in results:
    print result

The results are all True (because i,j==2,5 for all the lambdas). How can I defer the execution of the lambda until it is actually called, but with the correct variable bindings? And the expressions in the lambda are not all necessarily equality... there are a bunch of other more involved expressions.

Thanks for any help!

Fragrance answered 18/6, 2012 at 16:26 Comment(1)
I'm not really sure what you're trying to do. The lambda expression seems unnecessary to me here. Why can't you just do results.append(nodes[i].value() == nodes[j].value())?Singband
F
18

To bind the current values of i and j to the function instead of having it look in the outer scope, you can use either a closure or default argument values. The easiest way to do this is to use default argument values in your lambda:

for i in [0, 1, 2]:
    j = i + 3
    results.append(lambda i=i, j=j: nodes[i].value() == nodes[j].value())

Here is how it would look as a closure:

def make_comp_func(i, j):
    return lambda: nodes[i].value() == nodes[j].value()

for i in [0, 1, 2]:
    j = i + 3
    results.append(make_comp_func(i, j))
Fanni answered 18/6, 2012 at 16:31 Comment(0)
W
10

Wrap it in another lambda:

results.append((lambda x, y: lambda: nodes[x].value() == nodes[y].value()) (i, j))

or in a nicer way, with partial:

from functools import partial

results.append(partial(lambda x, y: nodes[x].value() == nodes[y].value(), i, j))

Default arguments trick is, well... a trick, and I'd suggest to avoid it.

Without answered 18/6, 2012 at 16:42 Comment(0)
B
5

The idiomatic way is to use a default argument:

[f() for f in [lambda: i for i in range(3)]]
[2, 2, 2]

Change this to:

[f() for f in [lambda i=i: i for i in range(3)]]
[0, 1, 2]
Bejewel answered 18/6, 2012 at 16:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.