Jupyter Notebook导入父目录自定义包报错需保留run.py原有逻辑的问题
问题根因
- 定时任务执行
run.py时,工作目录为PRJ_V2根目录,Python默认将当前工作目录加入模块搜索路径sys.path,因此可以正常识别MyPackage包。 - Jupyter Notebook启动时默认工作目录为
PRJ_V2/Analysis,sys.path中未包含PRJ_V2根路径,直接导入MyPackage会触发模块不存在错误;同时Notebook本身被识别为顶层运行脚本,不支持..形式的跨父级相对导入,会触发"attempted relative import beyond top-level package"错误。
实现方案(完全保留原有run.py运行逻辑,无任何侵入修改)
所有修改仅在predictive.ipynb内完成,不改动项目原有业务代码、不调整定时任务执行流程。
第一步:将项目根目录加入Python模块搜索路径
在Notebook最开头单元格运行以下代码,自动定位PRJ_V2根目录并加入搜索路径,无需写死绝对路径:
import sys from pathlib import Path # 当前Notebook位于Analysis目录下,父目录即为PRJ_V2项目根 prj_root = Path.cwd().parent if str(prj_root) not in sys.path: sys.path.insert(0, str(prj_root))
若你启动Notebook时的工作目录不是
Analysis,只需调整prj_root的取值,保证其指向存放MyPackage文件夹的PRJ_V2根目录即可。
第二步:模块导入与热重载
常规导入
路径添加完成后,直接使用和run.py完全一致的导入语句即可,不会触发报错:
from MyPackage.myclass import Myclass
热重载实现(适配Python 3.7)
如果需要频繁修改myclass.py代码,可通过以下方式实现热重载,无需重启Notebook内核:
import importlib target_module = "MyPackage.myclass" # 清除已加载的模块缓存 if target_module in sys.modules: del sys.modules[target_module] # 重新加载最新版本的模块 myclass_mod = importlib.import_module(target_module) # 取出最新的类定义 Myclass = myclass_mod.Myclass
如果
Myclass依赖MyPackage下的其他模块(如myclass2.py),只需把对应模块的全路径也加入缓存清理逻辑,一并删除sys.modules里的对应条目即可。
如果嫌手动执行重载代码麻烦,可以直接开启IPython自带的自动重载能力,开发效率更高,在Notebook开头加两行魔法命令即可:
%load_ext autoreload %autoreload 2
开启后每次执行单元格代码时,IPython会自动检测所有导入的本地模块变更,自动加载最新版本代码,无需手动写重载逻辑。
方案验证
- 原有定时任务流程完全不受影响:我们未修改
PRJ_V2根目录、MyPackage目录下的任何原有代码,切换目录、激活虚拟环境、执行.\venv\Scripts\python.exe run.py的逻辑可以完全保留,运行结果和之前一致。 - Notebook端:导入模块不再报错,热重载/自动重载功能正常,修改
myclass.py代码后无需重启内核即可生效。
内容的提问来源于stack exchange,提问作者Xavi Polo
相关产品推荐
相关产品推荐

