I need to write a custom exec function in python (for several purposes but this is not the problem here, so this custom exec which is called myExec
will do exactly as exec
for now).
I went into this problem :
def myExec(code):
exec(code)
code = """
a = 1
print(a)
u = [a for x in range(3)]
print(u)
"""
myExec(code)
Running this program gives
1
Traceback (most recent call last):
File "___.py", line 12, in <module>
myExec(code)
File "___.py", line 2, in myExec
exec(code, globals(), locals())
File "<string>", line 4, in <module>
File "<string>", line 4, in <listcomp>
NameError: name 'a' is not defined
So print(a)
went without any problems. But the error occurs with the line u = [a for x in range(3)]
. When the generator object is converted into a list, the name a
seems undefined.
Note that if the line were u = [a, a, a]
, then, no error is raised. Nor if we use exec
instead of myExec
.
Any reason why and how to solve this ?
We can explain this behavior if we take a look at the decompiled code.
from dis import dis
def myExec(code):
dis(code)
a = 1
is compiled to STORE_NAME
, so it stores a
as a local variable hereprint(a)
uses LOAD_NAME
to load the local a
. It is a local variable, so LOAD_NAME
finds it.LOAD_GLOBAL
for a
That's where the error is coming from. a
was created as a local variable and is accessed as a global one in the list comprehension. This results in a name error.
This also explains why the exec works in the global scope (either by calling exec
in the global scope or by passing globals()
). Because then STORE_NAME
stores a
in the current scope (which is global) and LOAD_GLOBAL
can find a
.
If you switch to Python3.12 which implements PEP 709 – Inlined comprehensions you will see that no extra function is created for the list comprehension and a
is looked up with LOAD_NAME
and can be found.
So to fix your issue: either upgrade to Python3.12 or pass the globals()