最简单的可以直接遍历目录,但是也有更方便的库pkgutil
(标准库)
import pkgutil
import eebackup
# 内置模块通常由不同的导入器处理
for importer, modname, ispkg in pkgutil.iter_modules(eebackup.__path__):
...
importer一般不用管,最常见的是FileFinder,但如果是从zip中导入的模块,则会是zipimporter
modname是模块名,比如core
,ispkg是模块是否是包
一个list,比如eebackup这个包的__path__是[''D:\\Python39\\lib\\site-packages\\eebackup'']
这样可以添加额外的搜索路径,比如
import eebackup
import httpx
eebackup.__path__.extend(httpx.__path__)
from eebackup import _client
print(eebackup._client)
输出<module 'eebackup._client' from 'D:\\Python39\\lib\\site-packages\\httpx\\_client.py'>
一般开发不推荐动__path__,因为很容易出错
for importer, modname, ispkg in pkgutil.iter_modules(eebackup.__path__, prefix="eebackup."):
...
设置prefix后,则返回的modname会加上prefix,比如eebackup.core
拥有模块的路径后就可以开始导入了
推荐使用importlib.import_module而不是__import__,免得因为缺少属性什么而踩坑
import importlib
name = "eebackup.core"
core = importlib.import_module(name) # 就和普通import一样
pkgutil的模块搜索功能,配合importlib.import_module,就可以实现插件系统了
import pkgutil
import importlib
plugin_pkg_name = "plugins"
plugins = []
for importer, modname, ispkg in pkgutil.iter_modules(["./plugins"], prefix=plugin_pkg_name+"."):
plugin = importlib.import_module(modname)
plugins.append(plugin)
按照刚才的插件例子,importlib.reload会返回新的模块,我们需要移除旧的模块再添加新的模块
import importlib
plugins = [...]
for model in plugins.copy():
new = importlib.reload(model)
plugins.remove(model)
plugins.append(new)
假如eebackup是个包而不是模块?要怎么做
根据importlib.reload底层,其实就是更改了sys.modules
使用import语句导入模块时,会先检查sys.modules中是否有这个模块,如果有则直接返回,没有则导入
所以,我们可以先删除模块再导入
import sys
import importlib
for key in sys.modules.keys():
if key.startswith("eebackup"):
del sys.modules[key]
sys.modules[key] = importlib.import_module(key)
但是其他模块对旧模块的引用也需要更新,这里需要根据项目自己解决