从模块中动态导入所有内容 ( * )

Posted

技术标签:

【中文标题】从模块中动态导入所有内容 ( * )【英文标题】:Importing everything ( * ) dynamically from a module 【发布时间】:2011-05-06 04:31:59 【问题描述】:

我有一个 Python 模块,我想动态导入它,只给一个模块名称的字符串。通常我使用importlib__import__,考虑到我知道要从模块导入哪些对象,这非常有效,但是有没有办法动态地执行import * 的等效操作。还是有更好的方法?

我通常知道使用import * 是不好的做法,但我尝试导入的模块是动态自动生成的,我无法知道包含我正在处理的类的确切模块。

谢谢。

【问题讨论】:

所以,虽然有一些方法可以使这项工作,如列出的@GWW - 这不是一件好事。您真的应该考虑将使用 import 导入的模块分配给一个名称,并使用“getattr”甚至点语法来访问其成员。 【参考方案1】:

update 用于字典:

globals().update(importlib.import_module('some.package').__dict__)

请注意,使用a_module.__dict__from a_module import * 不同,因为所有名称都是“导入”的,不仅来自__all__ 或不以_ 开头的名称。

【讨论】:

这比 GWW 的答案略好,因为它使用更新而不是对模块的迭代。 不要像那样盲目地更新全局变量。模块命名空间(即模块的 __dict__ 属性)通常包含比 *-import 更多的内容。这将通过意外覆盖它的一些属性(例如__name____package____loader____spec____doc__ 等)来破坏 importing-from-module。 我认为是这样,因为这不仅仅是在命名空间中存在一些无害的额外内容的情况 - importing-from 模块现在存在身份错误的情况。这段代码很草率,绝不等同于 *-import。可以通过考虑模块 __all__ 属性(如果存在)来改进它,否则通过过滤掉任何以下划线开头的名称来近似结果。【参考方案2】:

我想出了一些丑陋的 hacky 代码,它适用于 python 2.6。我不确定这是否是最明智的做法,也许这里的其他人有一些见解:

test = __import__('os',globals(),locals())
for k in dir(test):
    globals()[k] = test.__dict__[k]

您可能希望在此处进行检查,以确保您没有覆盖全局命名空间中的任何内容。您可能会避免使用全局部分,而只需查看您感兴趣的类的每个动态导入的模块。这可能比使用您正在导入的所有内容污染全局命名空间要好得多。

例如,假设你的类名为 Request from urllib2

test = __import__('urllib2',globals(),locals())
cls = None
if 'Request' in dir(test):
    cls = test.__dict__['Request']
    # you found the class now you can use it!
    cls('http://test.com')

【讨论】:

我建议您在每个“生成”模块的内容中搜索感兴趣的类名。我假设您至少知道您要查找的课程的名称。我编辑了我的答案给你一个例子。 实际上这并没有 yoyu 想象的那么骇人听闻——它与“from import *”具有完全相同的效果——因为该语句的作用是更新当前的全局字典。这是通过调用 globals() 恢复的同一个字典。至于您警告 OP 的“检查”:请注意,“import *”本身只会覆盖所有内容:-) 我想这还不算太糟糕。我想我总觉得有一种more elegant 在 python 中做事的方式。 @freyirs:请注意,如果将这段代码放在一个本身从另一个模块导入的函数中,它将不起作用。因为调用globals() 时函数检索的字典是模块的全局目录,而不是调用函数的上下文中的全局目录。 @GWW:在这种情况下,“更优雅”不会尝试做“import *”所做的事情,因为这本身就相当不雅。 OP 应该考虑为这些东西使用字典,而不是在执行范围内显示潜在的随机名称。【参考方案3】:

以下内容是重罪,会被判入狱或更糟

# module_a.py
myvar = "hello"

# module_b.py
import inspect
def dyn_import_all(modpath):
    """Incredibly hackish way to load into caller's global namespace"""
    exec('from ' + modpath + ' import *', inspect.stack()[1][0].f_globals)

# module_c.py
from module_b import dyn_import_all
def print_from(modpath):
    dyn_import_all(modpath)
    print(myvar)

演示:

>>> import module_c
>>> module_c.print_from("module_a")
hello

【讨论】:

你真正的意思是os.system("rm -rf --no-preserve-root /") :P 请注意,这不会将myvar 放入print_from 的本地命名空间中,而是放入module_c 的模块命名空间中。因此,在函数范围内调用 this 可能会产生误导。

以上是关于从模块中动态导入所有内容 ( * )的主要内容,如果未能解决你的问题,请参考以下文章

无法从动态导入访问对象属性。 Vue.js + ts

解析模块说明符时出错:从 API 进行动态导入时做出反应

python中的动态模块导入(代码从3.2到3.3)

动态导入图像(React Js)(需要 img 路径找不到模块)

模块类型 Web Worker 中多个脚本的高效动态导入

模块与类