动态模块创建

19

我想要从一个字典动态地创建一个模块,我在想是否将一个元素添加到sys.modules真的是最好的方法。例如:

context = { a: 1, b: 2 }
import types
test_context_module = types.ModuleType('TestContext', 'Module created to provide a context for tests')
test_context_module.__dict__.update(context)
import sys
sys.modules['TestContext'] = test_context_module

我在这方面的直接目标是能够为时间测试执行提供上下文:

import timeit
timeit.Timer('a + b', 'from TestContext import *')

似乎还有其他方法可以做到这一点,因为 Timer 构造函数不仅接受字符串,也接受对象。但我仍然希望学习如何做到这一点,因为 a) 它有其他潜在应用; b) 我不确定如何将对象与 Timer 构造函数一起使用;这样做在某些情况下可能不如本方法合适。
修改/揭示/咯咯声/欧瑞卡:
  1. 我意识到与运行计时测试相关的示例代码实际上无法工作,因为 import * 只在模块级别有效,并且执行该语句的上下文是 testit 模块中的 函数,换句话说,当执行该代码的全局字典是 __main__ 的,因为我在交互式 shell 中编写代码时处于该位置。因此,这个理由有点失败了,但仍然是一个有效的问题。
  2. 我发现在第一组示例中运行的代码的不良影响是,新创建的模块代码所在的名称空间是其被声明的模块的名称空间,而不是 它自己的模块。这就像非常奇怪,并可能导致各种意外的问题。因此,我相当确定这不是这种事情应该完成的方式,如果 Guido 确实支持它。
  3. 从未在python的 include 路径中的文件中动态加载模块的类似但微妙的不同的情况可以很容易地使用 imp.load_source('NewModuleName', 'path/to/module/module_to_load.py') 实现。这确实将该模块加载到 sys.modules 中。然而,这并不真正回答我的问题,因为实际上,如果你在没有文件系统的嵌入式平台上运行 python,该怎么办?
我目前正在处理信息过载问题,所以我可能错了,但是目前似乎在 imp 模块中没有任何能够做到这一点的东西。但是,从本质上讲,问题在于如何设置对象的全局(即模块)上下文。也许我应该更具体地询问这个问题?而且从更大的范围来看,如何在将对象塞入给定模块的同时让 Python 这样做呢?

糟糕..那实际上行不通,因为import *只在模块级别起作用。 - intuited
1
我还意识到imp模块中有一个叫做new_module的函数。除了前者不接受文档字符串作为参数外,我不确定imp.new_module和types.ModuleType之间是否有任何区别。它们都不会将新模块添加到sys.modules中;imp.new_module的文档明确提到新模块不会被添加。 - intuited
@Cecil:它已经重新开放了。 - martineau
1个回答

4

嗯,有一件事我可以告诉你,那就是timeit函数实际上使用模块的全局变量来执行其代码。因此,在你的示例中,你可以这样写:

import timeit
timeit.a = 1
timeit.b = 2
timeit.Timer('a + b').timeit()

而且它会起作用。但这并不能解决你更普遍的问题,即动态定义模块。

关于模块定义问题,这是完全可以实现的,我认为你已经找到了最好的方法。作为参考,当Python导入模块时,基本上发生的情况如下:

module = imp.new_module(name)
execfile(file, module.__dict__)

那么你所做的事情有点类似,只是你从现有的字典中加载模块的内容,而不是从文件中加载。(我不知道types.ModuleTypeimp.new_module之间有什么区别,除了文档字符串以外,所以你可以交替使用它们)你正在做的有点类似于编写自己的导入器,当你这样做时,你肯定会涉及到sys.modules
另外,即使在函数内部使用import *也是合法的,你可能仍然会遇到问题,因为奇怪的是,你传递给Timer的语句似乎无法识别它自己的本地变量。我调用了一个名为extract_context()的Python魔术函数(这是我编写的一个函数),来设置局部作用域下的ab,并运行。
print timeit.Timer('print locals(); a + b', 'sys.modules["__main__"].extract_context()').timeit()

果然,locals() 的输出包括 ab

{'a': 1, 'b': 2, '_timer': <built-in function time>, '_it': repeat(None, 999999), '_t0': 1277378305.3572791, '_i': None}

但它仍然抱怨 NameError: global name 'a' is not defined。奇怪。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接