为什么Python exec中的模块级变量不可访问?

时间:2014-07-12 10:53:50

标签: python

我试图在project中使用Python exec来执行嵌入式Python代码。

我遇到的问题是在exec语句中模块级创建的变量无法访问来自同义词中定义的函数模块。

假设你有以下Python程序:

x = 5
def foo():
    print x
foo()

如果你将上面四行放在一个文件中并运行它,那就没问题了。

但是,如果您尝试在exec声明中运行同一段代码,那么它将无法正常工作。

这是我们之前的程序,在exec声明中:

import __builtin__

global_env = {'__builtins__': __builtin__}
local_env = dict()

exec """
x = 5
def foo():
    print x
foo()
""" in global_env, local_env

执行时,它会产生以下错误,而不是工作:

Traceback (most recent call last):
  File "lab.py", line 94, in <module>
    """ in global_env, local_env
  File "<string>", line 5, in <module>
  File "<string>", line 4, in foo
NameError: global name 'x' is not defined

我认为模块级变量是全局存储的,但似乎至少在exec中,它们并非如此。

例如,在上一个示例中,如果将foo()的调用替换为:

print global_env
print local_env

你得到:

{'__builtins__': <module '__builtin__' (built-in)>}
{'x': 5, 'foo': <function foo at 0x102c12938>}

因此,模块级别中定义的任何内容(包括x)都存储在locals()中。

但是除了模块级别(x语句)之外,无法从任何地方访问exec。特别是,正如我们上面所看到的,x的局部范围对于在同一exec语句中定义的函数是不可见的。

变通方法

我找到了两种方法来解决此问题,并再次访问x

第一个是在函数中使用global关键字:

exec """
x = 5
def foo():
    global x
    print x
foo()
""" in global_env, local_env

第二个是在globals()中使用locals()exec的相同词典:

exec """
x = 5
def foo():
    print x
foo()
""" in global_env

但是,这些只是解决原始问题的一半修复/解决方法。

所以我的问题是:为什么exec中的模块级变量存储在本地,而为什么除了模块级之外的任何地方都无法访问?

一些密切相关的StackOverflow帖子:

3 个答案:

答案 0 :(得分:6)

要了解发生了什么,您需要非常仔细地阅读the docs。关键部分说:

  

如果给出两个单独的对象作为全局变量和局部变量,则代码将被执行,就像它嵌入在类定义中一样。

这意味着本地赋值将进入本地命名空间(相当于类级变量),但如果函数(即方法)尝试引用本地(类)变量,则它们不会成为闭包。

将您的代码与:

进行比较
class Test(object):
    x = 1
    def foo():
        print x
    foo()

出于同样的原因,你会得到同样的错误。 foo不是闭包,因此它尝试在全局命名空间中引用x(失败)。

答案 1 :(得分:4)

您看到的行为是well documented

  

在所有情况下,如果省略可选部分,则执行代码   在目前的范围内。如果只有in之后的第一个表达式   指定,它应该是一个字典,将用于两者   全局和局部变量。如果给出两个表达式,它们就是   分别用于全局变量和局部变量。如果提供,   locals可以是任何映射对象。请记住在模块级别,   globalslocals是相同的字典。 如果是两个单独的对象   作为全局变量和本地变量给出,代码将像执行一样执行   被嵌入到课程定义中。

事实上:

In [1]: class Test:
   ...:     x = 5
   ...:     def foo():
   ...:         print(x)
   ...:     foo()
   ...:     
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-f20229bce3a1> in <module>()
----> 1 class Test:
      2     x = 5
      3     def foo():
      4         print(x)
      5     foo()

<ipython-input-1-f20229bce3a1> in Test()
      3     def foo():
      4         print(x)
----> 5     foo()
      6 

<ipython-input-1-f20229bce3a1> in foo()
      2     x = 5
      3     def foo():
----> 4         print(x)
      5     foo()
      6 

NameError: name 'x' is not defined

您看到的行为是预期的。如果您想要像在模块级别那样执行代码,那么必须对全局和本地使用相同的对象,因此您的解决方法就是您应该做的。

答案 2 :(得分:3)

您的问题包含x是模块级变量的假设,但实际上并非如此。

在模块级别globals()locals()是相同的(这可能是CPython特有的),而在功能范围内它们是不同的:

def foo():
    print globals() is locals()

print globals() is locals()
foo()

exec语句指定范围并不在模块级执行代码,它会使用您告诉它执行的范围执行它。因此,您的上一个代码段不是半修复或解决方法,它正好解决了这个问题。

以下内容在模块级别显示时也有效。它与您的任何代码具有不同的含义,因为它分配给模块中的xfoo,因此 使x成为模块级变量:

exec '''
x = 5
def foo():
    print x
foo()
'''

最后一个代码出现在函数中时不起作用的原因,以及为什么你的第一次尝试不起作用,这是Blckknght的答案所说的。具有单独范围的exec执行&#34;就好像在类定义&#34;中,而不是&#34;就像在模块中一样&#34;或者&#34;好像在一个函数&#34;中。这意味着您在其中定义的任何功能都无法访问其周围的命名空间。他们访问自己的本地命名空间和exec语句的全局命名空间,这两者都不是定义x的地方。