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

31

我有一个Python模块,想根据仅有的模块名称字符串进行动态导入。通常我使用importlib__import__,这对于我知道要从模块中导入哪些对象来说非常有效,但是否有一种方法可以通过动态方式实现相当于import *的功能呢?或者是否有更好的方法?

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

谢谢。


因此,虽然有办法让它工作,比如@GWW列出的方法 - 但这不是一个好的做法。您应该真正考虑将通过__import__导入的模块分配给一个名称,并使用“getattr”甚至点语法来访问其成员。 - jsbueno
3个回答

27

使用update方法更新字典:

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

需要注意的是,使用 a_module.__dict__ 和使用 from a_module import * 是不同的,因为前者会导入所有名称,而不仅仅是__all__中定义或以_开头的名称。


5
这比GWW的回答稍微好一些,因为它使用更新而不是对模块进行迭代。 - gak
1
不要盲目地更新全局变量。模块命名空间(即模块的__dict__属性)通常包含比* -import多得多的东西。这会通过意外覆盖一堆属性(例如__name____package____loader____spec____doc__等)来破坏从模块导入的方式。 - wim
我认为是这样,因为这不仅仅是命名空间中存在一些无害的额外内容的情况 - 导入模块现在有了身份错误的情况。这段代码很懒散,绝不等同于 *-import。如果考虑到模块的 __all__ 属性(如果有的话),则可以改进它,否则通过过滤任何以下划线开头的名称来近似结果。 - wim

4

我想到了一些丑陋的hacky代码,它在Python 2.6中可以工作。但我不确定这是否是最明智的做法,也许这里的其他人有一些见解:

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

你可能希望在这里放置一个检查,以确保你没有覆盖全局命名空间中的任何内容。你可以避免使用全局变量,只需查找每个动态导入模块中的感兴趣的类即可。这可能比将所有要导入的内容污染全局命名空间要好得多。
例如,假设你的类名为urllib2中的Request。
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')

我建议你做的是搜索每个“生成”的模块的内容,以查找感兴趣的类名。我假设你至少知道你要查找的类的名称。我编辑了我的答案,给你一个例子。 - GWW
2
实际上,这比你想象的要不那么hackish - 它具有与 "from <module> import *" 相同的效果 - 因为这个语句所做的就是更新当前全局字典。这正是通过调用globals()恢复的完全相同的字典。至于你警告OP的 "检查":请注意, "import *" 本身只是简单地覆盖了所有内容 :-) - jsbueno
我想那也不算太糟糕。我猜我总觉得在Python中有一种“更优雅”的方法来完成事情。 - GWW
2
@freyirs:请注意,如果将此代码放在从另一个模块导入的函数内部,则该代码将无法正常工作。因为当调用globals()时,函数检索的字典是模块全局目录,而不是调用该函数的上下文中的全局目录。 - jsbueno
@jsbueno:我想最好的方法可能就是执行搜索并将类返回给需要它的上下文。 - GWW
2
@GWW:在这种情况下,“更优雅”的做法是不要尝试做“import *”所做的事情,因为那本身就不太优雅。原帖作者应该考虑使用字典来处理这些内容,而不是让潜在的随机名称出现在执行范围内。 - jsbueno

4
以下内容极其罪恶,会使你陷入炼狱或更糟的境地。
# 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 - evandrix
1
请注意,这并没有将 myvar 放入 print_from 的本地命名空间中,而是放入了 module_c 的模块命名空间中。因此,在函数范围内调用此函数可能会产生误导。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接