2

I have seen this question, but i still cannot get why such simple example does not work:

mylist = ["alice", "bob", "greta"]
funcdict = dict(((y, lambda x: x==str(y)) for y in mylist))
funcdict['alice']("greta")
#True
funcdict['alice']("alice")
#False
funcdict['greta']("greta")
#True

How is it different from:

[(y, y) for y in mylist]

Why y is not evalueated within each step of iteration?

Community
  • 1
  • 1
Dima Lituiev
  • 12,544
  • 10
  • 41
  • 58
  • 1
    Why do you expect `y` to be evaluated: it's a temporary variable inside a list comprehension. –  Jul 16 '16 at 01:53

2 Answers2

4

The y in the body of the lambda expression is just a name, unrelated to the y you use to iterate over mylist. As a free variable, a value of y is not found until you actually call the function, at which time it uses whatever value for y is in the calling scope.

To actually force y to have a value at definition time, you need to make it local to the body via an argument:

dict((y, lambda x, y=z: x == str(y)) for z in mylist)
chepner
  • 497,756
  • 71
  • 530
  • 681
2
((y, lambda x: x==str(y)) for y in mylist)

y inside the lambda is not bound at the time of the genration expression defined, but it's bound when it's called; When it's called, iteration is already done, so y references the last item greta.

One way to work around this is to use keyword argument, which is evaluated when the function/lambda is defined:

funcdict = dict((y, lambda x, y=y: x == y) for y in mylist)
funcdict = {y: lambda x, y=y: x == y for y in mylist}  # dict-comprehension

or you can use partial:

funcdict = {y: partial(operator.eq, y) for y in mylist}

y is evaluated while the mylist is iterated.

falsetru
  • 357,413
  • 63
  • 732
  • 636