One function calling another defined in the same file being exec'd
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Thu Jan 7 20:57:53 EST 2010
On Thu, 07 Jan 2010 17:47:13 -0500, Mitchell L Model wrote:
> 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
>
> [1] How is it that fn2 can be called from the top-level of the script
> but fn1 cannot be called from fn2?
This might help you to see what's going on. Define your own cut-down
version of the global namespace, and a local namespace, and a string to
execute:
myglobals = {'__builtins__': None, 'globals': globals, 'locals': locals,
'print': print}
mylocals = {'result': None}
s = """def f():
print("Globals inside f:", globals())
print("Locals inside f:", locals())
print("Globals at the top level:", globals())
print("Locals at the top level:", locals())
f()
"""
exec(s, myglobals, mylocals)
And this is what you should see:
Globals at the top level: {'__builtins__': None, 'print': <built-in
function print>, 'globals': <built-in function globals>, 'locals': <built-
in function locals>}
Locals at the top level: {'result': None, 'f': <function f at 0xb7ddeeac>}
Globals inside f: {'__builtins__': None, 'print': <built-in function
print>, 'globals': <built-in function globals>, 'locals': <built-in
function locals>}
Locals inside f: {}
Does that clarify what's going on?
> [2] Is this correct behavior or is there something wrong with Python
> here?
This certainly surprised me too. I don't know if it is correct or not,
but it goes back to at least Python 2.5.
> [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?
My preference would be to say, don't use exec, just import the module.
Put responsibility on the user to ensure that they set a global "result",
and then just do this:
mod = __import__('user_supplied_file_name')
result = mod.result
But if that's unworkable for you, then try simulating the namespace setup
at the top level of a module. The thing to remember is that in the top
level of a module:
>>> globals() is locals()
True
so let's simulate that:
myglobals = {'result': None} # You probably also want __builtins__
s = """def f():
return g() + 1
def g():
return 2
result = f()
"""
exec(s, myglobals, myglobals)
myglobals['result']
This works for me.
--
Steven
More information about the Python-list
mailing list