为什么Python 3 exec()在指定局部变量时失败?

为什么Python 3 exec()在指定局部变量时失败?,python,python-3.x,exec,python-exec,Python,Python 3.x,Exec,Python Exec,在Python 3中执行以下操作时不会出错: code = """ import math def func(x): return math.sin(x) func(10) """ _globals = {} exec(code, _globals) 但是如果我也尝试捕获局部变量dict,它将失败,并出现namererror: >>> _globals, _locals = {}, {} >>> exec(code, _globals, _loc

在Python 3中执行以下操作时不会出错:

code = """
import math

def func(x):
    return math.sin(x)

func(10)
"""
_globals = {}
exec(code, _globals)
但是如果我也尝试捕获局部变量dict,它将失败,并出现
namererror

>>> _globals, _locals = {}, {}
>>> exec(code, _globals, _locals)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-9-aeda81bf0af1> in <module>()
----> 1 exec(code, {}, {})

<string> in <module>()

<string> in func(x)

NameError: name 'math' is not defined
>>>\u全局变量,\u局部变量={},{}
>>>exec(代码、全局、局部)
---------------------------------------------------------------------------
NameError回溯(最近一次呼叫上次)
在()
---->1 exec(代码,{},{})
在()
在func(x)中
NameError:未定义名称“math”
为什么会发生这种情况,以及如何在捕获全局和局部变量的同时执行此代码?

来自:

请记住,在模块级别,全局变量和局部变量是同一个字典。如果
exec
获取两个单独的对象,即全局对象和局部对象,则代码将像嵌入类定义一样执行

您传入了两个单独的字典,但试图执行要求模块作用域全局变量可用的代码<类中的code>import math将生成一个局部作用域属性,而您创建的函数将无法访问该属性,因为函数闭包不考虑类作用域名称

请参见Python执行模型参考中的:

exec()
eval()
的类定义块和参数在名称解析上下文中是特殊的。类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的正常规则,但在全局名称空间中查找未绑定的局部变量除外。类定义的命名空间将成为该类的属性字典。类块中定义的名称范围仅限于类块;它不扩展到方法的代码块[。]

您可以通过尝试在类定义中执行代码来重现错误:

>>> class Demo:
...     import math
...     def func(x):
...         return math.sin(x)
...     func(10)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in Demo
  File "<stdin>", line 4, in func
NameError: name 'math' is not defined
类演示: ... 输入数学 ... def func(x): ... 返回math.sin(x) ... func(10) ... 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 演示中第5行的文件“” 文件“”,第4行,在func中 NameError:未定义名称“math”
只需交一本字典。

你能把短语“好像它嵌入了类定义”的含义解包出来吗?和/或给出一个具体示例,说明“函数闭包不考虑类作用域名称”@jez:see:class-definition块和
exec()
eval()
的参数在名称解析上下文中是特殊的。类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的正常规则,但在全局名称空间中查找未绑定的局部变量除外。可能值得明确指出的是,这里看到的行为与在类定义中定义方法时看到的行为完全相同(目前的答案确实是这样说的,但从来没有使用“方法”一词),并在类定义中重复问题中的代码以强调重点。另外,将其转换为闭包以使其按所写方式运行的巧妙技巧是:
exec(“def_dummy():\n{}\n_dummy()”。format(“\n).join(code.strip().splitlines()),{},{})
@ncoghlan:从技术上讲,你是在类中定义函数的。它们只有在实例中查找函数时才会变成方法,触发描述符协议来绑定它们。@Martijn:我在
exec
doc中添加了这句话。你解释得很好。