HOWTO · Matplotlib

Matplotlib 中 cla()、clf() 和 close() 的区别

本文介绍 matplotlib.pyplot.cla()、matplotlib.pyplot.clf() 和 matplotlib.pyplot.close() 的区别:它们分别清除当前 Axes、清除当前 Figure,以及关闭 Figure。它们是 pyplot 函数,不是方法。

本页内容

按你想作用的对象来选择函数:matplotlib.pyplot.cla() 清除当前 Axes,matplotlib.pyplot.clf() 清除当前 Figure,matplotlib.pyplot.close() 关闭一个 Figure。它们都是 pyplot 函数,不是方法,且各自的作用范围不同。当你只想重绘一个子图时用 cla(),想以全新布局复用一个 Figure 时用 clf(),当你用完一个 Figure 时用 close()。

区别一览

这三个函数构成作用范围的层级。cla() 作用于最小单位,即单个 Axes。clf() 作用于包含该 Axes 的整个 Figure。close() 则更进一步,将 Figure 从 pyplot 中彻底移除。

函数 作用对象 移除的内容 保留下来的内容
plt.cla() 当前 Axes 绘制的数据、标签、标题、图例及其他 Axes 内容 Axes 本身、它在布局中的位置、所有相邻 Axes 以及 Figure
plt.clf() 当前 Figure Figure 中的全部 Axes 及其内容 Figure 对象及其窗口,可供放入新的 Axes
plt.close() 由 pyplot 管理的 Figure 该 Figure 在 pyplot 中的注册(在交互式后端中还包括其窗口) 该 Figure 在 pyplot 中不复存在;相邻的 Figure 不受影响

当一个 Figure 包含多个子图时,作用范围最为关键。清除一个 Axes 会让该 Axes 保留在布局中,因此它的位置和其他子图仍然可用。清除一个 Figure 会一次性移除它的全部 Axes,但 Figure 本身仍可用于新的 Axes 和绘图。关闭一个 Figure 则结束 pyplot 对它的管理,这是三者中唯一能释放 Figure 资源的函数。

除非先选定目标,否则这三个 pyplot 函数都作用于当前的 Axes 或 Figure。如果你已经持有 Axes 或 Figure 对象,对象级操作 ax.clear() 和 fig.clear() 可以显式指定目标,而不必依赖当前恰好是哪一个 Axes 或 Figure。对于 close(),你可以直接传入 Figure,例如 plt.close(fig)。

matplotlib.pyplot.cla()

cla() 清除当前 Axes 的绘制数据、坐标轴标签、标题、图例及其他内容。Axes 本身仍留在 Figure 中的原位置,因此子图网格保持其布局,每个相邻子图也保留其图形。清除一个 Axes 不会把它从 fig.axes 中移除,也不会改变其相邻子图的位置。

这使 cla() 成为更新循环的合适工具:选中该 Axes,清除其内容,再把新数据绘制到同一位置。简单的动画和仪表盘正是采用这种模式——只刷新单个面板,而 Figure 的其余部分保持不变。

下面的示例创建两个子图,然后只清除第二个。打印出的检查结果表明,Figure 仍然包含两个 Axes,第一个 Axes 仍保留其曲线,第二个 Axes 没有曲线且标题为空。

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2 * np.pi, 100)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3.2))
ax1.plot(x, np.sin(x))
ax1.set_title("Sine (kept)")
ax2.plot(x, np.cos(x), color="orange")
ax2.set_title("Cosine (to be cleared)")
fig.tight_layout()
plt.sca(ax2)
plt.cla()
print(f"axes={len(fig.axes)}; ax1_lines={len(ax1.lines)}; ax2_lines={len(ax2.lines)}; ax2_title={ax2.get_title()!r}")
plt.close(fig)

输出:

axes=2; ax1_lines=1; ax2_lines=0; ax2_title=''

调用之前,两个子图都各有一条曲线:

调用 cla() 之前的两个子图:左侧为正弦曲线,右侧为余弦曲线。

执行 plt.cla() 之后,第二个 Axes 变空,但仍占据其位置,第一个子图保持不变:

cla() 之后的同两个子图:正弦曲线完好无损,第二个 Axes 已变空但仍在其位置上。

当子图的排列应保持不变、只需刷新一个 Axes 的内容时,选用 cla()。如果你的代码中已有 Axes 变量,ax.clear() 可以在不改变 pyplot 当前 Axes 选择的情况下,表达同样的 Axes 级别意图。

matplotlib.pyplot.clf()

matplotlib.pyplot.clf() 清除当前 Figure,包括它的全部 Axes 及其内容。它让 Figure 对象保持可复用状态,并且不会关闭关联的 Figure 管理器,因此同一个 Figure——在交互式会话中即同一个窗口——可以从头重新填充。不存在按 Axes 的 clf() 操作,因为该函数在 Figure 范围上工作。

在 clf() 之后,Figure 中没有任何 Axes,因此你必须先创建新的 Axes 才能再次绘图;直接调用 plt.plot() 会为你隐式创建一个新的 Axes。如果你打开了多个 Figure,请在调用 plt.clf() 之前选定期望的那一个;fig.clear() 是对象级的等价操作,它使目标明确。

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(8, 3.2))
axes[0].plot([0, 1, 2], [0, 1, 4])
axes[0].set_title("Line")
axes[1].bar(["A", "B", "C"], [3, 7, 5])
axes[1].set_title("Bar")
fig.tight_layout()
plt.figure(fig.number)
plt.clf()
print(f"figure_axes={len(fig.axes)}; figure_open={plt.fignum_exists(fig.number)}")
ax = fig.add_subplot(1, 1, 1)
ax.plot([0, 1, 2], [4, 1, 0], color="green")
ax.set_title("Rebuilt on the same Figure")
fig.tight_layout()
plt.close(fig)

输出:

figure_axes=0; figure_open=True

该 Figure 起初并排包含一个折线图和一个条形图:

调用 clf() 之前的 Figure:在两个子图中并排包含一个折线图和一个条形图。

输出确认 clf() 移除了全部 Axes(figure_axes=0),而 Figure 本身保持打开状态(figure_open=True)。随后示例添加了一个新的 Axes,并在同一个 Figure 上绘制了一个完全不同的单一图形:

clf() 并重建之后的同一个 Figure:一个新的绿色折线图占据了整个 Figure。

当你想让一个 Figure 保持可用、但要从头重建它的全部图形或子图布局时,使用 clf()。当同一 Figure 中的其他 Axes 必须保持原样时,改用 cla()。

matplotlib.pyplot.close()

matplotlib.pyplot.close() 关闭一个由 pyplot 管理的 Figure。不带参数调用时,它关闭当前 Figure。传入 Figure 对象、其编号或已注册的 Figure 名称,可以针对特定的 Figure,例如 plt.close(fig);传入 'all' 则可一次性关闭所有 Figure。

关闭会把该 Figure 从 pyplot 的注册表中移除,因此 plt.fignum_exists() 不再报告它,pyplot 也不再把它的内容保留在内存中。它与清除的一个重要区别在于:close() 之后,pyplot 中没有可复用的 Figure 剩下;而 clf() 之后,同一个 Figure 以空状态等待新内容。在交互式后端中,Figure 的窗口也会被销毁,不过可见窗口的确切行为取决于后端和执行上下文。

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig1, ax1 = plt.subplots(figsize=(5, 3.2))
ax1.plot([0, 1, 2], [0, 2, 1])
ax1.set_title("Report 1 (to be closed)")
fig1.tight_layout()
fig2, ax2 = plt.subplots(figsize=(5, 3.2))
ax2.plot([0, 1, 2], [1, 0, 3], color="purple")
ax2.set_title("Report 2 (kept open)")
fig2.tight_layout()
print(f"open_before={plt.get_fignums()}")
plt.close(fig1)
print(f"open_after={plt.get_fignums()}; fig1_open={plt.fignum_exists(fig1.number)}")
plt.close("all")

输出:

open_before=[1, 2]
open_after=[2]; fig1_open=False

调用之后,只有 Figure 2 仍被注册,它也是仍然可供显示或保存的那个 Figure:

close() 移除第一个 Figure 后留下的第二个 Figure:一幅标题为 Report 2 的紫色折线图。

每当你创建了不再需要的 Figure 时——尤其是在生成大量 Figure 的脚本和循环中——都应使用 close()。如果你需要复用同一个 Figure,改用 clf() 清除它;如果只需刷新一个子图,则使用 cla() 或 ax.clear()。

内存与 Figure 的生命周期

这三个函数在代价上也有所不同。cla() 和 clf() 是廉价且可重复的操作:Figure 仍在 pyplot 中注册,它的窗口和缓冲区保持存活,被丢弃的只有内容。这正是会被再次绘制的 Figure 所需要的。close() 是唯一能释放 Figure 本身的函数,因此它也是一个不再被绘制的 Figure 唯一正确的收尾方式。

pyplot 会把创建的每个 Figure 一直注册着,直到它被显式关闭;当注册表变得过大时,它会发出警告。在循环中不断创建 Figure 却不关闭,会累积内存,并最终触发 RuntimeWarning:

import warnings
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    for _ in range(25):
        plt.figure()
    print(f"figures_open={len(plt.get_fignums())}")
    print(f"too_many_figures_warning={any(str(w.message).startswith('More than 20 figures') for w in caught)}")
plt.close("all")
print(f"after_close_all={plt.get_fignums()}")

输出:

figures_open=25
too_many_figures_warning=True
after_close_all=[]

在那个循环中,cla() 和 clf() 都帮不上忙,因为问题在于已注册 Figure 的数量,而不是它们的内容。经验法则是:同一个 Figure 还要继续用,就清除;不再继续,就关闭。每个 Figure 保存一个文件的批处理任务,应在保存后立即调用 plt.close(fig),这样无论任务生成多少份报告,峰值内存都保持恒定。

应该使用哪个函数?

  • 重绘多面板 Figure 中的一个面板,例如在动画或实时仪表盘中:使用 cla()(或 ax.clear()),以便布局和其他面板得以保留。
  • 以不同的布局重新开始一个 Figure,同时保持同一个窗口或 Figure 对象:使用 clf()(或 fig.clear()),然后添加新的 Axes。
  • 用完了一个 Figure,尤其是在循环或批处理脚本中:使用 close(),传入 Figure、其编号或 'all',以便 pyplot 释放它。

最常见的错误包括:在本该用 cla() 的地方用了 clf()——这会悄无声息地抹除 Figure 中的全部子图,而不只是当前那一个;在错误的 Axes 处于当前状态时调用 cla();以及期望一个已关闭的 Figure 之后仍能通过 pyplot 访问。简而言之:用 cla() 清除一个 Axes,用 clf() 清除一个 Figure 中的全部 Axes,用 close() 为一个 Figure 收尾。