2

python 文档指出“execfile() 不能可靠地用于修改函数的局部变量。” 在页面http://docs.python.org/2/library/functions.html#execfile

谁能提供有关此声明的更多详细信息?文档相当少。该语句似乎与“如果省略两个字典,则表达式在调用 execfile() 的环境中执行”非常矛盾。这也在文档中。在函数中使用 execfile 时是否存在特殊情况,然后 execfile 的行为类似于函数,因为它创建了一个新的范围级别?

如果我在函数中使用 execfile,例如

def testfun():
    execfile('thefile.py',globals())
    def testfun2():
        print a

并且有由'thefile.py'中的命令创建的对象(例如对象'a'),我怎么知道它们是testfun的本地对象还是全局对象?那么,在函数 testfun2 中,'a' 会看起来是一个全局变量吗?如果我从 execfile 语句中省略 globals(),谁能更详细地解释为什么“thefile.py”中的命令创建的对象对“testfun”不可用?

4

2 回答 2

6

在 Python 中,查找名称的方式在函数内部进行了高度优化。副作用之一是返回的映射locals()为您提供了函数内部本地名称的副本,并且更改该映射实际上不会影响函数:

def foo():
    a = 'spam'
    locals()['a'] = 'ham'
    print(a)              # prints 'spam'

在内部,Python 使用操作码通过 indexLOAD_FAST查找a当前帧中的名称,而不是较慢的,后者会查找本地名称(按名称),如果在第一个中找不到,则在映射中查找。LOAD_NAMEglobals()

python 编译器只能为编译时LOAD_FAST已知的本地名称发出操作码;但是,如果您允许直接影响函数的本地人,那么您无法提前知道所有本地名称。使用范围名称(自由变量)的嵌套函数使事情变得更加复杂。locals()

在 Python 2 中,您可以通过在函数中使用语句来强制编译器关闭优化并LOAD_NAME始终使用:exec

def foo():
    a = 'spam'
    exec 'a == a'         # a noop, but just the presence of `exec` is important
    locals()['a'] = 'ham'
    print(a)              # prints 'ham'

在 Python 3 中,exec已被替换exec()并且解决方法消失了。在 Python 3中,所有函数都进行了优化。

如果您没有遵循所有这些,那也没关系,但这就是为什么文档会稍微掩盖这一点的原因。这完全归功于大多数 Python 用户不需要了解的 CPython 编译器和解释器的实现细节;所有你需要知道的是,使用locals()来更改函数中的本地名称通常不起作用。

于 2013-04-17T16:45:29.673 回答
1

本地人在 Python 中有点奇怪。常规局部变量通常通过字节码中的索引而不是名称访问(因为这样更快),但这意味着 Python 必须在编译时知道所有局部变量。这意味着您不能在运行时添加新的。

现在,如果你exec在函数中使用,在 Python 2.x 中,Python 知道不这样做,并回退到按名称访问局部变量的较慢方法,你可以以编程方式创建新变量。execfile()(这个技巧在 Python 3中exec被删除了。)你可能认为 Python 也会execfile()execfile.运行时(毕竟可以重新分配)。

您的示例函数会发生什么?好吧,试试看吧!作为execfile状态的文档,如果您不传入本地字典,则将使用您作为全局变量传入的字典。您传入globals()(您的模块的真正全局变量),因此如果它分配给a,则a成为全局变量。

现在你可以尝试这样的事情:

def testfun():
    execfile('thefile.py')
    def testfun2():
        print a
    return testfun2
    exec ""

最后的exec语句强制testfun()使用旧式的基于名称的局部变量。它甚至不必执行,因为它不在这里;它只需要在某处的功能中。

但这也不起作用,因为基于名称的局部变量不支持带有自由变量的嵌套函数(a在这种情况下)。该功能要求 Python 在函数定义时了解所有局部变量。你甚至不能定义上面的函数——Python 不会让你定义的。

简而言之,尝试以编程方式处理局部变量是一种痛苦,并且文档是正确的:execfile()不能可靠地用于修改函数的局部变量。

一个更好的解决方案可能是import将文件作为一个模块。您可以在函数中执行此操作,然后以通常的方式访问模块中的值。

def testfun():
    import thefile
    print thefile.a

如果您直到运行时才知道要导入的文件的名称,则可以__import__改用。此外,您可能需要修改sys.path以确保要从中导入的目录位于路径的第一个位置(然后可能会放回原处)。

您也可以只传入自己的字典execfile,然后使用等访问已执行文件中的变量myVarsDict['a']

于 2013-04-17T16:52:38.477 回答