Why can't I declare a function in exec within another function ? I really have no idea why it's happening, is there a way to make this work. I found this: Invoking problem with function defined in exec() but it's not exactly what I'm looking for, in my case I wan't the exec code to be arbitrary.
def test():
exec("""
def this():
print('this')
def main():
this()
main()
""")
test()
Returns:
Traceback (most recent call last):
File "/t.py", line 12, in <module>
test()
File "/t.py", line 2, in test
exec("""
File "<string>", line 8, in <module>
File "<string>", line 6, in main
NameError: name 'this' is not defined
When I do the exec outside a function it works just fine, why ? :
exec("""
def this():
print('this')
def main():
this()
main()
""")
CodePudding user response:
heres my attempt to make it work
from the error, i am guessing you can create an object by using exec()
in global environment, but cant add an object to global environment using exec()
in a function. thus the system sees it as a local exec()
variable and failed to read it
so i tried to add a global declaration and it worked:
def test():
exec("""
global this
def this():
print('this')
def main():
this()
main()
""")
test()
results:
this
however it may overwrite other global variables because i can now call this()
outside of test()
:
>>> this()
this
CodePudding user response:
TLDR: Provide explicit globals dictionaries to execute code "as if" it were run at top-level.
Python uses lexical scoping, which roughly means that names are looked up as per the nesting in source code. For example, a nested function can access an outer function's variable.
def foo():
bar = 12
def qux():
print(bar) # nested scope accesses outer scope
return qux
Notably, this kind of name lookup happens at source code compilation time – the compiler knows that both foo
and qux
access bar
, so it must be made available to both. The inner function is directly compiled to look at the variable of the outer function.
If instead a function is not nested, its name lookup to non-local variables automatically goes to global scope.
def qux():
print(bar) # top-level scope accesses global scope
This is a problem when we exec
code in a function: A definition such as def main(): this()
is compiled to lookup this
at global scope, but exec
runs in the current local scope of the function.
In all cases, if the optional parts are omitted, the code is executed in the current scope.
Thus, this
is looked up globally but defined locally.1 The compiler parsing def test
and the exec code separately does not know that lookups are meant to be treated as nested.
As a result, the lookup fails.
In order to exec
code "as if" it were run at top-level, it is sufficient to supply an explicit globals
– for example a fresh namespace {}
or the actual globals()
.
def test():
exec("""
def this():
print('this')
def main():
this()
main()
""", {})
# ^^ globals namespace for exec
test()
This means the code is now run in (its own) global namespace, which is where the lookup of functions searches as well.
1This can be checked by printing globals()
/locals()
and by inspecting the instructions of the function. dis.dis(main)
in the exec would produce:
6 0 LOAD_GLOBAL 0 (this)
2 CALL_FUNCTION 0
4 POP_TOP
6 LOAD_CONST 0 (None)
8 RETURN_VALUE