将变量注入导入名称空间

时间:2016-07-28 21:52:22

标签: python-3.x python-import

为了说明我想要做的事情,我们假设我有testmod的模块./testmod.py。此模块的整个内容为

x = test

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

显然,从当前目录中执行简单的import testmod语句会导致错误:NameError: name 'test' is not defined

我认为可能正确地将globalslocals传递给__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

我设置的测试值不同,所以如果有效,我可以看到哪个字典testmod.x来自。{/ p>

由于这些似乎都不起作用,我被困住了。是否有可能完成我想要做的事情?我猜是的,因为这是Python,而不是斯巴达。

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

更新:为什么

我将模块作为配置文件导入我的程序。我没有使用JSON或INI的原因是我希望可以使用Python的全部解释器来从表达式计算配置中的值。我想在程序中预先计算出某些值来进行这些计算。

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

2 个答案:

答案 0 :(得分:1)

您可以使用Python的内置函数来注入您自己的假内置test变量:

import builtins    # __builtin__, no s, in Python 2

builtins.test = 5  # or whatever other placeholder value

import testmod

del builtins.test  # clean up after ourselves

答案 1 :(得分:1)

我想出了一个基于this answerimportlib 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')

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

我决定将所有配置项放入可在加载时访问的单个字段,我将其命名为config。这允许我在testmod中执行以下操作:

if 'test' in config:
    x = config['test']

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

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')