如何编写带单元格分隔的Python脚本,兼顾程序化运行与自定义模块热重载需求?
如何编写带单元格分隔的Python脚本,兼顾程序化运行与自定义模块热重载需求?
嘿,这个问题我之前也踩过坑!先给你拆解成两部分解决:
首先说#%%单元格分隔的事儿——这个标记是Jupyter Notebook/VS Code交互式窗口识别的单元格分隔符,普通Python解释器会把它当成注释直接忽略,所以你的脚本本身直接用python script.py跑完全没问题,不需要做额外修改,这部分不用操心。
重点在模块热重载的问题,你说用importlib.reload没效果,大概率是用法不对!我给你捋清楚问题出在哪,再给你靠谱的解决方案:
为什么之前的reload没生效?
如果你之前是这么写的:
#%% cell1 import my_custom_module from my_custom_module import function_that_changes #%% cell3 修改模块后运行 import importlib importlib.reload(my_custom_module) result = function_that_changes(data)
那问题就来了:你直接导入的function_that_changes是一个指向旧版本函数的变量,就算你重载了模块,这个变量还是绑定着原来的函数,自然用不上新代码。
正确的热重载姿势
给你两种实用的方案,选哪种看你习惯:
方案1:始终通过模块名调用函数
不要直接导入模块里的函数/类,每次都通过模块名访问,这样重载模块后,访问的就是最新的内容:
#%% cell 1 import importlib import sys import my_custom_module # 交互式环境下首次加载就重载一次,确保拿到最新代码 if 'ipykernel' in sys.modules: my_custom_module = importlib.reload(my_custom_module) #%% cell2: data = load_data_that_takes_a_long_time_to_load() #%% cell 3: # 每次运行这个单元格前如果改了模块,就重载 if 'ipykernel' in sys.modules: my_custom_module = importlib.reload(my_custom_module) # 始终通过模块名调用函数 result = my_custom_module.function_that_changes(data)
方案2:重载后重新导入函数
如果你习惯直接导入函数,那一定要在重载模块后重新导入一次,覆盖原来的变量:
#%% cell 1 import importlib import sys from my_custom_module import function_that_changes #%% cell2: data = load_data_that_takes_a_long_time_to_load() #%% cell 3: if 'ipykernel' in sys.modules: import my_custom_module # 先确保模块被导入 importlib.reload(my_custom_module) # 重新导入函数,覆盖旧引用 from my_custom_module import function_that_changes result = function_that_changes(data)
额外优化:只在交互式环境执行重载
上面的代码里加了if 'ipykernel' in sys.modules:的判断,这是为了让脚本程序化运行时跳过重载步骤——毕竟批量跑脚本的时候不需要热重载,能省点时间。
补充提醒
如果你的自定义模块依赖其他模块,且你也修改了依赖模块,那可能需要递归重载,但一般情况下,只重载你直接修改的模块就够了,除非你碰到了深层依赖的问题。
备注:内容来源于stack exchange,提问作者Mefitico
相关产品推荐
相关产品推荐

