使用__import__进行动态模块加载的任何影响?

时间:2012-05-23 19:40:29

标签: python import

在我的申请中,我有:

...
folder_a/
    __init__.py
    a.py
    folder_b/
        __init__.py
        b1.py 
        b2.py
        ...

我需要根据变量(例如folder_b)将模块从a.py动态加载到module_from_b。要执行此操作,请在a.py中执行:

mymodule = __import__("folder_b.%s" % module_from_b, globals(), locals(), fromlist=["*"])

由于它将存在于循环中,即被多次调用,我想知道它是否会对性能或其他任何影响产生影响?如果是这样,有没有办法减轻它,同时保留动态模块加载的能力?

3 个答案:

答案 0 :(得分:1)

我从来没有对它进行基准测试,但我相信导入(无论是动态的还是静态的)先前导入到正在运行的解释器中的模块(即使是完全不相关的代码)应该相当便宜。所有它真正要做的应该是一些(成功的)字典查找。

导入新模块当然会运行模块中的所有代码,再加上文件系统搜索来查找它。

因此,如果您从一组相对较小的模块中反复导入动态选择的模块,那么只要您能够在第一次使用每个特定模块时容忍延迟,您就不应该遇到太多问题(如何大部分延迟取决于你的模块);一段时间后,您导入的几乎所有模块都已经导入,因此__import__调用将变得便宜。

您可能会考虑的另一种设计:如果您需要的整个模块集是可预先知道的(静态或动态),您可以在循环之前预先导入它们以“预热”Python的导入模块集。如果b中没有很多模块不会被使用,你可以导入b __init__.py中的所有内容。

这样,导入延迟将在启动时被取消,您可以使用b包上的getattr来动态获取模块而不需要使用__import__。如果您正在加载如此多的模块,而这些模块是想要在您的循环中分摊导入成本,或者如果您有许多模块但只需要相对较少的模块,那么这不是一个好的选择很难提前知道哪些。

还有另一种可能更好的方法,如果在您要识别要导入哪个模块的位置选择一些常量字符串(而不是从配置文件或用户输入中读取的内容),这将会有效。为什么不在那里导入模块然后将模块本身传递给最终在其他地方使用?作为一个例子,由于我不知道你在做什么,而不是:

for module_name in ['b1', 'b2', 'b3', 'b4']:
    function_using_module(module_name)

def function_using_module(module_name):
    module = __import__(...)
    ...

你可以这样做:

from folder_b import b1, b2, b3, b4

for module in [b1, b2, b3, b4]:
    function_using_module(module)

def function_using_module(module):
    ...

模块就像对象一样是对象,所以你可以将它们存储在列表或对象中,或者你用名字做的任何事情。直接传递模块通常比传递名称作为模块的代理并稍后导入它们更清晰。

答案 1 :(得分:0)

导入语句在字节码中表示为__import__()的调用完全没有区别。以下两个是equivalent

import spam
spam = __import__('spam', globals(), locals(), [], -1)

答案 2 :(得分:-1)

如果你可能会多次这样做,为什么不检查当前命名空间中是否已经存在该模块,如果不是,那么只导入它。

if 'module_I_need_to_load' not in dir():
    from b import module_I_need_to_load
# Or you can look in vars() instead if you have a
# hell of a lot of modules loaded up as dir is a 
# list and vars is a dictionary which will have a
# faster lookup time when you start having a lot of
# elements
if 'module_I_need_to_load' not in vars():
    from b import module_I_need_to_load