A problem with exec statement

Steve Holden steve at holdenweb.com
Fri Apr 14 03:16:56 EDT 2006


TPJ wrote:
> I have the following code:
> 
> -----------------------------------
> def f():
> 
>   def g():
>     a = 'a'             # marked line 1
>     exec 'a = "b"' in globals(), locals()
>     print "g: a =", a
> 
>   a = 'A'               # marked line 2
>   exec 'a = "B"' in globals(), locals()
>   print "f: a =", a
>   g()
> 
> f()
> -----------------------------------
> 
> I don't understand, why its output is:
> 
> f: a = A
> g: a = a
> 
> instead of:
> 
> f: a = B
> g: a = b
> 
> All works as intended, if the marked lines are commented out. I just
> don't understand, why. (I suppose I don't understand, how the exec
> statement works, or the way Python handles objects, their names and
> namespaces...) In my opinion (according to my knowledge about Python),
> with or without the marked lines commented, the code should work the
> same. Well - I think I have to learn more about Python...
> 
That's true of almost everybody reading this list, myself included.

> According to my knowledge, the most important are the namespaces: the
> local ones, in this case. When Python calls the f function, its
> namespace is created. This namespace contains only the g function.
> Then the a variable is created (and the "a" name is added to the f
> function namespace).
> 
That's a pretty good summary. In fact just after the call to f is 
started its namespace doesn't even contain "g", that's added by 
executing the def statement that defines g.

The assignment does indeed create the name "a" in the function's (local) 
namespace.

> The next statement is the exec one. Since the statement "knows" the
> local namespace (obtained from the locals() function), it should
> replace the value of the a variable in the local namespace with the
> value of the new string "B". I don't understand, why this is not done.
> 
So when you exec 'a = "B"' in globals(), locals() you might think you 
were changing the local namespace. In fact you are changing a *copy* of 
the local namespace: if you read the documentation carefully you will 
see under locals() it says """Warning: The contents of this dictionary 
should not be modified; changes may not affect the values of local 
variables used by the interpreter."""

> The situation in the g function is similar, the only difference is
> that the local namespace contains the "a" name, that refers to a
> different Python object.
> 
The same answer presumably pertains here. If you modify your code to read:

def f():

   def g():
     a = 'a'             # marked line 1
     print "globals:", globals(), '\nlocals:', locals()
     exec 'a = "b"' in globals(), locals()
     print "globals:", globals(), '\nlocals:', locals()
     print "g: a =", a

   a = 'A'               # marked line 2
   print "Globals:", globals(), '\nLocals:', locals()
   exec 'a = "B"' in globals(), locals()
   print "Globals:", globals(), '\nLocals:', locals()
   print "f: a =", a
   g()

f()

you will see quite clearly that you aren't making the changes you 
anticipate to the local namespace. I hope I have explained why.

One last note. Newcomers to Python often seem fascinated by the ability 
to use exec to achieve namespace indirection. Even allowing for the 
difficulties you've already experienced, it's nearly always better in 
practical cases to use assignment to the keys of a dictionary. Then no 
exec is required, and you have direct control over your own namespace.

regards
  Steve

-- 
Steve Holden       +44 150 684 7255  +1 800 494 3119
Holden Web LLC/Ltd                 www.holdenweb.com
Love me, love my blog         holdenweb.blogspot.com




More information about the Python-list mailing list