将变量注入导入命名空间

Mad*_*ist 2 python-import python-3.x

为了说明我正在尝试做什么,假设我有一个testmod位于./testmod.py. 这个模块的全部内容是

x = test
Run Code Online (Sandbox Code Playgroud)

我希望能够使用任何可用的工具importlib或任何其他内置库成功地将此模块导入 Python 。

显然import testmod从当前目录做一个简单的语句会导致错误:NameError: name 'test' is not defined.

我想,也许无论是传球globals还是locals__import__正确地将修改剧本正在运行的内部环境,但事实并非如此:

>>> testmod = __import__('testmod', globals={'test': 'globals'}, locals={'test': 'locals'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jfoxrabi/testmod.py", line 1, in <module>
    x = test
NameError: name 'test' is not defined
Run Code Online (Sandbox Code Playgroud)

我以不同的方式设置了 test 的值,这样我就可以看到testmod.x如果这行得通,我可以看到哪个 dict来自。

由于这些似乎都不起作用,我被卡住了。甚至有可能完成我想要做的事情吗?我猜是的,因为这是 Python,而不是 Sparta。

我在 Anaconda 上使用 Python 3.5。我非常不想使用外部库。

更新:为什么

我将一个模块作为配置文件导入到我的程序中。我不使用 JSON 或 INI 的原因是我希望 Python 解释器的完整范围可用于从表达式计算配置中的值。我想在程序中预先计算某些值以进行这些计算。

虽然我知道这和调用一样糟糕eval(我在我的程序中也是这样做的),但我暂时不关心安全方面。但是,如果这确实是 XY 的情况,我非常愿意接受更好的解决方案。

Mad*_*ist 5

我想出了一个基于这个答案importlib docs的解决方案。基本上,我可以通过使用正确的调用顺序在加载模块对象之前访问它importlib

from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename

def loadConfig(fileName):
    test = 'This is a test'
    name = splitext(basename(fileName))[0]
    spec = spec_from_file_location(name, fileName)
    config = module_from_spec(spec)
    config.test = test
    spec.loader.exec_module(config)
    return config

testmod = loadConfig('./testmod.py')
Run Code Online (Sandbox Code Playgroud)

这比修改 好一点builtins,这可能会在程序的其他部分产生意想不到的后果,并且还可能限制我可以传递给模块的名称。

我决定将所有配置项放入一个在加载时可访问的字段中,我将其命名为config. 这使我可以在以下内容中执行以下操作testmod

if 'test' in config:
    x = config['test']
Run Code Online (Sandbox Code Playgroud)

加载器现在看起来像这样:

from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename

def loadConfig(fileName, **kwargs):
    name = splitext(basename(fileName))[0]
    spec = spec_from_file_location(name, fileName)
    config = module_from_spec(spec)
    config.config = kwargs
    spec.loader.exec_module(config)
    return config

testmod = loadConfig('./testmod.py', test='This is a test')
Run Code Online (Sandbox Code Playgroud)