6

I have been trying to generate a list of lambda functions in python using list comprehension. but it didn't work,

for example

fl=[lambda x: x**i for i in range(5)]

i have check the other question, it basically generate the same function based on the reference of i.

so I also tried partial.

from functools import partial

fl=[partial(lambda x: x**i) for i in range(5)]

but it didn't work too. any help will be appreciated. cheers~

2
  • @Jeff Mercado fl[0](2) = fl[1](2) = 16 Commented Jun 14, 2011 at 18:42
  • Hmm, this is a puzzle alright. I don't think there is a way to keep i from being updated since it is a reference to the loop variable. Why exactly do you want to create this list of functions? Is it possible to solve your larger problem using a different method? Commented Jun 14, 2011 at 19:54

3 Answers 3

6

You're tripping over Python scopes.

fl=[lambda x, i=i: x**i for i in range(5)]
Sign up to request clarification or add additional context in comments.

6 Comments

Interesting. So Python seems to behave similar to JavaScript in this case.
This isn't really related to scoping. It's caused by the way the "close over variables of outer scopes" is interpreted (Python chooses sharing the same variable instead of copying its value), which is indeed the same thing many other languages (e.g. JS and C#) work.
It has everything to do with scoping. Each lambda has the same scope, which means that they all have the same variable. And if the variable changes, so will the result of all the lambdas. Assigning the value as a default argument to the lambda means that they no longer need to access (or even hold on to) the external scope.
Each lambda has its own scope. These scopes don't include copies of the value of i at definition time (as it is the case with default values), so they access the one from the encloding scope when running. They do this because it was defined that upvalues shouldn't become local copies, which may be argued to be a scoping decision, but it doesn't come from them not having their own scope.
"shares" may have been a better word to use than "has" in my comment, but I stand by my answer.
|
6

You're effectively passing i in by name.

fl=[lambda x: x**i for i in range(5)]

Every time lambda is executed, it binds the same i to the function, so when the function is executed (later) it uses the then-current value of i (which will be 4). You should pass it in as a default argument instead:

fl=[lambda x, j=i: x**j for i in range(5)]

Actually, I noticed that you're misusing partial. Here:

fl = [partial(lambda x, y: y ** x, i) for i in range(5)]

That works as well.

1 Comment

@jerry, yeah I just used j because it makes it clearer what's going on (to my mind, anyway).
4

Another common workaround is to use a closure:

def create_f(i):
    def f(x):
        return x**i
    return f

fl = [create_f(i) for i in range(5)]

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.