On Jan 8, 2010, at 9:55 AM, "Gabriel Genellina" <gagsl- p...@yahoo.com.ar> wrote:


Ok - short answer or long answer?

Short answer: Emulate how modules work. Make globals() same as locals(). (BTW, are you sure you want the file to run with the *same* globals as the caller? It sees the dofile() function and everything you have defined/imported there...). Simply use: exec(..., ldict, ldict)

[1] How is it that fn2 can be called from the top-level of the script but fn1
cannot be called from fn2?

Long answer: First, add these lines before result=fn2(5):

print("globals=", globals().keys())
print("locals=", locals().keys())
import dis
dis.dis(fn2)

and you'll get:

globals()= dict_keys(['dofile', '__builtins__', '__file__', '__package__', '__name__', '__doc__'])
locals()= dict_keys(['result', 'fn1', 'fn2'])

So fn1 and fn2 are defined in the *local* namespace (as always happens in Python, unless you use the global statement). Now look at the code of fn2:

 6           0 LOAD_GLOBAL              0 (fn1)
             3 LOAD_FAST                0 (arg)
             6 CALL_FUNCTION            1
             9 RETURN_VALUE

Again, the compiler knows that fn1 is not local to fn2, so it must be global (because there is no other enclosing scope) and emits a LOAD_GLOBAL instruction. But when the code is executed, 'fn1' is not in the global scope...

Solution: make 'fn1' exist in the global scope. Since assignments (implied by the def statement) are always in the local scope, the only alternative is to make both scopes (global and local) the very same one.

This is very helpful additional information and clarification! Thanks.


This shows that the identity "globals() is locals()" is essential for the module system to work.

Yes, though I doubt more than a few Python programmers would guess that identity.


[2] Is this correct behavior or is there something wrong with Python here?

It's perfectly logical once you get it... :)

I think I'm convinced.


[3] How should I write a file to be exec'd that defines several functions that
call each other, as in the trivial fn1-fn2 example above?

Use the same namespace for both locals and globals: exec(file.read(), ldict, ldict)


I was going to say that this wouldn't work because the script couldn't use any built-in names, but the way exec works if the value passed for the globals argument doesn't contain an entry for '__builtins__' it adds one. I would have a further problem in that there are some names I want users to be able to use in their scripts, in particular classes that have been imported into the scope of the code doing the exec, but come to think of it I don't want to expose the entire globals() anyway. The solution is do use the same dictionary for both globals and locals, as you suggest, to emulate the behavior of module import, and explicitly add to it the names I want to make available (and since they are primarily classes, there are relatively few of those, as opposed to an API of hundreds of functions). Thanks for the help.

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to