假设你是一名实验员,面前摆着一张宽敞的实验台。台上既有写实验步骤的记录本,又有跑化学反应的仪器,还有画结果曲线的坐标纸。传统做法是:记录本放左边,仪器放中间,等结果出来再誊到坐标纸上。可 Jupyter Notebook 把三者合成了一块“魔法实验台”——同一个面板里,你能写代码、运行代码、立刻看到图表,还能在旁边写一行中文笔记,记录此刻的思考。这就像把实验记录本、仪器和坐标纸钉在了同一张桌子上,写完一行配方,伸手一按,试管里的反应立刻发生,曲线马上画在旁白处。 沿着这个比喻继续走:Notebook 是单张实验台;JupyterLab 则是一整间实验室,台面、试剂柜、废液桶一应俱全;内核是藏在台面下的动力引擎——真正的反应都在那儿发生;魔法命令是一排快捷阀门;扩展插件是可加装的滴管与探头;nbconvert 是把实验记录整理成正式论文的“排版车间”;协作与版本管理则是多名实验员共享同一本记录册的规则。掌握这套生态,数据分析、教学演示、科研复现就像在实验台上从容操作,而不是在洞穴里点油灯。

岗位关联

对 AI 从业者而言,Jupyter 生态是数据探索、模型试验与结果复现的“标准实验台”。学会它,你的分析过程将变成可回放、可分享、可交付的活文档——这是从“会写代码”走向“会做研究”的关键一跃。

目录
  1. 可视化演示
  2. Notebook快速上手
  3. 内核机制解析
  4. 魔法命令精讲
  5. 扩展插件体系
  6. nbconvert导出
  7. 协作与版本管理
  8. Notebook与Lab
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Jupyter内核机制(Browser→ZeroMQ→多语言Kernel)与Notebook单元格执行流程,动态演示代码执行输出和nbconvert多格式导出。

本节配有交互动效(进入视口后自动加载,约 3KB)。

内核机制:前后端分离多语言支持 紫色为前端(Browser/JupyterLab) 浅紫为ZeroMQ消息协议 绿色为Python内核(IPython) Frontend ZMQ Kernel(Python/R/Julia) 内核是独立进程,通过ZeroMQ消息协议通信,支持任意语言实现内核

Notebook快速上手

想象你第一次走进那间“魔法实验台”房间:面前是一个网页,但它不像普通网页那样只能看;更像一本可以当场做实验的记录本。启动 Jupyter Notebook 的方式很简单——在终端敲一行 jupyter notebook,浏览器就会弹出一个文件列表,点开一个 .ipynb 文件,你的实验台就铺开了。台面上是一个个“单元格”(Cell),有的用来写代码,有的用来写笔记。你不需要先把整个程序写完再运行,而是写一小段、跑一小段、看一小段结果,像做化学实验时加一滴试剂、观察一次变色。

这种“写一点、跑一点、看一点”的节奏,是 Jupyter 与普通脚本文件最本质的区别。普通 Python 文件是一座封闭的工厂,所有工序一次性跑完;Notebook 则像开放式实验台,你随时可以改变配方、重跑某一步。台面上有两种模式:编辑模式(Edit Mode)和命令模式(Command Mode)。编辑模式好比“正在用笔写字”,单元格边框变绿,键盘输入会进入文字;命令模式好比“放下笔、准备翻页”,单元格边框变蓝,此时可以用快捷键对整个单元格操作,比如按下 B 在下方新建单元格,按下 DD 删除当前单元格。

回到实验台的比喻:Notebook 让你第一次体会到“实验记录本长出了手脚”——代码、结果、图表和心得挤在同一页纸上却能各司其职。对小白来说,先别急着背快捷键,只要记住一个动作:在一个代码格里写 print("hello"),按 Shift+Enter,你就正式踏上 Jupyter 的交互分析之路了。

单元格类型∈{Code, Markdown, Raw NBConvert}\text{单元格类型} \in \{\text{Code},\ \text{Markdown},\ \text{Raw NBConvert}\}
单元格是实验台的最小操作单位:Code 负责“做实验”,Markdown 负责“写记录”,Raw 是原样保留的原材料。
  1. 单元格类型判定的含义像实验台上的每个器皿都有用途标签一样,Notebook 每个格子都必须声明自己是“代码”还是“笔记”。
    Code 单元格→Shift+Enter执行结果\text{Code 单元格} \xrightarrow{\text{Shift+Enter}} \text{执行结果}
  2. 执行快捷键的作用按下 Shift+Enter 好比“做完这一步并翻到下一格”,既运行当前代码,又自动跳到下一个单元格。
    Markdown 单元格→Shift+Enter渲染后的富文本\text{Markdown 单元格} \xrightarrow{\text{Shift+Enter}} \text{渲染后的富文本}
  3. 双模式切换Esc 进入命令模式(放下笔),Enter 进入编辑模式(拿起笔),两种模式对应完全不同的快捷键语义。
    Esc→命令模式,Enter→编辑模式\text{Esc} \to \text{命令模式},\quad \text{Enter} \to \text{编辑模式}

内核机制解析

实验台表面光鲜,但真正的化学反应发生在台面下的引擎室——Jupyter 的内核(Kernel)。你在浏览器里输入代码,浏览器本身并不会计算,它只是把代码打包成一条消息,通过网络发送给内核这个小房间里的 Python 解释器;解释器算出结果后,再原路送回浏览器显示。这个“前端只管展示、后端真正计算”的架构,像餐厅里服务员(前端)只负责点单和上菜,后厨(内核)才是真正颠勺炒菜的地方。

理解内核机制,要抓住三个关键点。第一,内核是常驻进程:它一启动,变量就保存在内存里,所以你在第一个单元格定义了 x = 1,第二个单元格可以直接用 x——这就是“跨单元格共享状态”。第二,执行顺序等于代码实际运行顺序,而不是单元格的书写顺序。如果你先运行了下方的格子,再回头运行上方的格子,变量会被后运行的那次覆盖,好比后厨先炒了第二道菜、再炒第一道菜,用的却是同一个锅。第三,内核死了,所有变量归零:菜单栏的 Kernel -> Restart 好比把后厨的锅碗全部清洗一遍,重新开火。

这就像后厨与服务员的分工:你不需要关心代码如何在内存中辗转腾挪,只需要知道“谁先下锅、谁后下锅”会影响最终的菜味。理解内核机制,你就理解了 Jupyter 一切“诡异行为”的根源——变量串台、输出顺序错乱、重启后全部消失,归根结底都是“锅”在引擎室里按自己的节奏运转。

执行流: 单元格i→队列内核→计算输出i\text{执行流}:\ \text{单元格}_i \xrightarrow{\text{队列}} \text{内核} \xrightarrow{\text{计算}} \text{输出}_i
单元格按你点击运行的时间顺序排队进入内核,而非按它们在页面上的上下顺序。
  1. 前端发送代码浏览器把单元格内容连同单元格序号打包成执行请求,好比服务员把写好的菜单递给后厨窗口。
    ExecuteRequest(cell_i)→Kernel\text{ExecuteRequest}(\text{cell}\_i) \to \text{Kernel}
  2. 内核排队计算如果多个单元格连续执行,请求会进入一个先进先出的队列,内核依次处理,保证计算顺序与运行顺序一致。
    Queue:cell_1,cell_2,⋯→执行顺序=入队顺序\text{Queue}: \text{cell}\_1, \text{cell}\_2, \dots \to \text{执行顺序} = \text{入队顺序}
  3. 输出回传前端内核把计算结果、图表数据封装成消息返回浏览器,浏览器做最后的渲染展示。
    Kernel→ExecuteResult(output_i)→前端渲染\text{Kernel} \to \text{ExecuteResult}(\text{output}\_i) \to \text{前端渲染}

魔法命令精讲

实验台上有些活是重复的:看时间、量温度、测性能。传统做法是写一长串标准库代码;而 Jupyter 提供了一排“快捷阀门”——魔法命令(Magic Commands),以一个或两个百分号开头,让高频操作一步到位。好比实验台上有个总开关:%time 是秒表,%run 是直接运行外部脚本的传送带,%matplotlib inline 是把图表直接“钉”在记录本上的图钉。

魔法命令分两种:行魔法以单个 % 开头,只对当前行生效,像滴管只处理一滴液体;单元格魔法以 %% 开头,作用于整个单元格,像整瓶试剂做批量反应。常用的行魔法包括 %time(测单行耗时)、%who(列出当前变量)、%pwd(查看当前路径);常用的单元格魔法包括 %%time(测整格耗时)、%%writefile(把单元格内容写成文件)、%%capture(捕获输出)。还有一个特殊的 %matplotlib inline,它让 matplotlib 绘制的图表直接显示在单元格下方,而不是弹出独立窗口——这正是“图表与代码混排”体验的关键开关。

把这些快捷阀门装进实验台后,你的操作节奏会明显加快:测性能不用 import time 再写三行样板代码,一个 %time 搞定;嵌入图表不用反复 plt.show(),一个 %matplotlib inline 永久生效。魔法命令是 Jupyter 的“加速器”,也是从“会用”走向“用得溜”的分水岭——但注意,它是快捷键而非新语言,换回普通 Python 环境就失效了。

t省=∑it手写标准代码(i)−t魔法命令(i)t_{\text{省}} = \sum_{i} t_{\text{手写标准代码}}^{(i)} - t_{\text{魔法命令}}^{(i)}
魔法命令的本质,是把 Jupyter 内部频繁使用的功能封装成简短的语法糖,替你省去重复的标准库调用。
  1. 行魔法作用于单行%time 出现在某行开头时,只测量这一行代码的执行时间,好比只掐表记录“加这一滴试剂”用了多久。
    %time expr→Wall time: t ms\%\text{time}\ \text{expr} \to \text{Wall time: } t\ \text{ms}
  2. 单元格魔法作用于整格%%time 写在单元格第一行,会测量整个单元格所有代码的总耗时,好比记录“整组反应”的全程时长。
    %%time (lines)→CPU times: tuser, tsys\%\%\text{time}\ (\text{lines}) \to \text{CPU times: } t_{\text{user}},\ t_{\text{sys}}
  3. 魔法命令是函数调用的伪装本质上魔法命令会转化为 IPython 内核提供的函数调用,因此它只能在 Jupyter/IPython 环境使用,普通 Python 脚本中无效。
    Magic≃fIPython(args)\text{Magic} \simeq f_{\text{IPython}}(\text{args})

扩展插件体系

实验台用久了,你会发现总缺点什么:想要自动补全、想要代码折叠、想要一键格式化。Jupyter 的答案不是换一张台子,而是“加装外挂”——扩展插件。Notebook 时代的插件体系以 nbextensions 为核心,一个名为 jupyter_contrib_nbextensions 的包提供了几十款插件,像是实验台的“五金工具箱”,需要哪个模块就拧上哪个模块。

插件体系有清晰的层次。最底层是 Jupyter 前端的 JavaScript 扩展,它们通过修改 Notebook 的界面与交互逻辑来增加功能;中间层是 Python 包,负责把插件文件解压、配置、注册到 Jupyter 配置目录;最上层是配置文件 jupyter_notebook_config.json,决定哪些插件默认启用。常用的扩展包括:Table of Contents(自动生成目录)、Autopep8(一键代码格式化)、Codefolding(代码折叠)、ExecuteTime(显示每个单元格的执行时间)——这些功能像是给实验台加装标签架、吸尘器和计时器。

回到实验台:插件体系的意义在于“按需装配”。你不用一开始就装几十个插件,而是随着使用痛点逐渐加装,让台面保持清爽。值得留意的是,插件之间偶尔会“打架”——配置冲突的典型表现是页面白屏或按钮失灵,此时禁用最近安装的插件往往就能恢复,这正是“实验台上一次只动一个变量”的排错智慧。

启用状态=f(配置文件,插件资源)\text{启用状态} = f(\text{配置文件}, \text{插件资源})
扩展插件的启用状态由配置文件与插件资源共同决定:装了插件但没配置,就好比买了零件没安装。
  1. 插件安装通过 pip install jupyter_contrib_nbextensions 把扩展包安装到 Python 环境,相当于把五金件搬进储藏室。
    pip→site-packages→nbextensions 目录\text{pip} \to \text{site-packages} \to \text{nbextensions 目录}
  2. 插件注册运行 jupyter contrib nbextension install 把插件资源链接到 Jupyter 的静态文件目录,相当于把零件从储藏室搬到工作台抽屉。
    资源文件→installstatic/nbextensions\text{资源文件} \xrightarrow{\text{install}} \text{static/nbextensions}
  3. 配置启用在 Notebook 首页的 Nbextensions 标签页勾选插件,勾选状态写入配置文件,刷新页面后插件生效。
    勾选→config→页面加载插件\text{勾选} \to \text{config} \to \text{页面加载插件}

nbconvert导出

实验做完了,记录本上既有代码又有图表,怎么把它变成一份能交给导师或客户的正式报告?Jupyter 提供了“排版车间”——nbconvert。这个名字是 Notebook Convert 的缩写,它的工作是把 .ipynb 文件“翻译”成其他格式。好比实验台上的记录本是活页手稿,nbconvert 则把这些活页重新排版成论文、幻灯片或网页。

nbconvert 支持多种输出格式:HTML(网页报告)、Markdown(轻量文档)、PDF(正式排版)、LaTeX(学术论文)、Python 脚本(纯代码)和 Reveal.js 幻灯片。最直观的用法是菜单栏的 File -> Download as,但更强大的是命令行:

命令行的好处是可批量、可嵌入自动化流程,好比排版车间接入了流水线:你只需写一个脚本,就能把几十份 Notebook 一次性转为网页报告。更进一步,nbconvert 支持自定义模板——通过修改 Jinja2 模板文件,你可以控制导出文档的页眉、页脚、CSS 样式,甚至定制公司 Logo 与配色,让每一份报告都带上统一的“品牌烙印”。

nbconvert 让“实验记录”变成“正式成果”的最后一公里变得极其顺畅。值得注意的是,虽然 PDF 版效果最好,但它依赖系统安装 LaTeX(在 Windows 上通常需要安装 MiKTeX),如果嫌麻烦,--to webpdf 可以通过浏览器内核直接打印为 PDF,无需本地 LaTeX——相当于排版车间的外包打印服务。

Notebook→nbconvert{HTML, Markdown, PDF, Python, ⋯ }\text{Notebook} \xrightarrow{\text{nbconvert}} \{\text{HTML},\ \text{Markdown},\ \text{PDF},\ \text{Python},\ \cdots\}
导出不是简单的文本复制:nbconvert 会实际“执行”Notebook 中的代码来获取最新输出,再套用模板完成排版。
  1. 读取 Notebooknbconvert 解析 .ipynb 的 JSON 结构,提取所有单元格、代码、输出与元数据,好比排版车间先通读手稿。
    parse(.ipynb)→{cells, outputs, metadata}\text{parse}(\text{.ipynb}) \to \{\text{cells},\ \text{outputs},\ \text{metadata}\}
  2. 执行代码单元格nbconvert 可以调用内核重新执行所有代码单元格,确保导出文档中的输出是最新状态。
    execute(cells)→新鲜的 outputs\text{execute}(\text{cells}) \to \text{新鲜的 outputs}
  3. 套用模板渲染根据目标格式选择对应模板,把单元格内容填入模板的占位符,生成最终文件。
    render(cells, template)→HTML/PDF/Markdown\text{render}(\text{cells},\ \text{template}) \to \text{HTML}/\text{PDF}/\text{Markdown}
bash3 行
jupyter nbconvert --to html 分析报告.ipynbjupyter nbconvert --to markdown 分析报告.ipynbjupyter nbconvert --to python 分析报告.ipynb

协作与版本管理

实验台只有一张,但团队有十个人,怎么共用?Jupyter 生态的协作方案分为两个层面:云同步与版本管理。云同步层面,[JupyterLab](0) 配合 JupyterHub 可以搭建多人共用的云端实验室,每个人都有独立的实验台,但共享同一份文件系统;更轻量的选择是 VS Code 的 Jupyter 插件、Google Colab 或 Deepnote,它们把“实验台”搬到了浏览器上,支持实时多人编辑,像 Google Docs 一样能同时看到对方的操作光标。

版本管理层面则要面对一个“刺头”:.ipynb 文件本质是 JSON,包含代码、输出、元数据混在一起的巨型字典,每次运行都会更新输出时间戳,导致 Git 对比时出现大量无意义差异——好比记录本上每次换笔都会留下墨迹变化,让人看不清真正的改动了哪里。解决之道有三招:第一,输出不进 Git——提交前用 Kernel -> Restart & Clear Output 清空输出再提交,或者用 nbstripout 工具自动剥离输出;第二,用专门的对比工具——nbdime 提供针对 Notebook 结构的语义化 diff,能像对比论文修订稿一样逐单元格显示差异;第三,用.gitattributes 配置驱动,让 Git 自动调用 nbdime 来处理 .ipynb。

一张实验台、多人协作,最怕的是各改各的导致记录本“散架”。Jupyter 的协作之道在于:用云平台解决“人怎么共用一张台子”,用 nbdime 解决“改动了哪里说得清”,用清空输出解决“提交记录不被噪声淹没”。对 AI 团队来说,Notebook 不是一次性交付物,而是会持续演化的活文档——只有建立起协作规范,这份活文档才能越用越有价值。

协作成本∝输出噪声+冲突频率\text{协作成本} \propto \text{输出噪声} + \text{冲突频率}
降低 Notebook 协作成本的关键有两条:减少输出噪声(清空输出再提交)与减少冲突概率(一个 Notebook 只归一人主导修改)。
  1. 输出噪声Notebook 每执行一次,输出区就可能写入新的时间戳、随机数或图像 base64,这些内容进入 Git 会产生大量“假差异”。
    diffraw(.ipynb)≫diff真实代码改动\text{diff}_{\text{raw}}(\text{.ipynb}) \gg \text{diff}_{\text{真实代码改动}}
  2. nbdime 语义对比nbdime 按单元格配对、比较代码与输出,提供类似“逐行批改”的清晰视图,让冲突定位到具体单元格。
    nbdime: cell↦cell, 只报告实质差异\text{nbdime}:\ \text{cell} \mapsto \text{cell},\ \text{只报告实质差异}
  3. 清空输出提交用 nbstripout 或手动清空输出后再提交,让版本历史聚焦于“代码怎么写的”,而非“结果长什么样”。
    commit=代码+结构,输出∉commit\text{commit} = \text{代码} + \text{结构},\quad \text{输出} \notin \text{commit}

Notebook与Lab

最后把视线拉远,看看 Jupyter 生态的“旗舰”——JupyterLab。如果说 Notebook 是一张实验台,Lab 就是一间完整实验室:左侧有文件浏览器(试剂柜)、中间是多个并排的标签页(工作台面)、下方有终端(废液处理通道)、右侧还可以打开数据文件预览与 Git 面板。Lab 与 Notebook 并非替代关系,而是“同一项目的不同形态”:Notebook 是文件格式,Lab 是操作界面。你完全可以在 Lab 里打开同一份 .ipynb,获得完全一致的单元格体验,只是周围的“家具”更齐全了。

两者的差异主要在“多任务能力”。Notebook 一次只能专注一个文件,像单工位的实验台;Lab 支持把多个 Notebook、终端、Markdown 预览拖拽到同一屏幕,像多工位的开放实验室。此外,Lab 拥有更现代的扩展机制——基于 @jupyterlab/* 包的 JupyterLab 扩展,比 Notebook 时代的 nbextensions 更规范、更强大。对于新用户,官方也建议直接安装 JupyterLab,因为它包含了完整的 Notebook 能力,同时为未来的数据科学工作流铺好了路。

回到实验台的比喻:你从一张实验台起步,慢慢发现需要更多台面、更整齐的柜子,于是走进 JupyterLab 这间完整实验室。但不管环境怎么升级,最核心的“单元格—内核—输出”交互范式始终如一。对小白的建议是:不要纠结“学 Notebook 还是 Lab”——先通过 Notebook 建立交互分析的直觉,再平滑迁移到 Lab 体验完整的实验室氛围,两条路殊途同归。

Lab 能力⊇Notebook 能力+多任务界面+现代扩展\text{Lab 能力} \supseteq \text{Notebook 能力} + \text{多任务界面} + \text{现代扩展}
JupyterLab 与 Notebook 的关系:Lab 是包含 Notebook 全部能力的“完全体”,而 Notebook 只是其中一种文件查看器。
  1. 界面差异Notebook 的单页视图只呈现当前文件;Lab 提供主区域 + 侧边栏,可同时打开多个文件与工具。
    Notebook 视图:1 文件;Lab 视图:N 文件+侧栏\text{Notebook 视图}: 1\ \text{文件};\quad \text{Lab 视图}: N\ \text{文件} + \text{侧栏}
  2. 适用场景快速轻量的单文件分析用 Notebook 足够;涉及多文件、多语言、需要终端配合的项目工作流,Lab 更游刃有余。
    任务复杂度↑⇒Lab 优势↑\text{任务复杂度} \uparrow \Rightarrow \text{Lab 优势} \uparrow
  3. 迁移升级在 Lab 界面中可以直接打开旧 .ipynb,无需任何格式转换;甚至可以把 Notebook 界面绑定为 Lab 的默认视图器,实现无缝过渡。
    迁移成本≈0: 同源同格式\text{迁移成本} \approx 0:\ \text{同源同格式}

AI 应用场景

数据探索与特征工程

阶段:数据预处理与探索性分析(EDA)

解决的问题:传统做法是写一个长脚本一口气跑完,中间过程不可见、不可控;一旦发现数据异常,要改代码重新跑整个脚本,效率极低。Jupyter 的单元格式交互让每一步操作立刻可见——读数据、看分布、查缺失值、画相关性热力图,每一步都像实验台旁的一次小实验,随时可以回溯、调整。

具体案例:一位数据分析师拿到电商用户行为数据集(上百万行),先用一个单元格做 df.info() 看列概览,再用 %matplotlib inline 让图表内嵌,画出用户购买频次直方图,发现分布严重右偏后,当场在下一个单元格写 np.log1p 做对数变换,并立刻重画对比图。整个过程一气呵成,且每个决策都留在了 Notebook 的可视化轨迹里。

模型试验与科研复现

阶段:模型训练与实验记录

解决的问题:AI 实验中要反复调整超参数、对比多组结果,传统脚本难以记录“哪个参数得到哪个结果”的对应关系。Jupyter Notebook 天然把“参数—代码—结果”—一对应地组织起来,成为可复现的实验报告。

具体案例:一位算法工程师尝试三种学习率训练同一个模型,他在 Notebook 中为每种学习率建一个“实验记录段”:先是参数说明的 Markdown 单元格,再是训练代码单元格,最后是损失曲线与评估指标的输出。训练结束后,整份 Notebook 就是一份完整的实验报告,直接 nbconvert --to html 发给团队评审,或归档到 Git 作为实验留档。

教学演示与技术分享

阶段:知识传递与结果交付

解决的问题:讲清楚一个算法,光靠 PPT 静态图不够直观;传统演示环境切换代码编辑器与幻灯片,容易中断讲解节奏。Jupyter 的“文字+代码+图”混排特性,让讲者在一个页面内完成概念解释、代码演示与结果展示,甚至可以直接在演示现场修改参数重跑实验。

具体案例:一位培训机构讲师用 JupyterLab 讲授 K 近邻算法:上半页用 Markdown 写“什么是距离度量”,下半页写 sklearn.neighbors.KNeighborsClassifier 的训练代码,紧接着内嵌画出决策边界图。讲到参数 n_neighbors 时,现场修改数值、Shift+Enter 重跑,学生立刻看到边界变化,课堂反馈远超静态 PPT。

Python 演示

python39 行
# 演示:用 nbformat 创建/读取 Notebook,并用 nbconvert 导出 HTMLimport jsonimport nbformat as nbffrom nbconvert import HTMLExporter # 1. 创建一个新的 Notebook:先建 Notebook 对象,再追加两个单元格nb = nbf.v4.new_notebook()code = nbf.v4.new_code_cell(    "import matplotlib.pyplot as plt\n"    "import numpy as np\n"    "x = np.linspace(0, 10, 100)\n"    "plt.plot(x, np.sin(x))\n"    "plt.title('Demo')")md = nbf.v4.new_markdown_cell("## 这是演示\n\n代码和笔记混排。")nb.cells.append(code)nb.cells.append(md)nb.metadata["kernelspec"] = {    "name": "python3",    "display_name": "Python 3",    "language": "python",} # 2. 把 Notebook 写入磁盘(.ipynb 本质是 JSON)with open("demo.ipynb", "w", encoding="utf-8") as f:    nbf.write(nb, f) # 3. 重新读取,验证 Notebook 结构完整with open("demo.ipynb", "r", encoding="utf-8") as f:    nb_loaded = nbf.read(f, as_version=4)print("单元格数量:", len(nb_loaded.cells))print("首个单元格类型:", nb_loaded.cells[0].cell_type) # 4. 用 nbconvert 把 Notebook 导出为 HTML 字符串html_exporter = HTMLExporter()(body, resources) = html_exporter.from_notebook_node(nb_loaded)with open("demo.html", "w", encoding="utf-8") as f:    f.write(body)print("已导出 demo.html,长度:", len(body))
目的:演示 Jupyter Notebook 文件(.ipynb)的本质是 JSON,以及如何用 nbformat 编程式创建/读取 Notebook,再用 nbconvert 完成格式导出。
  1. nb = nbf.v4.new_notebook()创建空白 Notebook 对象,相当于铺开一张空实验台。
  2. nbf.v4.new_code_cell(...)以编程方式构造“代码单元格”,代替在界面里手动输入。
  3. nb.cells.append(md)把 Markdown 笔记单元格加入 Notebook,呼应“代码与文字混排”的理念。
  4. nb.metadata["kernelspec"] = {...}声明内核为 Python 3,这是让 Notebook“跑起来”的关键元数据。
  5. nbf.write(nb, f)将 Notebook 对象序列化为 JSON 写入磁盘,.ipynb 文件就此诞生。
  6. nbf.read(f, as_version=4)反序列化读取 Notebook,验证文件可被完整还原。
  7. HTMLExporter().from_notebook_node(...)调用 nbconvert 的 HTML 导出器,完成“实验台 → 网页报告”的转换。

常见误区

误区“Notebook 不就是网页版 Python 吗?代码肯定从上往下跑,跟 `.py` 文件一样。”

正解Notebook 的实际执行顺序只取决于你点击运行的顺序,而不是单元格的排列顺序。如果你先运行了第三个格子,再回到第一个格子运行,变量状态就按“第三次先、第一次后”更新。这好比实验台上“先做第二道菜、再做第一道菜”,出锅顺序与菜谱页码无关。要避免变量串台,请养成“从上到下逐个运行”或直接用 Run All 的习惯。

为何常见:初学阶段的脚本经验根深蒂固,而 Notebook 的“随意点击”自由度恰恰打破了顺序假设,让人在不知不觉中踩坑。

误区“我改了变量定义,但结果没变,是不是要重启内核?”

正解大部分情况下只需重新运行修改的那个单元格即可。重启内核(Kernel -> Restart)会清空所有变量,相当于把实验台全部擦净重来——它解决的是“状态混乱”问题,而不是“代码没更新”问题。如果涉及 import 的模块更新,可以用 importlib.reload 重载模块,或直接重启内核获得干净环境。

为何常见:内核状态不可见,用户无法直观判断变量是否过期,只能靠“重启大法”赌一手,误以为重拳才能解决所有问题。

误区“`%time` 不是 Python 官方语法吗?我把 Notebook 代码复制到普通脚本里也能用。”

正解魔法命令是 IPython 内核提供的环境级功能,只在 Jupyter/IPython 交互环境中有效。把含 % 的代码存成 .py 文件用 python script.py 运行,会直接报语法错误。想要在脚本中测时间,应改用标准的 time 模块;魔法命令的价值在于交互场景的便捷,而非替代 Python 语法。

为何常见:Notebook 的代码块太像“纯净 Python”,导致学习者忽略了一层关键的“外壳”——IPython 内核。

误区“.ipynb 是 Notebook 专用格式,换成 JupyterLab 是不是要重新建文件?”

正解JupyterLab 与 Notebook 是“界面”与“文件格式”的关系,二者共用同一套 .ipynb。在 Lab 里直接双击打开任何 Notebook 文件,体验完全兼容;Lab 还额外支持把 Notebook 视图嵌入多标签工作区。迁移成本近乎为零,更不是“换格式”。

为何常见:两者名字不同、界面迥异,容易被误认为是两套独立工具,实际上它们是同一生态的不同前端。

误区“`.ipynb` 是 JSON,每次运行输出都会变,Git 一对比全是乱码,团队怎么协作?”

正解Notebook 完全支持协作,只是需要正确的“协作姿势”:提交前用 nbstripout 清空输出,避免噪声进入版本历史;用 nbdime 做语义级对比,让冲突定位到具体单元格;配合 JupyterHub 或 Google Colab 实现云端实时协同编辑。规范到位后,Notebook 反而比脚本更适合作“编码实验+结果留档”的团队知识库。

为何常见:很多人第一次 git diff 看到几千行 JSON 变化就被吓退,没有意识到问题出在“没清理输出”,而非“格式不适合协作”。

面试题

Q1. 在 Jupyter Notebook 中,按下 Shift+Enter 执行当前单元格后,会发生什么?

单选题 · easy

  • A.只运行代码,不跳转到下一个单元格
  • B.运行当前单元格并跳转到下一个单元格← 正确答案
  • C.打开一个新的浏览器标签页
  • D.重新启动内核

答案:B。Shift+Enter 是 Notebook 最常用的执行快捷键,语义是“运行当前单元格并选中下一个单元格”。A 对应的是 Ctrl+Enter(运行但不跳转);C 是打开新标签页的浏览器行为;D 是内核菜单操作,二者均非快捷键语义。

Q2. Jupyter Notebook 的单元格类型不包括以下哪一项?

单选题 · easy

  • A.Code
  • B.Markdown
  • C.Raw NBConvert
  • D.Shell← 正确答案

答案:D。Notebook 只支持 Code、Markdown、Raw NBConvert 三种单元格类型。"Shell"不是单元格类型,Shell 命令需要通过 ! 前缀或 Magics 在 Code 单元格中执行,因此 D 正确。

Q3. 关于 Jupyter 内核(Kernel)与前端的关系,下列说法正确的是?

单选题 · mid

  • A.前端浏览器负责所有计算,内核只负责显示
  • B.内核是独立进程,负责实际执行代码并返回结果← 正确答案
  • C.内核每次执行完单元格后都会自动销毁
  • D.内核与前端必须在同一台机器上运行

答案:B。Jupyter 采用前后端分离架构:浏览器前端负责展示,内核是独立进程(通常是 IPython)负责真正执行代码并返回输出。内核是常驻进程,不会随单元格执行完毕而销毁;通过 Jupyter 的通信协议,内核甚至可以运行在远程服务器上,因此 A、C、D 均错误。

Q4. 你在第一个单元格定义 x = 10,随后先运行第三个单元格(使用了 x),再回头运行第二个单元格(重新定义了 x = 99),那么第三个单元格再次运行时会看到什么?

单选题 · mid

  • A.10
  • B.99← 正确答案
  • C.报错 NameError
  • D.随机值

答案:B。Notebook 的执行顺序取决于运行顺序而非书写顺序。第二次定义 x = 99 后,变量 x 的值已被覆盖为 99,因此第三个单元格再运行会看到 99。这体现了“共享内核状态”的特性——实验台的“锅”是同一口,后下锅的配料会覆盖先前的状态。

Q5. 关于魔法命令,下列说法错误的是?

单选题 · mid

  • A.%time 是行魔法,只测量一行代码的执行时间
  • B.%%time 是单元格魔法,测量整个单元格的执行时间
  • C.魔法命令在普通 Python 脚本中同样有效← 正确答案
  • D.%matplotlib inline 让图表内嵌显示

答案:C。魔法命令是 IPython 内核提供的环境级语法,普通 .py 脚本运行时没有 IPython 内核,% 指令会直接触发语法错误。A、B、D 都是对行魔法/单元格魔法及%matplotlib inline作用的正确描述,因此错误的是 C。

Q6. 下列哪个命令用于把 Notebook 导出为 HTML?

单选题 · easy

  • A.jupyter nbconvert --to html 文件.ipynb← 正确答案
  • B.python 文件.ipynb
  • C.jupyter notebook --export html 文件.ipynb
  • D.pip install html 文件.ipynb

答案:A。nbconvert 命令行工具的格式是 jupyter nbconvert --to <格式> 文件.ipynb,因此 A 正确。B 试图用 Python 解释器运行一个 JSON 文件;C 中 jupyter notebook 没有 --export 参数;D 是 pip 安装命令,与导出无关。

Q7. 使用 nbdime 的主要目的是什么?

单选题 · mid

  • A.把 Notebook 转为 PDF
  • B.对两个 Notebook 做语义级差异对比,辅助解决 Git 冲突← 正确答案
  • C.自动安装 Notebook 扩展插件
  • D.清空 Notebook 的所有输出

答案:B。nbdime 是专为 Notebook 设计的语义对比工具,它按单元格配对、分别比较代码与输出,让冲突可读可解。A 是 nbconvert 的职责;C 是 nbextensions 的安装器职责;D 是 nbstripout 或手动清空输出的职责,均与 nbdime 无关。

Q8. Jupyter 扩展插件的正确启用流程包括哪些步骤?

多选题 · mid

  • A.用 pip 安装扩展包← 正确答案
  • B.运行 jupyter contrib nbextension install 注册插件资源← 正确答案
  • C.在 Nbextensions 标签页勾选启用← 正确答案
  • D.重启操作系统才能生效

答案:ABC。扩展插件三步走:pip 安装、install 命令注册、界面勾选启用。D 错误,启用后只需刷新浏览器页面即可生效,无需重启操作系统。“重启操作系统才能生效”是典型的新手误解。

Q9. 关于 JupyterLab 与 Jupyter Notebook 的关系,正确的说法有?

多选题 · easy

  • A.二者共用相同的 .ipynb 文件格式← 正确答案
  • B.JupyterLab 支持多标签界面与侧边栏← 正确答案
  • C.JupyterLab 无法打开旧 Notebook 文件
  • D.Lab 的扩展机制比 Notebook 时代更规范← 正确答案

答案:ABD。Lab 与 Notebook 是同一生态的不同前端,共用 .ipynb 格式,因此 A 正确;Lab 的界面是多标签 + 侧边栏,B 正确;Lab 可以直接打开旧 Notebook 文件,迁移成本为零,C 错误;Lab 基于 @jupyterlab/* 的现代扩展体系确实比 nbextensions 更规范,D 正确。

Q10. 两个同事同时修改同一个 .ipynb 并在 Git 中合并,出现冲突后,合适的处理方式包括?

多选题 · hard

  • A.先清空输出再对比,避免输出噪声干扰← 正确答案
  • B.用 nbdime 按单元格语义级对比定位冲突← 正确答案
  • C.直接用文本编辑器手动修改冲突标记
  • D.用 git checkout --theirs 无脑选择远程版本丢弃本地改动

答案:AB。清空输出能消除时间戳等噪声,nbdime 能按单元格定位真正的代码冲突,A、B 正确。C 容易破坏 JSON 结构,因为 .ipynb 是嵌套 JSON,直接改冲突标记极易留语法错误;D 是极端做法,会静默丢失本地改动,属于应急手段而非“合适处理方式”。