func_code vs. string problem

Steven Bethard steven.bethard at gmail.com
Sat Apr 23 18:13:40 EDT 2005


Filip Dreger wrote:
> Uzytkownik "Steven Bethard" <steven.bethard at gmail.com> napisal w 
> wiadomosci news:04mdnXLeedaPLvffRVn-vQ at comcast.com...
> 
>>See the documentation:
>>
>>http://docs.python.org/ref/dynamic-features.html
>>
>>"""The eval(), execfile(), and input() functions and the exec 
>>statement do not have access to the full environment for resolving 
>>names. Names may be resolved in the local and global namespaces of 
>>the caller. Free variables are not resolved in the nearest enclosing 
>>namespace, but in the global namespace."""
> 
> Thank you! I feel silly I have not found the piece of instruction 
> myself. And even worse, as I still do not understand: if exec() 
> resolves free bariable names in the global namespace, how come it 
> works well with code in a string? Or with a code in a compiled string?

Well, probably the best thing to do is to check out what byte-code is 
generated in each case:

py> s = compile('print b', 'string', 'exec')
py> def f():
...     print b
...
py> import dis
py> dis.dis(s)
   1           0 LOAD_NAME                0 (b)
               3 PRINT_ITEM
               4 PRINT_NEWLINE
               5 LOAD_CONST               0 (None)
               8 RETURN_VALUE
py> dis.dis(f)
   2           0 LOAD_GLOBAL              0 (b)
               3 PRINT_ITEM
               4 PRINT_NEWLINE
               5 LOAD_CONST               0 (None)
               8 RETURN_VALUE

So, the code is basically identical, except that with the string, the 
code uses LOAD_NAME, while with the function, the code uses LOAD_GLOBAL. 
  Note that this is because Python can tell that b is not defined in the 
function, and so it assumes that is must be a global lookup.  Look what 
happens if I give f a parameter:

py> def f(b):
...     print b
...
py> dis.dis(f)
   2           0 LOAD_FAST                0 (b)
               3 PRINT_ITEM
               4 PRINT_NEWLINE
               5 LOAD_CONST               0 (None)
               8 RETURN_VALUE

Now f is using LOAD_FAST, which looks at the locals.  I believe the 
point of LOAD_GLOBAL and LOAD_FAST (instead of always using LOAD_NAME) 
is to increase performance in the common case, where exec is not used.

Seems like the documentation is being a little conservative -- it does 
look like some statements will do the lookup in the locals too...  If I 
was more confident about what goes on here, I'd probably try to file a 
documentation bug...

> The manual says I can pass two dictionaries to exec, one for global 
> namespace and one for local. But, strange as it seems, I could not get 
> it to work. In the example I gave, changing exec f.func_code to exec 
> f.func_code in globals(),locals() does not help a bit.

That's because, as the dis.dis output above shows, the function you've 
defined is only searching the global namespace.  So the locals() dict 
you pass won't ever be looked at.

[snip example using locals as globals]
> 
> I thought about that... I don't know why, but it seems wrong. Maybe 
> the updating dictionaries is not very expensive, but there must be 
> some better way. This code has a 'workaround - fixme' written all over 
> it.

Yeah, I agree.  On the other hand, whenever I see exec, it has 
'workaround - fixme' written all over it too. ;)

STeVe



More information about the Python-list mailing list