30

I tried the following code in Python 3.5.1:

>>> f = {x: (lambda y: x) for x in range(10)}
>>> f[5](3)
9

It's obvious that this should return 5. I don't understand where the other value comes from, and I wasn't able to find anything.

It seems like it's something related to reference - it always returns the answer of f[9], which is the last function assigned.

What's the error here, and how should this be done so that it works properly?

0

2 Answers 2

35

Python scoping is lexical. A closure will refer to the name and scope of the variable, not the actual object/value of the variable.

What happens is that each lambda is capturing the variable x not the value of x.

At the end of the loop the variable x is bound to 9, therefore every lambda will refer to this x whose value is 9.

Why @ChrisP's answer works:

make_func forces the value of x to be evaluated (as it is passed into a function). Thus, the lambda is made with value of x currently and we avoid the above scoping issue.

def make_func(x):
    return lambda y: x

f = {x: make_func(x) for x in range(10)}
Sign up to request clarification or add additional context in comments.

7 Comments

An alternate shorthand would be f = {x: (lambda y, x=x: x) for x in range(10)}, because function default arguments are bound at definition time, so they'd bind the value of x, not the name x.
This may sound like a very dumb question (I'm not too familiar with lambda), but how does make_func(x) work? I can imagine that f[5](3) means -> f = {5:make_func(5)}. So how is it that make_func(5)(3) equal to 5?
The dictionary comprehension makes a dictionary keys of ints to values of functions. f[5] returns the value in the dictionary keyed by 5 which happens to be the function lambda y: x. Note the lambda ignores whatever is passed in (y) and always returns x. That is why the lambda will either return 5 or 9 (due to the issue described above).
@gnicholas I see, so basically lambda is relying on a global x value (i.e. global within the scope of the dictionary?)
Also see the Lazy binding paragraph in Common Gotchas In Python.
|
9

The following should work:

def make_func(x):
    return lambda y: x

f = {x: make_func(x) for x in range(10)}

The x in your code ends up referring to the last x value, which is 9, but in mine it refers to the x in the function scope.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.