尽管成功使用解释器,但仍无法动态地使用来自功能的EXEC进行动态导入



尽管能够在解释器中这样做,但我在使用功能导入某些东西时遇到了一些困难。

想象一下,在文件夹中有一个文件,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')

我没有找到名称,找不到B。发生了什么事?

函数中的本地变量的处理方式不同于全局变量和对象属性(两者都使用字典将词典映射到值)。

定义函数时,Python编译器会检查其代码,并记录使用哪些局部变量名称,并为每个局部变量名称指定一个"插槽"。调用函数时,插槽是指帧对象中内存的一部分。本地变量分配和查找逐数,而不是按名称访问插槽。这使得局部变量查找的速度明显比全局变量和属性查找更快(因为索引插槽比进行DICT查找要快得多)。

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

即使您单独初始化的本地变量,用于在exec'D代码中分配给您的名称,但它仍然行不通。exec'D代码不知道它是从函数内部运行的,并且总是想将变量写入字典(永远不要函数插槽)。函数确实具有局部名称空间词典,该字典确实会捕获作业(它们不会成为全局变量),但是通过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

最新更新