exec(), execfile() and local-variable binding?

Jonathan jonaomi at excite.com
Sun Aug 10 08:35:54 CEST 2003

jonaomi at excite.com (Jonathan) wrote in message news:<c0055aea.0308091519.40ff00fe at posting.google.com>...
> I'm puzzled by Python's behavior when binding local variables which
> are introduced within exec() or execfile() statements. [...]

I now think I understand the gist of the problem. A Google group
search turned up some very similar past discussions. Essentially,
execfile() is a function, and so can't (reliably) change the
local-variable dictionary (locals()) which is passed into it. So any
local-variable settings which happen in the file which execfile()
executes are lost when it returns.

In contrast, exec() is a *statement*, not a function. Therefore, the
locals() dictionary passed into it is mutable. So variable assignments
in the string of an exec() *will* affect the local-variable dictionary
of the function which calls it. Python's usual static compile-time
variable binding can't cope with these potential dynamically-defined
variables. So therefore, if any exec() statement is seen in a function
body at compile-time, static binding is switched off, and a slower
form of run-time binding is used. This dynamically searches the
enclosing variable scopes (dictionaries) for variable definitions.

The run-time binding triggered by exec() explains a weird effect which
I stumbled on after I posted my original message. That is, the
execfile() version of the code *will* work if an exec() appears
anywhere before or after it in the f() function definition. This is
because run-time binding is now used for the whole function - even for
the execfile(). That's why I said above that execfile() can't
*reliably* change the calling function's local-variable dictionary -
it can if this run-time-binding mode is in effect.

Such sneaky insertion of a dummy exec() is not great form, though. The
recommended way to allow execfile() code to affect current variables
is to pass in an explicit context dictionary as an argument to
execfile(). Then you are free to use that dictionary as you see fit
(notably, using it for other exec()'s or execfile()'s). Another way to
implement more of a #include-type effect is to replace this:
with this:
    exec open("blah.py").read()

However, once again, this solution will trigger the use of the slower
run-time binding for the whole enclosing function.

I trust the experts out there will comment if I misrepresented any of
these issues.

-- Jonathan

More information about the Python-list mailing list