如何创建并导入包含不确定模块集合的包?

0 投票
1 回答
626 浏览
提问于 2025-04-18 18:16

使用Python 3.3或更高版本,我想知道如何创建一个包含不确定数量模块的包,并且希望在导入这个包的时候,所有模块都能自动导入,而不需要一个个手动导入。这样的话,如果以后往这个包里添加了新模块,下次导入这个包时,新模块也能自动加载,不用再对包做其他修改。

在包外部,我希望导入和使用这个包的方式大概是这样的:

import package
package.module.function()

而在包内部,我希望导入和使用兄弟模块的方式大概是这样的:

from . import sibling
sibling.function()

或者这样:

import package.sibling
package.sibling.function()

1 个回答

1

在Python 3.3或更高版本中,使用pkgutil搜索包目录,并通过importlib加载每个模块。

在包的__init__.py文件中:

# Import standard modules.
import importlib
import os
import pkgutil

# For each module in the current directory...
for void, module_name, void in pkgutil.iter_modules([os.path.dirname(__file__)]):

    # Import the module.
    importlib.import_module(__name__+'.'+module_name)

以上内容可以移到一个库中,这样包的__init__.py就变得简单了:

# Import local modules.
import loader

# Load all modules in the current directory.
loader.load_all_modules(__file__,__name__)

需要注意的是,importlib这个工具是在Python 3.1中引入的,并在Python 3.3中进行了重要更新,而上述方法在Python 3.1.x或3.2.x中没有经过测试。

https://docs.python.org/3.3/library/importlib.html#importlib.import_module https://docs.python.org/3.3/library/pkgutil.html#pkgutil.iter_modules

其他失败的方法

尝试 1

从包外部,使用绝对导入:

import package.module

这需要知道模块的名称。

尝试 2

从包的__init__.py中,使用绝对导入:

import module

这同样需要知道模块的名称。

尝试 3

从包的__init__.py中,设置__all__

__all__ = ['module']

这需要使用:

from package import *

这样做会污染命名空间,可能会带来危险,尤其是在这种情况下,因为模块的名称并不明确。

尝试 4

从包的__init__.py中,使用pkgutil来识别和导入包中的所有模块,虽然看似可以导入,但实际上只是模拟了原生的导入机制,并没有更新sys.modules字典。这会导致包内的模块在尝试导入兄弟模块时出现问题,例如:

没有在sys.modules中列出会导致相对导入失败,并出现以下消息:

父模块''未加载,无法执行相对导入。

http://hg.python.org/cpython/file/f7992397e98d/Lib/importlib/_bootstrap.py#l1518

此外,绝对导入会重新加载被导入的模块。

https://docs.python.org/3.3/library/imp.html#imp.load_module http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html#the-double-import-trap

尝试 5

从包的__init__.py中,使用importlib似乎没有简单的方法来识别可导入的模块和子包,除了可能通过导入所有内容并捕获错误,而这些错误信息可能会让人误解,从而更难发现真正的问题。

撰写回答