importlib.import_module无法找到工作目录下新建的模块
问题描述
我想从当前工作目录导入模块,写了这段代码:
import os import pathlib import tempfile from importlib import import_module with tempfile.TemporaryDirectory() as temp_dir: tmp_dir = pathlib.Path(temp_dir) (tmp_dir / "foo").mkdir() (tmp_dir / "foo" / "__init__.py").write_text("def bar(): pass") orig = os.getcwd() os.chdir(tmp_dir) print(import_module("foo")) os.chdir(orig)
但运行时触发了ModuleNotFoundError:
Traceback (most recent call last): File "/Users/tdegeus/data/prog/src/conda_envfile/t.py", line 12, in <module> print(import_module("foo")) ^^^^^^^^^^^^^^^^^^^^ File "/Users/tdegeus/miniforge3/envs/pre/lib/python3.11/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<frozen importlib._bootstrap>", line 1204, in _gcd_import File "<frozen importlib._bootstrap>", line 1176, in _find_and_load File "<frozen importlib._bootstrap>", line 1140, in _find_and_load_unlocked ModuleNotFoundError: No module named 'foo'
注:如果目标目录在程序启动前就存在,导入能正常工作。
问题根源
Python启动时会把程序初始运行的当前工作目录加入sys.path(模块搜索路径列表),但后续用os.chdir()切换工作目录后,sys.path并不会自动更新。你的代码里切换到临时目录后,这个临时目录不在sys.path里,所以import机制找不到foo模块。而预先存在的目录如果本来就在sys.path中(比如初始工作目录的子目录),自然能被找到。
解决办法
方法1:手动把临时目录加入sys.path
这是最简单的方案,切换目录后将临时目录路径添加到模块搜索路径:
import os import sys import pathlib import tempfile from importlib import import_module with tempfile.TemporaryDirectory() as temp_dir: tmp_dir = pathlib.Path(temp_dir) (tmp_dir / "foo").mkdir() (tmp_dir / "foo" / "__init__.py").write_text("def bar(): pass") orig = os.getcwd() os.chdir(tmp_dir) # 把临时目录加入模块搜索路径 sys.path.append(str(tmp_dir)) print(import_module("foo")) # 可选:导入完成后移除该路径,避免干扰后续模块搜索 sys.path.remove(str(tmp_dir)) os.chdir(orig)
方法2:直接通过文件路径加载模块
不需要切换工作目录,用importlib的工具直接加载指定路径的模块:
import pathlib import tempfile from importlib.machinery import SourceFileLoader from importlib.util import module_from_spec, spec_from_loader with tempfile.TemporaryDirectory() as temp_dir: tmp_dir = pathlib.Path(temp_dir) foo_dir = tmp_dir / "foo" foo_dir.mkdir() init_file = foo_dir / "__init__.py" init_file.write_text("def bar(): pass") # 创建模块加载器和规范 loader = SourceFileLoader("foo", str(init_file)) spec = spec_from_loader(loader.name, loader) foo_module = module_from_spec(spec) loader.exec_module(foo_module) print(foo_module) # 可以直接调用模块里的函数 foo_module.bar()
方法3:用相对导入方式
保持切换目录的逻辑,通过import_module的package参数实现相对导入:
import os import pathlib import tempfile from importlib import import_module with tempfile.TemporaryDirectory() as temp_dir: tmp_dir = pathlib.Path(temp_dir) (tmp_dir / "foo").mkdir() (tmp_dir / "foo" / "__init__.py").write_text("def bar(): pass") orig = os.getcwd() os.chdir(tmp_dir) # 用相对导入,package设为空字符串表示当前顶级目录 print(import_module(".foo", package="")) os.chdir(orig)
内容的提问来源于stack exchange,提问作者Tom de Geus
相关产品推荐
相关产品推荐

