刚接触 Jupyter 的时候,很多人都以为它就是一个“网页版 Python”。第一个单元格里运行print("hello")成功,觉得挺简单;真正开始用它写作业、做分析、跑演示,问题才一个接一个冒出来:改了后面的单元格,前面的输出怎么就不对?明明在终端里装好了包,导入时却报错?Windows 下输入jupyter notebook,提示“不是内部或外部命令”?浏览器打开后一片空白?这些问题的背后,不是 Jupyter 本身多复杂,而是它那套“文档 + 内核 + 执行顺序”的工作方式,和普通脚本文件很不一样。
我更愿意把 Jupyter 的核心价值说成:它让探索性编程的过程变得可记录、可回看、可解释。但代价是,你必须先理解单元格之间不是“从上到下自动读完”,而是由你手动决定什么时候运行哪一段。这个差异,就是新手最容易翻车的地方。
整篇文章不打算只罗列操作步骤,而是想帮你想清楚几个关键问题:Jupyter 到底在解决什么问题?环境、内核、工作目录为什么会影响结果?怎么把一次随手实验沉淀成可复用的代码流程?最后,也会给出一套常见的排查链路,覆盖安装失败、空白页、导入错误、路径找不对这些高频问题。
1. 先搞清楚 Jupyter 真正解决的问题,再谈基础操作
1.1 它不只是“能运行代码的网页”
Jupyter Notebook 不是一个普通文本编辑器,也不是一个在线 IDE。它的核心单位是“单元格”,一个.ipynb文件里既有代码单元格,也有 Markdown 说明单元格,运行完代码后,输出结果会直接显示在代码块下方。
这个设计解决了一个很实际的问题:过去你用脚本文件写代码,运行完只看得到最终输出,中间的过程、当时的想法、为什么这么写,只能靠注释和回忆;现在把代码、运行结果、图表、说明文字放在同一个文档里,等于把“实验过程”完整保留下来。
所以 Jupyter 最擅长的场景是探索性分析、数据清洗、模型调参、教学演示,而不是纯工程开发。它适合你一边思考一边写代码,及时看到每一步的结果,然后决定下一步怎么做。
也正因为这样,你不能指望它像普通脚本那样“保存后一运行就得到最终结果”。你在页面里看到的是一个交互式文档,它的每一次输出,都取决于当时内核里已经执行过哪些代码。
1.2 Notebook、Lab、编辑器插件:底层都是同一套内核逻辑
现在 Jupyter 相关的前端很多:经典的 Jupyter Notebook、功能更完整的 Jupyter Lab、VS Code 里的 Jupyter 插件、PyCharm 里的 Jupyter Notebook 支持。很多人纠结到底学哪个、用哪个。
先给一个判断:哪个前端不是重点,重点是你始终在跟同一个内核打交道。
- Jupyter Notebook:单文档界面,打开一个
.ipynb文件就是一大块画布,适合入门和做一次性分析。 - Jupyter Lab:相当于把 Notebook、文件树、终端、文本编辑器整合在一个工作台里,适合同时打开多个 Notebook、对照文件结构、跑终端命令。
- VS Code 和 PyCharm:本质是外部编辑器接入了 Jupyter 内核,你说它在 VS Code 里写笔记,其实运行的还是 Jupyter 的
ipykernel。
所以在学习基础操作时,先用 Jupyter Notebook 就好。等你需要多文件并行、项目目录管理、边写说明边调试的时候,再切换到 Jupyter Lab 也不迟。切换成本很低,因为核心操作和快捷键基本一致。
注意:不要把 Jupyter 仅仅理解成“运行 Python 的工具”。它改变的是你和代码之间的协作方式:从“写完全部再跑”变成“写一小段、看结果、再继续”。理解这一点,后面的很多困惑都会消失。
2. 环境安装与启动:先把最基础的一环做稳
2.1 推荐路线:先装 Anaconda,再用终端启动
安装 Jupyter 的常见方式有两种:装 Anaconda,或者单独用 pip 安装。
对于初学者,我更推荐 Anaconda。理由不是 Anaconda 一定比手动 pip 好,而是它把 Python、常用数据科学库、Jupyter、包管理器捆绑在一起,减少环境错乱的概率。尤其 Windows 上,系统 Python 往往会和 Anaconda 的 Python 产生PATH冲突,少一个变量就少一类问题。
安装完成后,有人喜欢直接打开 Anaconda Navigator,点一下 “Launch Jupyter Notebook”,虽然方便,但我不建议把图形界面当作唯一入口。更可控的方式是打开 Anaconda Prompt(Windows)或终端,先切换到项目目录,再启动:
conda activate base cd D:\projects\jupyter-demo jupyter notebook启动后终端会输出一个链接,比如http://localhost:8888/tree,浏览器把它打开才是 Jupyter 的主界面。
这里有一个容易被忽略的点:启动 Jupyter 时所在的目录,就是你看到文件树的根目录。如果你在用户目录启动,然后在文件树里一层层点进去找 Notebook,后续读写文件时很容易出现路径和预期不一致的情况。建议每个项目固定一个文件夹,启动前先cd过去。
2.2 启动后先确认三个信息:环境、内核、工作目录
打开一个新建的 Notebook 后,不要急着写业务代码,先确认三个信息:
import sys print(sys.executable) import os print(os.getcwd())sys.executable告诉你当前内核用的 Python 解释器路径。如果你在 base 环境启动,却期望它使用另一个 conda 环境,这里一眼就能看出来。os.getcwd()告诉你内核当前的工作目录。注意:这个目录不一定等于 Notebook 文件所在的目录,也不一定等于页面顶部显示的路径。- 如果你想用某一个 conda 环境作为内核,可以在页面右上角 “Kernel” 菜单里选择 “Change Kernel”;如果没看到你需要的环境,就需要在对应环境里安装
ipykernel。
这一步不是浪费时间,它能提前排查掉很多“装了包但导入失败”的错觉。很多时候你觉得自己已经安装了包,其实装进的是另一个环境。
2.3 看到“不是内部或外部命令”、空白页、启动失败代码2,优先查这些
Windows 下高频报错里,“jupyter 不是内部或外部命令”几乎都能归结为 PATH 或环境激活问题。排查顺序是:
where jupyter python -m notebook --version conda list jupyter如果where jupyter找不到,说明当前终端的 PATH 里没有 Jupyter 脚本目录。这时候先确认你用的是不是 Anaconda Prompt;如果是普通 CMD,需要先激活 conda 环境,或者改用python -m jupyter notebook这种模块调用方式,绕过入口脚本。
“启动失败代码 2”比较模糊,常见原因是入口脚本损坏、Anaconda 与系统 Python 冲突、或这个环境里的 Jupyter 安装不完整。建议先看终端里的完整报错,重点看最早出现的长提示,再考虑用python -m jupyter notebook启动;如果这种方式能启动,说明入口脚本问题更多。必要的时候,在该环境里重新安装:
pip install --user notebook ipykernel至于“启动后空白页”,不要一上来就重装。先做四件事:换浏览器、开无痕窗口、清缓存、看启动终端是否报错。空白页常见的来源是浏览器插件冲突、Jupyter 前端资源加载失败、或者 Notebook 输出内容过大导致页面卡死。
如果用了很多扩展脚本和主题样式,也可以禁用后重启 Jupyter,再判断是否扩展导致。
3. 单元格操作不难,难的是“执行顺序”
3.1 代码单元格、Markdown单元格、快捷键,一次讲清
Jupyter 里一个单元格可以有两种常见类型:
- Code:写 Python 代码,运行后显示输出结果。
- Markdown:写说明文字,用 Markdown 语法排版,运行后变成格式化文本。
在单元格里,按下Esc进入命令模式,就可以对单元格做键盘操作;按Enter进入编辑模式,就可以修改内容。新手最需要掌握的几个快捷键:
| 快捷键 | 作用 |
|---|---|
Shift + Enter | 运行当前单元格,并选中下一个单元格 |
Ctrl + Enter | 运行当前单元格,停留在当前位置 |
Alt + Enter | 运行当前单元格,并在下方插入新单元格 |
A | 在选中单元格上方插入新单元格 |
B | 在选中单元格下方插入新单元格 |
M | 把当前单元格转成 Markdown |
Y | 把当前单元格转成 Code |
D D | 连续按两次 D,删除当前单元格 |
这些快捷键不需要一次性背完,先用Shift + Enter、A、B、M这几个,基本就能顺畅操作。接着,真正要理解的是“执行顺序”。
3.2 内核状态、变量污染、重复执行,新手翻车重灾区
Jupyter 的代码运行逻辑和脚本文件不一样:脚本文件每次从头到尾执行,变量是干净的;Jupyter 的内核则一直保存着所有变量的状态,你每运行一个单元格,就像给这个“会话状态”多打了一个补丁。
举个例子:
- 第一个单元格写
x = 1,运行它。 - 第二个单元格写
print(x),运行,输出1。 - 回到第一个单元格,把
x = 1改成x = 2,但不重新运行。 - 再运行第二个单元格,输出仍然是
1。
这不是 bug,而是 Jupyter 的正常行为。因为修改单元格不会自动影响内核内存,你只有重新运行修改过的单元格,新的值才会被写进内核。
再比如:你写了一个单元格df = pd.read_csv("data.csv"),运行成功了;后来你把这个单元格删除,后面所有引用df的单元格也不会立刻报错,因为df还残留在内核变量里。可一旦你重启内核,再只运行后面的单元格,就会直接NameError。
这就是很多“昨天还能跑,今天打开就报错”的原因。不是代码没了,而是执行顺序和变量状态不是你想的那样。
3.3 一条最小验证流程:写完重新启动内核并按顺序执行
我建议把“重新启动内核并全部运行”当成一个标准动作,出现在两个时机:
- 每次你修改了前面的单元格之后。
- 每次你重新打开一个旧的 Notebook,准备继续使用之前。
操作为:Kernel -> Restart Kernel and Run All Cells。执行之后,Jupyter 会清空内核变量,然后从上到下重新运行所有代码单元格,确保结果和代码完全一致。
这段流程可以沉淀成一个三步检查:
- 确认顺序:单元格在上方,依赖它的逻辑在下方。
- 确认状态:修改过代码后,不要只运行当前单元格,要重跑所有被影响的前置单元格。
- 确认可复现:最终结果以“重启内核后全部运行”的输出为准,而不是以你自己刚才随意点过的输出为准。
养成这个习惯,你就避开了 Jupyter 最典型的一类坑:输出和代码不一致。
4. 把 Jupyter 放进真实工作流:目录、.py 文件、外部编辑器
4.1 切换目录看似简单,其实是文件路径问题的根
Jupyter 的“当前工作目录”很容易被误解。你在页面文件树里看到的目录,是启动 Jupyter 时设置的根目录;但内核工作时使用的目录,是当前 Notebook 所在目录。
如果你用pd.read_csv("data.csv")读取文件,却提示找不到文件,多半是因为当前工作目录与data.csv实际位置不一致。解决方式有三种:
- 启动时进入目标目录再启动 Jupyter;
- 在单元格里使用
%cd切换:%cd D:\projects\jupyter-demo - 使用绝对路径或基于路径库的写法:
import os os.chdir(r"D:\projects\jupyter-demo")
实际上,比较稳妥的做法是项目根目录统一,并且在 Notebook 开头用os.chdir固定一次,或者用pathlib.Path组织路径。不要在每个单元格里写一堆绝对路径,后面换机器维护起来非常痛苦。
谈到 Jupyter Lab 切换目录,其实和 Notebook 一样:Lab 左侧的文件树可以切目录,但内核工作目录仍然取决于你打开的 Notebook 文件位置。建议固定习惯:先进入项目文件夹,再在该目录下启动 Jupyter Lab。
4.2 在Jupyter里创建.py文件的主要方式
Jupyter 默认创建的是.ipynb文件,但实际项目中,你可能需要把最终逻辑整理成.py文件交给别人维护,或者在 Jupyter 里调用自己写好的 Python 模块。
常见方式有三种:
第一种,在页面文件树中新建一个文本文件,然后重命名为.py后缀,再双击打开编辑。这种方式适合创建临时脚本。
第二种,用魔术命令%%writefile在单元格里写入 Python 文件:
%%writefile demo.py def add(a, b): return a + b if __name__ == "__main__": print(add(2, 3))运行这个单元格后,当前工作目录下就会生成demo.py文件。
第三种,在 Jupyter Lab 中直接通过“File -> New -> Python File”创建.py文件,右侧会打开一个文本编辑器,写完保存即可。
如果要把已有的 Notebook 导出成脚本,可以使用:
jupyter nbconvert --to script notebook.ipynb会生成一个notebook.py,里面包含所有代码单元格,以及把 Markdown 单元格转成注释。这通常是“从探索到交付”的关键一步。
4.3 和PyCharm、VS Code协同:什么时候用哪种组合
不少人会把 Jupyter 和 PyCharm 对立起来,其实它们在流程里的角色不一样。
Jupyter 适合探索、验证、演示;PyCharm / VS Code 适合完整工程开发、重构、调试、写单元测试。你完全可以把 Jupyter 当作草稿本,等思路清晰后,把核心逻辑搬到.py文件里做封装。
- 如果你在用 PyCharm Professional,可以直接打开
.ipynb文件,它内置 Jupyter 支持,运行单元格时会连接本地的 Jupyter 内核。运行时需要配置好正确解释器,否则会出现“内核不可用”或“找不到模块”。 - 如果你用的是 PyCharm Community 版,默认不支持 Notebook 文件,建议直接浏览器用 Jupyter,或者改用 VS Code。
- VS Code 中安装“Jupyter”扩展后,也可以打开
.ipynb,右上角选择内核;对喜欢编辑器体验的人来说,这种方式能把 Notebook 和普通代码放在同一个窗口,非常方便。
无论哪一种,底层都是连接 Jupyter 内核。所以你在 Jupyter 里遇到的问题,换到编辑器里的 Jupyter 集成也大概率会遇到。先把基础概念理解透,工具切换不会给你带来多大冲击。
建议:不要试图用 Jupyter 完成所有工作,也不要因为它是“交互式笔记”就低估它。把探索和交付分开,前端工具按场景选,才是效率比较高的状态。
5. 从“随手实验”到“可复用代码”:项目化的关键几步
5.1 清理输出、固定随机种子、导出脚本
当你把一个 Notebook 当作交付物发出去之前,需要做几件小事:
第一,清理输出。运行Kernel -> Restart Kernel and Run All Cells后,如果你仍然希望交付时没有运行痕迹,或者文件中不包含过长的输出,可以在Edit -> Clear All Outputs清空输出。这样对方拿到文件后,只能看到代码和 Markdown,保持干净。
第二,固定随机种子。凡是涉及随机数的代码,比如模型初始化、随机采样、数据打乱,都应该在开头显式固定种子:
import random import numpy as np random.seed(42) np.random.seed(42)如果是深度学习相关,还要根据框架设置相关种子,比如 PyTorch 的torch.manual_seed。固定种子不能保证 100% 在所有机器上结果一致,但在同一环境下能显著提高可复现性。
第三,导出脚本。用jupyter nbconvert --to script把最终代码提取成.py文件,然后交给工程团队封装。这是将“实验代码”向“生产代码”过渡的常见路径。
5.2 记录依赖、避免“我当初能跑,现在不能跑”
Jupyter Notebook 文件只保存代码和输出,不保存 Python 环境和依赖信息。所以换台机器打开 Notebook,经常出现这样的情况:代码里import pandas,但新机器没装对应版本,于是立刻报错。
解决办法不是把包名写在 Markdown 里,而是用环境管理工具生成依赖文件:
pip freeze > requirements.txt # 或者 conda 环境 conda env export > environment.yml这能锁定当前环境中的包版本。虽然pip freeze会包含一些间接依赖,导出后可能比较冗余,但它至少能让对方快速复现你的环境。如果要更精细,建议记录:
- Python 版本
- 操作系统类型
- 关键依赖包名和版本
- 是否使用了 GPU 相关框架
另外,代码里尽量使用相对路径,不要把C:\Users\你的用户名\...这样的绝对路径写死。路径一旦写进 Notebook,换机器就是一场折腾。
5.3 用来做演示和教学时,还要注意什么
很多人用 Jupyter 做培训、演示、线上分享,踩坑最多的不是代码写错,而是演示现场“重启内核”导致变量丢失,或者某个单元格没执行,后面展示结果时出现NameError。
我的经验是:演示前一定要做一次完整的“Kernel -> Restart Kernel and Run All Cells”,确认从头到尾都能跑通。如果 Notebook 里有长时间运行的模型训练,演示前可以把结果保存成文件,在演示时直接读取,避免现场等待。
另外,Markdown 单元格不只是装饰。它承担了“解释为什么”的功能。真正好的教学 Notebook,应该让读者先通过 Markdown 知道这一步要做什么,再通过代码看到怎么实现。代码不要放太多输出,关键图表保留即可,既保证阅读节奏,也避免打开文件时被海量输出拖慢。
6. 常用问题排查链路:不要急着改代码,先按顺序查
6.1 常见问题速查表
| 问题现象 | 优先排查方向 |
|---|---|
终端提示jupyter 不是内部或外部命令 | 当前终端是否为 Anaconda Prompt;path 中是否有 Jupyter 脚本目录;是否用过python -m jupyter notebook |
| 浏览器打开 Jupyter 后空白页 | 浏览器兼容、无痕模式、清缓存、启动终端是否有报错、是否安装过冲突的扩展 |
| 启动失败,代码 2 | 查看完整报错信息;尝试python -m jupyter notebook;检查 Python 环境是否损坏 |
| 能启动,但新建 Notebook 失败 | 当前环境是否安装了ipykernel;Jupyter 版本是否过老 |
| 导入自己安装的包失败 | sys.executable是不是当前环境;对应包是否安装在该环境的 Python 里 |
| 运行结果和代码不一致 | 是否修改了前面的单元格但没有重新执行;是否依赖了已删除单元格中的变量;建议重启内核后全部运行 |
| 读取文件报“FileNotFoundError” | os.getcwd()是否在项目目录;相对路径有没有写错;考虑用%cd或绝对路径 |
.py文件修改后,Notebook 里调用结果没变 | import缓存了旧模块;尝试importlib.reload或重启内核 |
6.2 一套通用排查顺序
遇到问题,不要第一时间改代码,先按这个顺序查:
- 看现象和日志。启动 Jupyter 的那个终端终端会有完整输出,报错时先翻最上面的原始错误,不要只看浏览器页面里的红色提示。
- 确认环境。在单元格里运行
import sys; sys.executable,确认当前用的 Python 是不是你预期环境。 - 确认工作目录。用
import os; os.getcwd()检查路径,文件读取问题绝大多数出现在这里。 - 确认执行顺序。是不是所有被依赖的单元格都运行过?修改之后有没有重新执行完整流程?
- 确认边界。如果以上都没问题,再考虑 Jupyter 版本、扩展冲突、浏览器兼容、输出量过大等前端因素。
排查时还可以用两个命令快速摸底:
jupyter --version jupyter notebook --debugjupyter --version能看到各组件版本,方便判断版本过老;jupyter notebook --debug会输出更详细的启动日志,适合定位启动阶段的异常。
提醒:如果报错信息涉及 Python 入口脚本损坏,优先考虑在当前环境重新安装
jupyter和ipykernel,不要急着重装整个 Anaconda。重装属于最后手段,因为它会打断你当前所有项目环境。
说到底,Jupyter 的基础操作并不难,难的是理解它背后的执行模型:你要维护的不只是代码,还有“文档结构、内核状态、执行顺序”这三者的一致性。把这一点想明白,很多后续问题都能被提前规避。而一旦你掌握了这套内部逻辑,无论以后用 Jupyter Lab、VS Code 还是 PyCharm,本质上都是同一套思路换了一个皮肤,真正值钱的是你沉淀下来的工作方法。