Does mixing pyplot state-machine and Object-Oriented API cause figure reference conflicts?
0 reputation · 03 Sept 2021, 08:05 UTC
0 reputation · 03 Sept 2021, 08:05 UTC
Matplotlib provides two primary interfaces for creating visualizations: the pyplot state-machine and the Object-Oriented (OO) API. While the OO API allows for explicit management of Figure and Axes objects, pyplot maintains a global state to track the active figure and current axes.
In complex applications where both interfaces are used simultaneously, there is uncertainty regarding how the global state is updated when an Axes object is manipulated via an explicit reference rather than through plt.gca(). This is particularly relevant when managing multiple figures in a non-interactive environment.
Axes object created via plt.subplots() automatically update the current axes tracked by the pyplot state?plt.plot() is called immediately after an explicit OO method call on a different figure?34775 reputation · 03 Sept 2021, 10:07 UTC
No. Calling a method on an Axes returned by plt.subplots() does not update pyplot's current-axes pointer. The behavior is deterministic: plt.plot() resolves its target as gcf().gca(), meaning it draws on the current figure's current axes—the last axes activated by a pyplot call, not the last axes you manipulated through an explicit reference.
Confirmed across recent Matplotlib versions:
plt.subplots() is itself a pyplot call, so at creation time it makes the new Figure current and its Axes current.Axes (ax.plot, ax.set_xlim) do not mutate pyplot's global state.plt.plot() after an OO call on a different figure targets the pyplot-current axes, not the OO-referenced one.Likely explanation: pyplot maintains a global current-figure and current-axes pointer. Only pyplot entry points—plt.figure, plt.subplot, plt.subplots, plt.axes, plt.sca, and gca when it auto-creates—mutate that pointer. Explicit Axes methods operate on the object directly without side effects on the global state.
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
ax1.set_title("oo") # explicit OO call
plt.plot([0, 1], [0, 1]) # pyplot call
Result: the title lands on ax1, but the line lands on ax2.
To force pyplot to target an OO-held Axes, re-activate it explicitly:
plt.sca(ax1) # or plt.figure(fig1); plt.sca(ax1)
plt.plot([0, 1], [0, 1])
Verify with a non-interactive backend:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
ax1.set_title("oo")
plt.plot([0, 1], [0, 1])
assert plt.gca() is ax2
assert len(ax1.lines) == 0
assert len(ax2.lines) == 1
Use one interface per figure. If you must mix them, call plt.sca(ax) immediately before any pyplot draw command. Avoid plt.gca() when you expect a specific Axes, because it can silently create a new Axes on the current Figure.
One missing detail changes the advice: if your Figure was created directly with matplotlib.figure.Figure() instead of plt.subplots(), it is not registered with pyplot's manager, so plt.gcf() and plt.plot() will not see it. Confirm the creation path if the behavior differs from the above.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.