lambda not outputting correct value with argument

This is the place for queries that don't fit in any of the other categories.

lambda not outputting correct value with argument

Postby rovf » Tue Sep 10, 2013 9:48 am

Here is a small example program to demonstrate my problem:
Code: Select all
#!/usr/bin/python
# -*- coding: utf-8 -*-

def cb(n):
    print "cb invoked using:"
    print n

# Make closures
farr=[None]*3
for i in xrange(0,3):
    farr[i]=lambda: cb(i)
farr[0]()   


My intent is to store into the array farr 3 anonymous functions. Each should call my function cb, but with parameters 0, 1 or 2 respectively. In particular, I expected farr[0] to have stored a function lambda: cb(i) with i bound to zero. However, farr[0]() outputs 2, not 0.

Well, I thought, maybe i is passed by reference, and hence all three anonymous functions access the same copy of i, and I therefore get the last value for i. But how can I then bind the three functions to different parameters?
Last edited by metulburr on Tue Sep 10, 2013 10:21 am, edited 1 time in total.
Reason: changed title
rovf
 
Posts: 23
Joined: Fri Aug 16, 2013 4:35 pm

Re: My closure doesn't work!

Postby metulburr » Tue Sep 10, 2013 10:02 am

farr is not an array, it is a python list. Python lists are like vectors, they can be added to or deleted from, etc.
Code: Select all
farr=[None]*3

you do not need to declare its elements/indexes beforehand

I believe you are looking for:
Code: Select all
def cb(n):
    print("cb invoked using:")
    print(n)

f = []
for i in range(3):
    f.append(lambda x=i: cb(x))
   
f[0]()
f[1]()
f[2]()


Code: Select all
lambda c=c:c
is evaluate at creation time, whereas:
Code: Select all
lambda: c
is evaluate at call time

Programming Python 4th Edition wrote:referencees to names in the enclosing scope are resolved when the generated function is called, not when it is created. Because of this, when the function is later called, those references will reflect the latest or final assignments made to the names anywhere in the enlcosing scope, which is not the values they held when the function was made.
New Users, Read This
version Python 3.3.2 and 2.7.5, tkinter 8.5, pyqt 4.8.4, pygame 1.9.2 pre
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
User avatar
metulburr
 
Posts: 1099
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: lambda not outputting correct value with argument

Postby rovf » Tue Sep 10, 2013 11:20 am

Great solution! Though I just now found meanwhile another one, i.e.:

Code: Select all
def mkcb(n):
    local_n=1*n
    return lambda: cb(local_n)
   
# Make closure
farr=[None]*3
for i in xrange(0,3):
    farr[i]=mkcb(i)


I like yours better, because I don't need the auxiliary function mkcb.

Sorry about the confusion between array and list. Coming from the Ruby world, I didn't think well enough about using the correct terminology here.

BTW, the reason why I predeclare the - eh ... - list is, because in my application, I know at creation time of the list, how big it will be, but the element are not necessary created in sequence (in fact, some elements are permitted to remain None). But in general you are of course right, that just continually appending to the list would be sufficient.
rovf
 
Posts: 23
Joined: Fri Aug 16, 2013 4:35 pm

Re: lambda not outputting correct value with argument

Postby metulburr » Tue Sep 10, 2013 12:04 pm

You can of course assign defualt values, i just meant that you do not have to.
New Users, Read This
version Python 3.3.2 and 2.7.5, tkinter 8.5, pyqt 4.8.4, pygame 1.9.2 pre
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
User avatar
metulburr
 
Posts: 1099
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY


Return to General Coding Help

Who is online

Users browsing this forum: Google [Bot] and 5 guests