动态模块创建

发布于 2024-09-03 05:29:08 字数 1552 浏览 4 评论 0原文

我想从字典动态创建一个模块,我想知道向 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:

  1. 我意识到与运行计时测试相关的示例代码实际上不起作用,因为import *仅适用于模块 级别,执行该语句的上下文是 testit 模块中的函数。换句话说,执行该代码时使用的全局字典是 __main__ 的字典,因为这就是我在交互式 shell 中编写代码时所在的位置。因此,弄清楚这一点的理由有点糟糕,但这仍然是一个有效的问题。

  2. 我发现第一组示例中运行的代码会产生不良影响,即执行新创建的模块代码的命名空间就是声明它的模块的命名空间, 不是它自己的模块。这有点奇怪,可能会导致各种意想不到的响尾蛇般的粗略。所以我很确定这不是这种事情应该如何做,如果它确实是 Guido 所关注的事情的话。

  3. 使用 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:

  1. 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 the testit 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.

  2. 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.

  3. 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 into sys.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

思慕 2024-09-10 05:29:09

嗯,我可以告诉您的一件事是 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 voodoo,以在本地设置 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' 未定义。诡异的。

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 write

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

and 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:

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

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 and imp.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 with sys.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 the Timer doesn't seem to recognize its own local variables. I invoked a bit of Python voodoo by the name of extract_context() (it's a function I wrote) to set a and b at the local scope and ran

print timeit.Timer('print locals(); a + b', 'sys.modules["__main__"].extract_context()').timeit()

Sure enough, the printout of locals() included a and b:

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

but it still complained NameError: global name 'a' is not defined. Weird.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文