尽管使用解释器成功,但无法使用exec从函数动态导入

时间:2017-03-01 01:41:19

标签: python python-import

我在使用函数导入某些内容时遇到了一些问题,尽管可以在解释器中执行此操作。

想象一下,文件夹A中有一个文件input.py,而该文件又与我的脚本位于同一目录中。在此文件中,我们定义变量' B'。

B = 5

当我进入解释器时,以下命令为我提供了正确的B

>>> import sys
>>> sys.path.append('A')
>>> exec('from inputs import *')
>>> print(B)

但是,如果我将该代码移到单独的文件中,请说“test.py'”

import sys

def import_stuff(import_dir):
    sys.path.append(import_dir)
    exec('from inputs import *')
    print(B)

然后从解释器中调用它:

>>> import test
>>> test.import_stuff('A')

我收到一个NameError,找不到B.发生了什么事?

1 个答案:

答案 0 :(得分:0)

函数中的局部变量与全局变量和对象属性(它们都使用字典将名称映射到值)的处理方式不同。

定义函数时,Python编译器会检查其代码并记下使用的局部变量名称,并为每个变量名称指定一个“槽”。调用该函数时,插槽指的是帧对象中的部分内存。局部变量赋值和查找按编号访问插槽,而不是按名称访问。这使得局部变量查找明显快于全局变量和属性查找(因为索引槽比执行dict查找要快得多)。

当您尝试使用exec创建局部变量时,它会绕过插槽。编译器不知道将在exec'代码中创建哪些变量,因此没有为它们分配插槽。这也是Python 3不允许在函数内部使用from module import *的原因:在函数的编译时不知道要导入的名称(仅在运行它并导入导入的模块时)编译器无法为名称设置插槽。

即使您在exec代码中单独初始化了您希望分配给的名称的局部变量,它仍然无效。 exec'代码不知道它是从函数中运行的,并且总是想要将变量写入字典(永远不要使用插槽)。函数确实有一个本地命名空间字典,它可以捕获赋值(它们不会成为全局变量),但是通过locals()函数使用它是非常不稳定的。每次调用locals()时,存储在插槽中的所有局部变量的值都会被复制到字典中,但是不会发生另一个方向的复制(修改从locals()返回的字典不起作用存储在插槽中并以正常方式访问的变量值。)

这让我想到了解决这个问题的最佳方法。而不是让exec修改当前的命名空间(如果你在一个函数中,它会以一种不稳定的方式发生),你应该显式传入一个字典,以便用作它的命名空间。

def import_stuff(import_dir):
    sys.path.append(import_dir)
    namespace = {}                           # create a namespace dict
    exec('from inputs import *', namespace)  # pass it to exec
    print(namespace["B"])                    # read the results from the namespace