为什么在含有子函数的函数中exec不起作用?

58 投票
6 回答
36775 浏览
提问于 2025-04-16 08:45

看起来你不能在一个有子函数的函数里使用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 个回答

7

这是一个相当有趣的案例:

>>> 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 中被修复了,现在不再抛出异常。

29

虽然在Python中,看起来本地变量好像是存储在一个叫做locals()的字典里,但实际上它们通常不是。大多数情况下,这些变量是存储在一个叫做“栈”的地方,并通过索引来访问。这种方式让查找本地变量的速度比每次都去查字典要快。如果你使用locals()这个函数,你得到的是一个新创建的字典,里面包含了所有的本地变量,这也是为什么给locals()赋值通常不起作用的原因。

这种情况有几个例外:

第一个例外是,当你在一个函数内部使用不带限定符的exec时,Python会关闭优化,使用真正的字典来存储本地变量。这意味着你可以在exec内部创建或更新变量,但这也意味着在这个函数中的所有本地变量访问速度会变得更慢。

第二个例外是,当你嵌套函数时,内部函数可以访问外部函数的本地变量。当内部函数这样做时,变量会存储在一个叫做“单元格”的对象中,而不是存储在栈上。由于多了一层间接访问,无论是从内部函数还是外部函数访问这些变量,速度都会变慢。

你遇到的问题是,这两种本地变量存储的例外情况是互相矛盾的。你不能同时让一个变量存储在字典中并通过单元格引用来访问。Python 2.x通过禁止使用exec来解决这个问题,即使在你并不打算使用任何作用域变量的情况下也是如此。

69

没错。如果一个函数里面有子函数,你不能在这个函数里使用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

撰写回答