Python create function in a loop capturing the loop variable

后端 未结 5 1404
無奈伤痛
無奈伤痛 2020-12-05 03:38

What\'s going on here? I\'m trying to create a list of functions:

def f(a,b):
    return a*b

funcs = []

for i in range(0,10):
    funcs.append(lambda x:f(         


        
相关标签:
5条回答
  • 2020-12-05 03:58

    Yep, the usual "scoping problem" (actually a binding-later-than-you want problem, but it's often called by that name). You've already gotten the two best (because simplest) answers -- the "fake default" i=i solution, and functools.partial, so I'm only giving the third one of the classic three, the "factory lambda":

    for i in range(0,10):
        funcs.append((lambda i: lambda x: f(i, x))(i))
    

    Personally I'd go with i=i if there's no risk of the functions in funcs being accidentally called with 2 parameters instead of just 1, but the factory function approach is worth considering when you need something a little bit richer than just pre-binding one arg.

    0 讨论(0)
  • 2020-12-05 04:01

    lambdas in python are closures.... the arguments you give it aren't going to be evaluated until the lambda is evaluated. At that time, i=9 regardless, because your iteration is finished.

    The behavior you're looking for can be achieved with functools.partial

    import functools
    
    def f(a,b):
        return a*b
    
    funcs = []
    
    for i in range(0,10):
        funcs.append(functools.partial(f,i))
    
    0 讨论(0)
  • 2020-12-05 04:05

    Considering the final value of i == 9

    Like any good python function, it's going to use the value of the variable in the scope it was defined. Perhaps lambda: varname (being that it is a language construct) binds to the name, not the value, and evaluates that name at runtime?

    Similar to:

    i = 9
    def foo():
        print i
    
    i = 10
    foo()
    

    I'd be quite interested in finding out of my answer is correct

    0 讨论(0)
  • 2020-12-05 04:07

    There's only one i which is bound to each lambda, contrary to what you think. This is a common mistake.

    One way to get what you want is:

    for i in range(0,10):
        funcs.append(lambda x, i=i: f(i, x))
    

    Now you're creating a default parameter i in each lambda closure and binding to it the current value of the looping variable i.

    0 讨论(0)
  • 2020-12-05 04:07

    All the lambdas end up being bound to the last one. See this question for a longer answer:

    How do I create a list of Python lambdas (in a list comprehension/for loop)?

    0 讨论(0)
提交回复
热议问题