【问题标题】:dynamic module creation动态模块创建
【发布时间】:2011-02-25 07:02:33
【问题描述】:

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

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

  2. 我发现在第一组示例中运行的代码具有不良影响,即新创建的模块代码在其中执行的命名空间是它被声明的模块的命名空间 em>,不是 它自己的模块。这很奇怪,可能会导致各种意想不到的响尾蛇粗略。所以我很确定这不是这种事情的本意是,如果它实际上是 Guido 所擅长的事情的话。

  3. 使用imp.load_source('NewModuleName', 'path/to/module/module_to_load.py') 可以轻松完成从不在python 包含路径中的文件中动态加载模块的类似但略有不同的情况。这确实将模块加载到sys.modules。然而,这并不能真正回答我的问题,因为真的,如果你在 embedded platform with no filesystem 上运行 python 怎么办?

目前我正在与一个相当大的信息过载情况作斗争,所以我可能弄错了,但imp 模块中似乎没有任何东西能够做到这一点。

但是,从本质上讲,此时的问题是如何为对象设置全局(即模块)上下文。也许我应该更具体地问这个?在更大的范围内,如何让 Python 在将对象硬塞到给定模块中时做到这一点?

【问题讨论】:

  • 哎呀.. 这实际上不起作用,因为import * 仅在模块级别起作用。
  • 我还意识到imp 模块中有一个名为new_module 的函数。我不确定 imp.new_module 和 types.ModuleType 之间是否有任何区别,除了前者不将文档字符串作为参数。他们都没有将新模块添加到 sys.modules; imp.new_module 的文档确实明确提到新模块没有添加。
  • +1 编辑/启示/PHOOEYS/EUREKAE :-)
  • 谁将此标记为重复?导入和创建模块是有区别的。导入意味着模块已经存在于某处,只需导入即可。创建意味着您实际上希望知道动态创建它,我不在乎某些不相关的问题恰好对此有部分答案,这在当时可能是有效的。特别是因为该答案已经过时,人们唯一能做的就是对其发表评论,而不是 - 你知道 - 在它所属的问题下创建一个新答案
  • 请为有足够权限的人投票以重新打开此问题。作为@FelixB。建议,这个问题被错误地关闭为重复。这实际上是一个非常有趣的问题。让我们看看社区从这里走向何方! \o/

标签: python dynamic module timing


【解决方案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__)

这与您所做的类似,只是您从现有字典而不是文件加载模块的内容。 (我不知道 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' is not defined。很奇怪。

【讨论】:

    猜你喜欢
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多