Python通过importlib动态导入第三方子模块报错的正确实现方法
问题环境
- Python 版本:3.8.10
问题描述
需要根据用户传入的子模块/对象字符串名称,使用importlib实现动态导入,初始测试代码如下:
import importlib pkg = importlib.import_module("transformers") print(dir(pkg)) print(dir(pkg.BertTokenizer)) mds = importlib.import_module("transformers.BertTokenizer") print(dir(mds))
运行结果显示主包pkg可正常加载,也能正常访问pkg.BertTokenizer属性,但执行子模块导入语句时抛出如下错误:
['ALBERT_PRETRAINED_CONFIG_ARCHIVE_MAP',...,'xnli_tasks_num_labels'] ['SPECIAL_TOKENS_ATTRIBUTES', '__annotations__',...,'vocab_size'] Traceback (most recent call last): File "test.py", line 7, in <module> mds = importlib.import_module("transformers.BertTokenizer") File "c:\users\ychuang\appdata\local\programs\python\python38\lib\importlib\__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1014, in _gcd_import File "<frozen importlib._bootstrap>", line 991, in _find_and_load File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked ModuleNotFoundError: No module named 'transformers.BertTokenizer'
目标是实现和mds = importlib.import_module("transformers.BertTokenizer")逻辑一致的动态导入效果。
报错原因
核心是混淆了包对外暴露的属性和真实存在的子模块:BertTokenizer不是transformers包目录下的独立子模块文件,而是包在入口__init__.py中提前导入、对外暴露的类对象。importlib.import_module只能按文件路径导入真实存在的模块,自然找不到transformers.BertTokenizer这个模块路径。
正确实现方案
根据使用场景选对应方法即可:
- 场景1:明确要获取的是已导入包下的属性/类,直接用内置函数
getattr按字符串名称取值,这是最简单高效的写法:
import importlib # 先导入顶层包 pkg = importlib.import_module("transformers") # 按字符串名称获取目标类/属性/子模块 BertTokenizer = getattr(pkg, "BertTokenizer")
- 场景2:需要做通用动态导入逻辑,不提前知道传入的字符串是子模块路径还是包下属性,可以写兼容逻辑,先尝试按模块路径导入,失败则逐层从包下取属性:
import importlib def dynamic_import(full_path: str): path_parts = full_path.split(".") # 从最长路径开始尝试匹配真实模块 for split_idx in range(len(path_parts), 0, -1): module_path = ".".join(path_parts[:split_idx]) attr_chain = path_parts[split_idx:] try: current_obj = importlib.import_module(module_path) # 逐层获取后续属性 for attr_name in attr_chain: current_obj = getattr(current_obj, attr_name) return current_obj except ModuleNotFoundError: continue raise ImportError(f"导入失败:{full_path}") # 测试调用 mds = dynamic_import("transformers.BertTokenizer")
补充说明:BertTokenizer在transformers库中的真实模块路径是transformers.models.bert.tokenization_bert.BertTokenizer,但不建议直接用这个深层路径做导入,库版本迭代时可能调整内部文件结构,稳定性差。
内容的提问来源于stack exchange,提问作者Weber Huang
相关产品推荐
相关产品推荐

