动态模块创建
我想从字典动态创建一个模块,我想知道向 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 构造函数一起使用;在某些情况下,这样做可能不如这种方法合适。
编辑/启示/PHOOEYS/EUREKA:
我意识到与运行计时测试相关的示例代码实际上不起作用,因为
import *
仅适用于模块 级别,执行该语句的上下文是testit
模块中的函数。换句话说,执行该代码时使用的全局字典是__main__
的字典,因为这就是我在交互式 shell 中编写代码时所在的位置。因此,弄清楚这一点的理由有点糟糕,但这仍然是一个有效的问题。我发现第一组示例中运行的代码会产生不良影响,即执行新创建的模块代码的命名空间就是声明它的模块的命名空间, 不是它自己的模块。这有点奇怪,可能会导致各种意想不到的响尾蛇般的粗略。所以我很确定这不是这种事情应该如何做,如果它确实是 Guido 所关注的事情的话。
使用 imp.load_source('NewModuleName', 'path/to/module/ 从不在 python 包含路径中的文件动态加载模块的情况类似但略有不同module_to_load.py')。这会将模块加载到 sys.modules 中。然而,这并不能真正回答我的问题,因为实际上,如果你在 上运行 python 会怎么样?没有文件系统的嵌入式平台?
我目前正在与相当大的信息过载情况作斗争,所以我可能会弄错,但 imp
模块中似乎没有任何东西可以做到这一点。
但此时的问题本质上是如何设置对象的全局(即模块)上下文。也许我应该问得更具体一些?从更大的范围来看,如何让 Python 做到这一点,同时将对象硬塞到给定的模块中?
I'd like to dynamically create a module from a dictionary, and I'm wondering if adding an element to sys.modules
is really the best way to do this. 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
My immediate goal in this regard is to be able to provide a context for timing test execution:
import timeit
timeit.Timer('a + b', 'from TestContext import *')
It seems that there are other ways to do this, since the Timer constructor takes objects as well as strings. I'm still interested in learning how to do this though, since a) it has other potential applications; and b) I'm not sure exactly how to use objects with the Timer constructor; doing so may prove to be less appropriate than this approach in some circumstances.
EDITS/REVELATIONS/PHOOEYS/EUREKA:
I've realized that the example code relating to running timing tests won't actually work, because
import *
only works at the module level, and the context in which that statement is executed is that of a function in thetestit
module. In other words, the globals dictionary used when executing that code is that of__main__
, since that's where I was when I wrote the code in the interactive shell. So that rationale for figuring this out is a bit botched, but it's still a valid question.I've discovered that the code run in the first set of examples has the undesirable effect that the namespace in which the newly created module's code executes is that of the module in which it was declared, not its own module. This is like way weird, and could lead to all sorts of unexpected rattlesnakeic sketchiness. So I'm pretty sure that this is not how this sort of thing is meant to be done, if it is in fact something that the Guido doth shine upon.
The similar-but-subtly-different case of dynamically loading a module from a file that is not in python's include path is quite easily accomplished using
imp.load_source('NewModuleName', 'path/to/module/module_to_load.py')
. This does load the module intosys.modules
. However this doesn't really answer my question, because really, what if you're running python on an embedded platform with no filesystem?
I'm battling a considerable case of information overload at the moment, so I could be mistaken, but there doesn't seem to be anything in the imp
module that's capable of this.
But the question, essentially, at this point is how to set the global (ie module) context for an object. Maybe I should ask that more specifically? And at a larger scope, how to get Python to do this while shoehorning objects into a given module?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
嗯,我可以告诉您的一件事是 timeit 函数实际上使用模块的全局变量执行其代码。因此,在您的示例中,您可以编写
并且它会起作用。但这并不能解决动态定义模块的更普遍的问题。
关于模块定义问题,这绝对是可能的,我认为您已经偶然发现了最好的方法。作为参考,Python 导入模块时发生的事情的要点基本上如下:
这与您所做的事情相同,只是您从现有字典而不是文件加载模块的内容。 (除了文档字符串之外,我不知道
types.ModuleType
和imp.new_module
之间有什么区别,所以你可以互换使用它们)你正在做的是有点类似于编写您自己的导入程序,当您这样做时,您肯定会弄乱sys.modules
。顺便说一句,即使您的
import *
内容在函数内是合法的,您可能仍然会遇到问题,因为奇怪的是,您传递给Timer
的语句似乎并不存在识别它自己的局部变量。我通过名称extract_context()
(这是我编写的函数)调用了一些 Python voodoo,以在本地设置a
和b
果然,
locals()
的打印输出包括a
和b
:但它仍然抱怨
NameError: global name ' a' 未定义
。诡异的。Hmm, well one thing I can tell you is that the
timeit
function actually executes its code using the module's global variables. So in your example, you could writeand it would work. But that doesn't address your more general problem of defining a module dynamically.
Regarding the module definition problem, it's definitely possible and I think you've stumbled on to pretty much the best way to do it. For reference, the gist of what goes on when Python imports a module is basically the following:
That's kind of the same thing you do, except that you load the contents of the module from an existing dictionary instead of a file. (I don't know of any difference between
types.ModuleType
andimp.new_module
other than the docstring, so you can probably use them interchangeably) What you're doing is somewhat akin to writing your own importer, and when you do that, you can certainly expect to mess withsys.modules
.As an aside, even if your
import *
thing was legal within a function, you might still have problems because oddly enough, the statement you pass to theTimer
doesn't seem to recognize its own local variables. I invoked a bit of Python voodoo by the name ofextract_context()
(it's a function I wrote) to seta
andb
at the local scope and ranSure enough, the printout of
locals()
includeda
andb
:but it still complained
NameError: global name 'a' is not defined
. Weird.