execfile()不能可靠地用于修改函数的本地

时间:2013-04-17 16:34:12

标签: python function global local execfile

python文档声明“execfile()不能可靠地用于修改函数的本地。”在页面http://docs.python.org/2/library/functions.html#execfile

有人可以提供有关此声明的任何进一步细节吗?文档相当少。该语句似乎与“如果省略两个字典,表达式在调用execfile()的环境中执行”非常矛盾。“这也在文档中。在函数中使用excecfile时是否有特殊情况,然后execfile的作用类似于函数,因为它创建了一个新的作用域级别?

如果我在

这样的函数中使用execfile
def testfun():
    execfile('thefile.py',globals())
    def testfun2():
        print a

并且'thefile.py'中的命令创建了对象(例如对象'a'),我怎么知道它们是testfun还是全局对象的本地对象?那么,在函数testfun2中,'a'似乎是全局的?如果我从execfile语句中省略globals(),那么任何人都可以更详细地解释为什么'thefile.py'中的命令创建的对象不能用于'testfun'吗?

2 个答案:

答案 0 :(得分:5)

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

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

在内部,Python使用LOAD_FAST操作码通过索引在当前帧中查找a名称,而不是使用较慢的LOAD_NAME。查找本地名称(按名称),然后在globals()映射中找不到第一个名称。

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()更改函数中的本地名称通常不起作用。

答案 1 :(得分:1)

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

现在,如果你在一个函数中使用exec,在Python 2.x中,Python知道不会这样做,而是回到了通过名称访问局部变量的较慢方法,你可以通过编程方式创建新的。 (这个技巧在Python 3中已被删除。)你会认为Python也会为execfile()执行此操作,但事实并非如此,因为exec是一个语句而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']从执行的文件中访问变量,依此类推。