scoping with lambda in loops

David Eppstein eppstein at ics.uci.edu
Tue Sep 16 18:31:28 EDT 2003


In article <7f9e1817.0309161338.20fbdcbc at posting.google.com>,
 imcmeans at telus.net (Ian McMeans) wrote:

> First, I made multiple lambda functions inside a loop, each of which
> depended on the current loop variable.
> 
> >>> a = []
> >>> for index in range(5):
> 	a.append(lambda: index)
> 
> 
> Now, see if you can guess what the output was for each of the
> functions in the list a:
> >>> a[0](), a[1](), a[2](), a[3](), a[4]()
> I had expected it to be (0, 1, 2, 3, 4), but actually, it's:
> 
> (4, 4, 4, 4, 4)
> 
> This really surprised me. I guess what is happening is that each
> lambda knows what the context of execution is where it was defined,
> and doesn't actually evaluate until the function is called, and when
> it does evaluate, it uses the current value of the variable. Is this
> related to static scoping?

It's related to closures.  If you're using lambda, you're probably a 
lisp programmer, and should know all about closures.  Creating a 
function object with def or lambda, within an outer function scope, 
creates a closure for that outer function call.  The inner function's 
accesses to variables from the outer function will return the 
most-recently-updated binding from the closure.  If you call the outer 
function again, you will get a different unrelated closure.

If you the inner function to have its own local variable that stores 
some expression value as it existed at the creation time of the inner 
function, rather than re-evaluating the expression whenever the inner 
function is called, the standard way is to use a defaulted keyword 
parameter:

a = []
for index in range(5):
    a.append(lambda index=index: index)

or maybe more concisely

a = [lambda index=index: index for index in range(5)]

-- 
David Eppstein                      http://www.ics.uci.edu/~eppstein/
Univ. of California, Irvine, School of Information & Computer Science




More information about the Python-list mailing list