简体   繁体   English

Python AST exec“...未定义”递归错误 function

[英]Python AST exec “… is not defined” error on recursive function

I came across this error我遇到了这个错误

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")))

This yield this error, which is weird这会产生这个错误,这很奇怪

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

If I copy and paste the same code in REPL it works fine如果我在 REPL 中复制并粘贴相同的代码,它可以正常工作

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

Any ideas?有任何想法吗?

I could reduce the problem further here is the minimal exempla, this would overflow the stack but it gives me the same not defined error我可以进一步减少问题这里是最小的例子,这会溢出堆栈但它给了我同样的未定义错误

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

f()
""")

-- --

Second edit, going even further, this only happens inside functions第二次编辑,更进一步,这只发生在函数内部

This works这有效

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

f(10)""")

But this gives me the same error as above但这给了我与上面相同的错误

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

f(10)""")


foo()

If you use exec with the default locals, then binding local variables is undefined behavior.如果将exec与默认局部变量一起使用,则绑定局部变量是未定义的行为。 That includes def , which binds the new function to a local variable.这包括def ,它将新的 function 绑定到局部变量。

Also, functions defined inside exec can't access closure variables, which fact would be.此外,在exec中定义的函数不能访问闭包变量, fact就是这样。

The best way to avoid these problems is to not use exec .避免这些问题的最好方法是不使用exec The second best way is to provide an explicit namespace:第二种最佳方式是提供显式命名空间:

namespace = {}
exec(whatever, namespace)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM