Friday, May 6, 2011

What's going on with the lambda expression in this python function?

Why does this attempt at creating a list of curried functions not work?

def p(x, num):
    print x, num

def test():
    a = []
    for i in range(10):
        a.append(lambda x: p (i, x))
    return a

>>> myList = test()
>>> test[0]('test')
9 test
>>> test[5]('test')
9 test
>>> test[9]('test')
9 test

What's going on here?

A function that actually does what I expect the above function to do is:

import functools
def test2():
    a = []
    for i in range (10):
        a.append(functools.partial(p, i))
    return a


>>> a[0]('test')
0 test
>>> a[5]('test')
5 test
>>> a[9]('test')
9 test
From stackoverflow
  • In Python, variables created in loops and branches aren't scoped. All of the functions you're creating with lambda have a reference to the same i variable, which is set to 9 on the last iteration of the loop.

    The solution is to create a function which returns a function, thus scoping the iterator variable. This is why the functools.partial() approach works. For example:

    def test():
        def makefunc(i):
            return lambda x: p(i, x)
        a = []
        for i in range(10):
            a.append(makefunc(i))
        return a
    
    David : Got it. Thanks very much!
  • I asked a similar question, and got two answers. One basically the same as the accepted answer here, and the other which is less clear but slightly more succint.

    http://stackoverflow.com/questions/728356/dynamically-creating-a-menu-in-tkinter-lambda-expressions

    mataap : Which includes (and I finally understand now) the lambda x=x : ... hack.
  • Well you can also bind the i to an outer lambda for the lazy.

    def p(x, num):
        print x, num
    
    def test():
        a = []
        for i in range(10):
            a.append((lambda i :lambda x: p (i, x))(i))
        return a
    

0 comments:

Post a Comment