En Thu, 07 Jan 2010 19:47:13 -0300, Mitchell L Model <mlm...@comcast.net> escribió:

     def dofile(filename):
        ldict = {'result': None}
        with open(filename) as file:
            exec(file.read(), globals(), ldict)
        print('Result for {}: {}'.format(filename, ldict['result']))

Next I call dofile() on a slightly more complex file, in which one function
calls  another function defined earlier in the same file.

################################
def fn1(val):
     return sum(range(val))

def fn2(arg):
     return fn1(arg)

result = fn2(5)
################################

This produces a surprise:

     NameError: global name 'fn1' is not defined

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 shows that the identity "globals() is locals()" is essential for the module system to work.

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

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

[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)

--
Gabriel Genellina

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

Reply via email to