我想从字典中动态创建一个模块,我想知道向sys.modules
添加元素是否真的是最好的方法。 EG
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构造函数的对象;在某些情况下,这样做可能不如这种做法合适。
我已经意识到与运行时序测试相关的示例代码实际上不起作用,因为import *
仅适用于模块级别,以及其中的上下文该语句的执行是testit
模块中函数的语句。换句话说,执行该代码时使用的全局字典是__main__
的字典,因为那是我在交互式shell中编写代码时的位置。因此,解决这个问题的理由有点拙劣,但这仍然是一个有效的问题。
我发现在第一组示例中运行的代码会产生不良影响,即新创建的模块代码执行的命名空间是声明的模块的命名空间, 不 自己的模块。这就像奇怪的方式,并可能导致各种意想不到的响尾蛇的粗略。所以我很确定这是不这种事情是如何完成的,如果它实际上是Guido发光的东西。
使用imp.load_source('NewModuleName', 'path/to/module/module_to_load.py')
可以很容易地完成从不在python包含路径中的文件动态加载模块的类似但略有不同的情况。这会将模块加载到sys.modules
。然而,这并没有真正回答我的问题,因为如果你在embedded platform with no filesystem上运行python会怎么样?
我目前正在与一个相当大的信息过载案件作斗争,所以我可能会弄错,但imp
模块中似乎没有任何能够做到这一点。
但问题本质上就是如何设置对象的全局(即模块)上下文。也许我应该更具体地问一下?在更大的范围内,如何让Python在将对象转换为给定模块时执行此操作?
答案 0 :(得分:1)
嗯,我可以告诉你的一件事是timeit
函数实际上使用模块的全局变量执行其代码。所以在你的例子中,你可以写
import timeit
timeit.a = 1
timeit.b = 2
timeit.Timer('a + b').timeit()
它会起作用。但这并没有解决您动态定义模块的更普遍的问题。
关于模块定义问题,这绝对是可能的,我认为你偶然发现了最好的方法。作为参考,Python导入模块时的内容基本如下:
module = imp.new_module(name)
execfile(file, module.__dict__)
除了从现有字典而不是文件加载模块的内容之外,这与你做的一样。 (除了docstring之外,我不知道types.ModuleType
和imp.new_module
之间有什么区别,所以你可以互换地使用它们。你所做的有点类似于编写自己的导入器,以及何时你做到了,你当然可以期待弄乱sys.modules
。
顺便说一句,即使你的import *
事物在一个函数中是合法的,你可能仍然会遇到问题,因为奇怪的是,你传递给Timer
的声明似乎并不认识它自己局部变量。我用extract_context()
的名称调用了一些Python voodoo(这是我编写的一个函数),在本地范围内设置a
和b
并运行
print timeit.Timer('print locals(); a + b', 'sys.modules["__main__"].extract_context()').timeit()
果然,locals()
的打印输出包括a
和b
:
{'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
。怪异。