Wednesday, April 13, 2005

how well do you know python, part 4

import os

def foo(s):
    f = lambda a: a + "; print '%s'" % os.getcwd()
    exec(f(s))

foo("print 'asdf'")

What error does this give? Why?

This one is pretty tough. I'll put a simpler illustration that gives the same error in the comments as a hint.

(Updated to fix inadvertent SyntaxError in the exec'd string, pointed out by Ian Bicking. Python bails with the error I intended before reaching that point, though.)

6 comments:

Jonathan Ellis said...

Doh, blogger doesn't allow <pre> in comments... Here it is in nbsp-d glory. (This is the last time I try giving hints. :)

import os

def foo(s):
  f = lambda: os.gtcwd()
  exec(s)

foo("print 'asdf'")

Fredrik said...

Note that it does print "asdf" in 2.0 and earlier. For extra credit, figure out *why* that was changed in 2.1.

Ian Bicking said...

Huh. I figured the first one would bail because it would try to execute "print 'asdf'; print /home/ianb'" which is syntactically incorrect. But not so, since there's a syntax error before that, but I had to cheat and run the program to find that out.

I'm guessing the reason for the error is because f doesn't know if "os" is a local or a global function in the presence of an exec that happens inside the scope of the function. E.g., foo("os=None") would make os a local variable, where otherwise it would be a global variable. Or something. If you use "f = lambda os=os: os.getcwd()" you're all good, because "os" doesn't get looked up in the enclosing scope when f() is called (it's already bound, or rebound by the call). But I'm still fuzzy on the reasoning behind the error.

Anonymous said...

Fredrik wrote: "Note that it does print "asdf" in 2.0 and earlier. For extra credit, figure out *why* that was changed in 2.1."

I can tell you why it won't work in Python 3.0: lambda sucks and will finally be gone. I'd rather have no anonymous methods than a crippled one.

Hans said...

"""I can tell you why it won't work in Python 3.0: lambda sucks and will finally be gone. I'd rather have no anonymous methods than a crippled one."""

It has nothing to do with lambda. The same effect happens when using a regular def statement.

paul cannon said...

Having been pretty immersed in python bytecode during my latest project, I think I can make a good stab at this one: Python won't allow the unqualified exec because the existence of the nested function with free variables means there's a possibility of some local variables being stored in cells. The exec won't be able to tell which ones are cell variables (so it wouldn't know whether to use STORE_FAST or STORE_DEREF, for example). Free variables and the corresponding local variables in the nesting scope are stored in cells, so both frames can have references.