Python递归函数上的 AST exec“...未定义”错误

Python AST exec "... is not defined" error on recursive function

我遇到了这个错误

def test_rec():
    import ast
    exec(compile(ast.fix_missing_locations(ast.parse("""
def fact(n):
    return 1 if n == 0 else n * fact(n - 1)

print(fact(5))
"""), "<string>", "exec")))

这会产生这个错误,这很奇怪

Traceback (most recent call last):
  File "/Users/gecko/.pyenv/versions/3.9.0/envs/lampy/lib/python3.9/site-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/Users/gecko/code/lampycode/tests/test_let_lang.py", line 6, in test_rec
    exec(compile(ast.fix_missing_locations(ast.parse("""
  File "<string>", line 4, in <module>
  File "<string>", line 3, in fact
NameError: name 'fact' is not defined

如果我在 REPL 中复制并粘贴相同的代码,它可以正常工作

>>> def fact(n):
...     return 1 if n == 0 else n * fact(n - 1)
... 
>>> print(fact(5))
120
>>>

有什么想法吗?

我可以进一步减少这个问题,这里是最小的例子,这会溢出堆栈,但它给我同样的未定义错误

def test_rec3():
    exec("""
def f():
    f()

f()
""")

--

第二次编辑,更进一步,这只发生在函数内部

这个有效

exec("""
def f(n):
    print("end") if n == 1 else f(n-1)

f(10)""")

但这给了我和上面一样的错误

def foo():
    exec("""
def f(n):
    print("end") if n == 1 else f(n-1)

f(10)""")


foo()

如果您将 exec 与默认局部变量一起使用,则绑定局部变量是未定义的行为。其中包括 def,它将新函数绑定到局部变量。

此外,在 exec 中定义的函数不能访问闭包变量,而 fact 可以。

避免这些问题的最好方法是不使用 exec。第二种最好的方法是提供一个明确的命名空间:

namespace = {}
exec(whatever, namespace)