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:

  1. 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.
    ReplyDelete
  2. 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.
    ReplyDelete
  3. @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!
    ReplyDelete
  4. needless to say, i am stumped as to how to preserve whitespace in a blogger comment ... :(
    ReplyDelete