One function calling another defined in the same file being exec'd

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Fri Jan 8 09:43:33 EST 2010


En Thu, 07 Jan 2010 19:47:13 -0300, Mitchell L Model <MLMDev at 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




More information about the Python-list mailing list