Python同级目录导入异常:两种导入场景差异解惑
好问题!咱们结合Python的模块查找规则和你的项目结构来拆解问题:
首先明确你的项目结构:
package/ __init__.py code/ __init__.py model.py helper.py notebooks/ __init__.py notebook1.ipynb
核心原因:模块导入层级与sys.path的匹配问题
先搞懂Python的模块查找逻辑
当你执行from X import Y时,Python会遍历sys.path中的每一个路径,依次查找:
- 名为
X.py的单文件模块 - 名为
X的目录,且该目录下存在__init__.py(即合法的Python包)
只有找到符合条件的X,才会继续导入Y。
场景一的问题拆解
你在notebook1.ipynb中执行:
sys.path.insert(0, os.path.abspath('..')) from code.model import MetaModel
假设你的notebook工作目录是package/notebooks/,那么os.path.abspath('..')会得到package/的绝对路径,这个路径被加入到sys.path最前面。
这时候你尝试导入code.model,Python会在package/路径下找到code包(因为它有__init__.py),但加载code.model时会触发model.py里的错误导入:model.py里写的是from helper import *,这是绝对导入,Python会在sys.path里找helper模块。但此时sys.path里只有package/,而helper.py在package/code/目录下,不在sys.path的直接查找范围内,所以Python会抛出ModuleNotFoundError: No module named 'helper'。
你提到的报错'code' is not a package,大概率是因为model.py的导入错误导致code包加载失败,Python误报了包的有效性;或者是你执行sys.path.insert时路径有误(比如工作目录不是notebooks/,导致..指向了package/的父目录,这时候sys.path里找不到code目录,才会报错code不是包)。
场景二为什么可行?
你执行:
sys.path.insert(0, os.path.abspath(os.path.join('../code'))) from model import MetaModel
这里把package/code/加入了sys.path,此时:
model.py和helper.py都在package/code/目录下,这个目录在sys.path里,所以from model import MetaModel能直接找到model.py;model.py里的from helper import *也能在sys.path的package/code/路径下找到helper.py,所以导入成功。
让包在多场景正常运行的解决方案
1. 使用相对导入(推荐)
修改model.py里的导入语句为相对导入,明确从当前包内导入helper:
from .helper import *
这样无论model.py是被作为包的一部分导入(场景一),还是直接运行,都能正确找到helper模块。
2. 把项目根目录加入sys.path
在notebook中把package/的父目录加入sys.path,然后用完整的包路径导入:
import os import sys sys.path.insert(0, os.path.abspath(os.path.join('..', '..'))) from package.code.model import MetaModel
这种方式保持model.py的绝对导入不变,但需要确保package本身是sys.path下的顶级包。
3. 兼容直接运行脚本的场景
如果你需要在package/目录下直接运行model.py,可以在model.py开头加入以下代码,确保脚本运行时能找到helper:
import os import sys if __name__ == "__main__": sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
这样当直接运行model.py时,会把code/目录加入sys.path,保证from helper import *能正常工作。
内容的提问来源于stack exchange,提问作者user3408780

