【问题标题】:Python exec with a function chain producing NameError带有产生 NameError 的函数链的 Python exec
【发布时间】:2020-10-29 04:08:02
【问题描述】:

考虑以下脚本,它使用exec 定义两个函数,其中一个调用另一个:

def run_code():
  code = """
def foo():
  print('foo')
  return 1

def bar():
  print('bar calls foo')
  return 1 + foo()

result = bar()
"""

  exec(code, globals(), locals())
  print('Result: {}'.format(locals()['result']))

run_code()

我希望看到以下输出:

bar calls foo
foo
Result: 2

但相反,我得到以下输出+堆栈跟踪:

bar calls foo
Traceback (most recent call last):
  File "minimal.py", line 17, in <module>
    run_code()
  File "minimal.py", line 14, in run_code
    exec(code, globals(), locals())
  File "<string>", line 10, in <module>
  File "<string>", line 8, in bar
NameError: name 'foo' is not defined

有趣的是,如果run_code 的内容被移动到模块级别,那么它工作正常。但是,如果我随后用新的空字典替换 globals()locals(),它会再次中断。我也知道将def foo 放在bar 的体内会起作用。

为什么会发生此错误,正确的解决方法是什么?

(我知道exec 通常不受欢迎。我使用它是有充分理由的。)

【问题讨论】:

  • 修改 locals() 是未定义的行为,而您正在这里这样做。
  • @user2357112supportsMonica 如果我用新的空字典替换 locals()globals(),问题仍然存在。

标签: python python-3.x python-exec


【解决方案1】:

来自documentation

如果提供,locals 可以是任何映射对象。请记住,在模块 level、globals 和 locals 是同一个字典。如果 exec 得到两个 将对象作为全局对象和局部对象分开,代码将被执行为 如果它嵌入到类定义中。

并且类定义不创建封闭范围,注意,这就是为什么不使用self 就不能从另一个方法调用方法的原因。所以只需传递globals() 字典。或者将两个相同的 dict 传递给两个参数。

In [4]: def run_code():
   ...:     code = """
   ...: def foo():
   ...:   print('foo')
   ...:   return 1
   ...:
   ...: def bar():
   ...:   print('bar calls foo')
   ...:   return 1 + foo()
   ...:
   ...: result = bar()
   ...: """
   ...:     namespace = {}
   ...:     exec(code, namespace)
   ...:     print('Result: {}'.format(namespace['result']))
   ...:

In [5]: run_code()
bar calls foo
foo
Result: 2

【讨论】:

    【解决方案2】:
    code = """  
    def foo():
      print('foo')
      return 1
    
    def bar():
      global foo;
      print('bar calls foo')
      return 1 + foo()
    
    result = bar()
    """
    def run_code():
        exec(code, globals(), locals())
        print('Result: {}'.format(locals()['result']))
    
    
    run_code()
    

    输出:

    bar calls foo
    foo
    Result: 2
    

    【讨论】:

    • 等等,这到底是怎么回事 - global 不应该那样工作。
    • 我认为这实际上是一个错误。 global foobar 导致 def foo()foo 存储在全局而不是本地,但 global 声明只应该影响到 foobar 的分配。
    猜你喜欢
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多