Oxfam America banner

Thursday, October 9, 2008

Lexical Scope

So, briefly on the topic of lexical scope, what does the following print?


def f():
funcs = []
for i in range(5):
def g():
print i
funcs.append(g)
return funcs

funcs = f()
for func in funcs:
func()

4 comments:

ynniv said...

Python scopes at the function level, so framing 'i' as an argument to another local function will produce the expected output.

def f():
    funcs = []
    for i in range(5):
        def close(i):
            def g():
                print i
            return g
        funcs.append(close(i))
    return funcs

funcs = f()
for func in funcs:
    func()

In addition, closures in python are read only, so g can't modify i either. The java-like workaround for that is to use mutable collections, but thats just annoying. JavaScript (originating in Scheme) does this a little better, having mutable closures and a new "let" keyword that provides lexical binding without a function call.

Mark Luffel said...

JavaScript folks run into this one somewhat often, and Vinny's transformation is what I've always used. I hadn't realized that the "let" keyword provided a workaround, that's excellent. JavaScript's "let" also allows tuple/destructuring assignment, which is a python nicety that I occasionally miss.

djfroofy said...

@ynniv That's a particularly nice solution. I'm guessing you're also aware of the partial function in functools (requires Python 2.5) which makes this even more concise:


def f():
funcs = []
for i in range(5):
def g(i):
print i
funcs.append(partial(g, i))
return funcs


I still can't help but laugh when some stubborn OO folks can't see the use of higher order functions!

djfroofy said...

needless to say, i am stumped as to how to preserve whitespace in a blogger comment ... :(

Post a Comment