为什么在含有子函数的函数中exec不起作用?
看起来你不能在一个有子函数的函数里使用exec...
有人知道为什么这段Python代码不工作吗?我在test2里的exec那行遇到了错误。而且,我知道使用exec不是个好习惯,但相信我,我是有正当理由才用它的,不然我不会这么做。
#!/usr/bin/env python
#
def test1():
exec('print "hi from test1"')
test1()
def test2():
"""Test with a subfunction."""
exec('print "hi from test2"')
def subfunction():
return True
test2()
补充:我把问题缩小到是在子函数里有一个函数。这和raise这个关键词没有关系。
6 个回答
这是一个相当有趣的案例:
>>> def func():
... exec('print "hi from func"')
... def subfunction():
... return True
...
File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'func' because
it contains a nested function with free variables
之所以这个代码不工作,实际上是因为 subfunction
里面有一个自由变量。由于在 Python 2 中,exec
理论上可以修改外层作用域的局部变量,所以很难判断这个变量应该从全局变量还是父函数的作用域中获取。Python 的哲学中有一句话是:"面对模糊的情况,拒绝猜测的诱惑." 这就是 Python 2 的做法。
那么,这个自由(未绑定)变量是什么呢?其实就是 True
!
实际上,用 None
也能复现这个问题:
>>> def func():
... exec('print "hi from func"')
... def subfunction():
... return None
...
File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'test2' because it contains a nested
function with free variables
尽管 None
是不能被赋值的,并且在字节码中被视为一个 常量,但有问题的解析器在这里认为它是一个未绑定的变量。
不过,如果你把它换成 1
,就能正常工作了:
>>> def test2():
... exec('print "hi from func"')
... def subfunction():
... return 1
...
>>>
为了避免这个错误,可以明确指定 exec
要使用的全局变量和可能的局部变量,比如:
>>> def test2():
... exec 'print "hi from test2"' in {}
... def subfunction():
... return None
...
>>>
在 Python 3 中,exec
只是一个简单的函数,不会被解析器或字节码编译器特别处理。在 Python 3 中,exec
不能重新绑定函数内部的名称,因此这个语法错误和模糊性就不存在了。
在 Python 2 和 3 的兼容性中,有一个特别的情况,就是虽然 Python 2.7 的文档 说明:
形式
exec(expr, globals)
等价于exec expr in globals
,而形式exec(expr, globals, locals)
等价于exec expr in globals, locals
。元组形式的exec
提供了与 Python 3 的兼容性,因为在 Python 3 中,exec
是一个函数,而不是一个语句。
元组形式并不总是 100% 兼容,因为在处理带有嵌套函数的 exec
时曾经有 一个 bug(问题 21591);在 Python 2.7.8 之前,以下代码可能会抛出异常:
def func():
exec('print "hi from test2"', {})
def subfunction():
return None
这个问题在 Python 2.7.9 中被修复了,现在不再抛出异常。
虽然在Python中,看起来本地变量好像是存储在一个叫做locals()
的字典里,但实际上它们通常不是。大多数情况下,这些变量是存储在一个叫做“栈”的地方,并通过索引来访问。这种方式让查找本地变量的速度比每次都去查字典要快。如果你使用locals()
这个函数,你得到的是一个新创建的字典,里面包含了所有的本地变量,这也是为什么给locals()
赋值通常不起作用的原因。
这种情况有几个例外:
第一个例外是,当你在一个函数内部使用不带限定符的exec
时,Python会关闭优化,使用真正的字典来存储本地变量。这意味着你可以在exec
内部创建或更新变量,但这也意味着在这个函数中的所有本地变量访问速度会变得更慢。
第二个例外是,当你嵌套函数时,内部函数可以访问外部函数的本地变量。当内部函数这样做时,变量会存储在一个叫做“单元格”的对象中,而不是存储在栈上。由于多了一层间接访问,无论是从内部函数还是外部函数访问这些变量,速度都会变慢。
你遇到的问题是,这两种本地变量存储的例外情况是互相矛盾的。你不能同时让一个变量存储在字典中并通过单元格引用来访问。Python 2.x通过禁止使用exec
来解决这个问题,即使在你并不打算使用任何作用域变量的情况下也是如此。
没错。如果一个函数里面有子函数,你不能在这个函数里使用exec,除非你指定一个上下文。根据文档的说明:
如果在一个函数中使用exec,而这个函数里面有一个包含自由变量的嵌套块,编译器会抛出一个语法错误,除非exec明确指定了本地命名空间。换句话说,“exec obj”是非法的,但“exec obj in ns”是合法的。
这样做是有原因的,我可能会理解这些原因,如果今天不是星期天晚上。接下来,问你一个问题:你为什么要使用exec?这种情况非常少见。你说你有个好理由,我对此有点怀疑。😉 如果你真的有好理由,我可以告诉你解决办法。😜
好吧,反正这里有解决办法:
def test2():
"""Test with a subfunction."""
exec 'print "hi from test2"' in globals(), locals()
def subfunction():
return True