很多人在 Jupyter Notebook 里第一次画 Matplotlib 图时,代码看起来没问题,结果却遇到图不显示、中文标题变方框,或者子图标签挤在一起的情况。真正影响结果的,通常不是绘图函数本身,而是显示模式有没有先启用、全局配置有没有在正确时机设置,以及你到底是在操作当前图还是明确的 axes 对象;下面就按这几个最容易踩坑的点逐一拆开。
%matplotlib inline 为什么要先执行
在 Jupyter Notebook 里,%matplotlib inline 不是装饰性写法,而是让图表直接渲染到单元格输出区的前提。少了这一步,plt.show() 可能弹出新窗口,也可能什么都不显示,尤其是在远程服务器、Docker 或 JupyterLab 里更常见。

%matplotlib inline要放在第一个绘图单元格的最上方,而且必须单独成行,不能和import写在同一行。- 执行后,当前内核的绘图后端状态会改变,之后像
plt.plot()、plt.scatter()这类调用都会自动渲染到当前单元格。 - 如果你已经运行过绘图代码但没看到图,不要只补跑
plt.show(),而是先确认这行是否漏写,再重新运行整个绘图单元格。 %matplotlib notebook并不适合作为通用替代,它依赖浏览器端 JavaScript 支持,在 GitHub Codespaces、Kaggle 这类云环境里经常报错或白屏。
也就是说,Notebook 里的显示问题,第一步不是检查 plot 参数,而是先看后端有没有在绘图前进入 inline 模式。
inline 模式下还要不要写 plt.show()
启用 %matplotlib inline 后,Matplotlib 通常会在单元格末尾自动完成渲染。这种情况下,很多示例里继续写 plt.show() 并非必需,反而可能带来重复输出或空白图的问题。
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [1, 4, 2])
- 上面这段代码已经足够在 Notebook 中显示图表,不需要再补一行
plt.show()。 - 如果你在 inline 模式下强行调用
plt.show(),有时会让同一张图被触发两次渲染,表现成重复输出,或者多出一个空图框。 - 只有在非 inline 后端,比如
QtAgg,或者你需要手动控制显示时机时,plt.show()才更有必要。 - 如果目标是保存图像,直接用
fig.savefig("xxx.png")即可,不依赖plt.show()。
判断标准可以很简单:如果图表是为了在 Notebook 单元格里直接看结果,通常依赖自动渲染就够了;如果是桌面窗口交互或特殊显示流程,再考虑显式调用 plt.show()。
中文显示异常,关键在 rcParams 的设置时机
标题、坐标轴标签一写中文就出现方框、空白或者负号显示异常,这基本都不是文本内容的问题,而是 Matplotlib 默认字体配置不支持中文,或者配置写得太晚。
更稳妥的做法,是在导入 pyplot 之后、任何绘图命令之前就把字体和负号规则设好:
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei", "KaiTi", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams属于全局配置,设一次即可影响后续绘图。- 但如果你先画图、后改配置,已经生成的 Axes 通常不会自动按新字体重绘,所以“第二次才正常”这类现象很常见。
- 如果你还会调用
plt.style.use("seaborn-whitegrid"),也要注意它可能覆盖部分rcParams,因此风格设置最好同样提前完成。 - 即使用的是
plt.subplots()来创建figure和axes,中文配置也仍然应该在前面完成,否则 axes 初始化时拿到的还是默认字体环境。
很多人以为中文乱码是“缺一个字体包”,其实更常见的原因是配置顺序错了。字体有没有装是一层,什么时候生效是另一层,两者都要对上。
多子图场景里,为什么推荐 plt.subplots()
只画一张简单折线图时,直接写 plt.plot() 问题不大;但一旦涉及多子图、局部标签设置、间距调整或复杂布局,继续依赖隐式状态就容易把代码写乱。这个时候,显式拿到 fig 和 axes 会更稳定。

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(x, y)
ax.set_xlabel("时间")
ax.set_ylabel("数值")
plt.plot()属于隐式绘图,操作默认作用在当前 active axes 上;而ax.plot()是明确告诉 Matplotlib 该往哪个子图里画。- 不建议混用
plt.xlabel()和ax.set_xlabel()。前者修改“当前图”,后者修改“指定图”,一旦有多个子图,冲突就很难排查。 plt.subplots(nrows=2, ncols=2)返回的是axes数组,常见访问方式是axes[0, 1]或axes[1],而不是把它当成通用嵌套列表随意索引。- 调整布局时,可以用
plt.subplots_adjust(hspace=0.3)手动改间距,但多数场景下fig.tight_layout()更省事,它会自动避让标签和标题,兼容性也更好。
如果你已经遇到“标题压到另一张图上”“明明设置了 xlabel 却出现在别的子图里”这类问题,通常就说明当前代码已经不适合继续靠 plt.* 的隐式状态推进了。
真正容易卡住的,是执行顺序和对象归属
回头看这些常见问题,会发现它们大多不是语法错误,而是上下文没有理顺:
%matplotlib inline没先执行,导致 Notebook 单元格里根本不出图。- 中文字体在绘图之后才配置,结果第一张图乱码、后面的图才正常。
- 子图场景里混用了
plt.*和ax.*,导致标签、标题和坐标轴设置落到错误的位置。 - 布局拥挤时只盯着单个参数调整,却忽略了
tight_layout()这类更直接的方式。
因此,Jupyter Notebook 中 Matplotlib 的稳定写法,可以概括成一条顺序链:先启用 %matplotlib inline,再做 plt.rcParams 和风格配置,随后用 plt.subplots() 获取明确的 axes,最后再开始绘图。把这个执行顺序固定下来,图表不显示、中文乱码和多子图混乱这几类问题,基本都会少很多。









