当前位置: 首页 > article >正文

漫谈 module caching——PyCharm jupyter notebook 在导入模块被更新后无法及时同步问题

目录

  • 引子:问题的发现
  • 何为 module caching
  • 见微知著:Python 中的缓存机制
  • 参考链接

引子:问题的发现

近日笔者用 PyCharm 创建了一个项目时不经意间发现了这个问题:事情发生在调试 Jupyter Notebook 的过程中。当笔者修改了自己编写的某个库函数后,重新运行代码时却发现,Notebook 仍然调用的是修改前的旧版本库函数。经过查阅大量资料和反复测试,终于弄清楚,问题的根源在于 Python 自带的模块缓存机制。笔者也发现,国内资源中对于这一问题也没有过多详细的解释,因此决定写下此文,以供遇到相同问题的读者们参考,也欢迎大家提出建议。

何为 module caching

(这一部分可以在 PyCharm 中动手实验,本人所用版本为 2024.3 专业版。)

我们新建一个项目,其中创建两个文件:

```a.py```
def echo():
    print(1)
```b.ipynb```
from EM.a import echo
echo()

运行 b.ipynb, 如我们所料,程序正常输出了 1. 但假如我们改动 a.py 中函数的输出,将打印的 1 改为 2 并保存,照理来说重新运行 b.ipynb 应该会重新导入一遍这个新的函数吧?但实际运行发现,调用的函数仍然是老版本的,其输出为 1.
此处仍然是老的 echo 函数这便是所谓的,‘module caching’(模块缓存),即当 kernel 尚未重启时,重新运行所有的 cell 并不会从文件目录(硬盘)中再调用一遍改动后的模块函数。相反,系统会优先使用内存中已有的一份缓存副本。这种机制的设计极大地提高了模块调用的效率,因为避免了频繁的文件 I/O 操作。然而,也正是这个机制导致了上文中提到的模块未完全更新的问题,在此需要多多注意。经过笔者的反复搜寻,发现有这么两种解决方案:

  1. 每次运行所有 cell 之前先重启 kernel
    其实如果读者熟悉浏览器形式的 jupyter notebook, 会发现工具栏的 >> 按钮和 PyCharm 含义有且仅有一点差别——jupyter notebook 上是 ‘restart the kernel and run all cells’, 而 PyCharm 上则只是 ‘run all cells’。虽然 PyCharm 自身也有 ‘restart kernel’ 的按钮,但如果不想按两次,可以点击右上角的浏览器图标,在浏览器中打开 .ipynb 文件。当然,由于笔者对 PyCharm 的 UI 并不是那么熟悉,如果有读者知道更改按钮设定 / 一次性快捷完成这两项操作的方法,欢迎在评论区留言分享。
  2. 使用 importlib 中的 reload 函数
    当然,万能的 Python 总是能给我们提供奇妙的库函数来解决问题。这里用到的函数是 importlib 库中的 reload 函数,能够强制在同一进程下从硬盘中重新获取更新后的库文件,并在下一次调用时使用更新后的函数。具体操作方法如下:
    ```b.ipynb```
    from importlib import reload
    import EM.a
    reload(EM.a)	# 重新加载模块
    from EM.a import echo	# 加载后导入需要的函数
    echo()
    

见微知著:Python 中的缓存机制

事实上,上述所说的缓存机制只是 python 中的一种。而 python 的缓存按范围可以分为两类:单进程中的缓存机制进程之间的缓存机制。虽然我们平时只需一下 Ctrl+F5 就能保存并重新运行一个 .py 文件,但前后两次运行是两个不同的进程。而到了 .ipynb 的 notebook 文件中,重新运行 cell 而不重启 kernel 实际上还是留在原来的老进程中(这点从我们在 cmd 输入 jupyter notebook 后只要不重启 kernel 命令行就不会断这一事实也不难看出)。这一分类下,其实我们刚才提到的缓存属于第一类

  • 单进程:具体来说,每当你在同一进程中使用 import 语句导入某个模块时,Python 不会重复加载该模块的内容,而是直接从全局字典(sys.modules)中获取对应的模块对象。这种设计不仅提升了运行效率,避免了重复的磁盘 I/O 操作,还确保了模块在进程中的状态得以持久化。
  • 进程间:多个解释器进程加载相同的 Python 模块时,在第一次加载时,Python 会将 .py 源代码编译为包含字节码的 .pyc 文件。这一操作只需进行一次,因此后续的进程无需重新解析 .py 文件,加载速度会快上不少。这种机制在安装新库时尤为显著。例如,当我们使用工具安装一个 Python 库时,系统会自动生成 .pyc 文件,从而优化后续的模块调用效率。

参考链接

  • https://stackoverflow.com/questions/2918898/prevent-python-from-caching-the-imported-modules
  • https://stackoverflow.com/questions/29353600/ipython-notebook-caching-issue
  • https://www.reddit.com/r/Python/comments/140c9z9/does_python_cache_package_imports/

欢迎关注我的博客!
Find me on GitHub: GitHub profile page
Gitee account (under construction): Gitee site
GitLab account (under construction): GitLab site
Also find me on Luogu:Luogu profile

欢迎大家关注我,在项目上与我协作哦!


http://www.kler.cn/a/410101.html

相关文章:

  • 替换Nacos的MySQL驱动
  • selinux及防火墙
  • 【C++】static修饰的“静态成员函数“--静态成员在哪定义?静态成员函数的作用?
  • 大数据实验4-HBase
  • 开发者视角下的鸿蒙
  • 数据结构-位运算笔记
  • rabbitmq exchange queue topic的关系
  • 前端---HTML(一)
  • 【CSP CCF记录】201809-2第14次认证 买菜
  • SpringMVC-03-HelloSpring
  • 人工智能之数学基础:线性代数在人工智能中的地位
  • HttpServletRequest req和前端的关系,req.getParameter详细解释,req.getParameter和前端的关系
  • docker 安装arm架构mysql8
  • 全面解读RuoYi 系列项目不同版本与应用场景
  • Java中的集合体系
  • 问题记录-Java后端
  • STM32端口模拟编码器输入
  • docker部署springboot、挂载配置文件
  • 241125学习日志——[CSDIY] [ByteDance] 后端训练营 [15]
  • 代谢组数据分析(二十二):Zscore标准化后主成分分析(PCA)及热图展示
  • vue中el-table合并单元格
  • 【论文解析】HAQ: Hardware-Aware Automated Quantization With Mixed Precision
  • 深入解析常见的设计模式
  • 三种蓝牙架构实现方案
  • python基础练习
  • ThingsBoard安装测试