Skip to content

Idea: Reducing verbosity through more compact API patterns #30941

Closed

Description

@timhoffm

Summary

Matplotlib is often considered verbose. I've been pondering whether/how one could write the code more compact without completely reinventing our API. The two main points for simplification I've come up with are:

  • Don't use set_* for configuration, but instead do the configuration via kwargs. The set_* methods can be a fallback for mor complex configuration.
  • Don't hold a figure reference as it's rarely needed.

What I explicitly do not want to change:

  • No breakage of existing API
  • No completely new interface namespace
  • No method chaining

This is just an early and wild idea, not a proposal. Thoughts and feedback are welcome.

Proposed fix

The ideas are illustrated on a slightly modified variant of https://matplotlib.org/devdocs/gallery/lines_bars_and_markers/simple_plot.html

The formal Axes interface approach is:

fig, ax = plt.subplots()
ax.plot(t, s)
ax.set_xlabel('time (s)')
ax.set_ylabel('voltage (mV)')
ax.set_title('A simple plot')
plt.show()

In some parts, we already have replaced the individual setters by ax.set(...) with kwargs.

fig, ax = plt.subplots()
ax.plot(t, s)
ax.set(xlabel='time (s)', ylabel='voltage (mV)', title='A simple plot')
plt.show()

Possible improvement 1: Replace ax.set(...) by kwargs during Axes creation:

fig, ax = plt.subplots(xlabel='time (s)', ylabel='voltage (mV)', title='A simple plot')
ax.plot(t, s)
plt.show()

Possible improvement 2: Don't store a reference to the figure - rationale: The fig, ax = plt.subplots() pattern is quite boilerplate and you rarely need the figure. One can always get the figure via ax.get_figure() if needed, which is only mildy more work than holding a fig variable.

ax = plt.figure().subplots(xlabel='time (s)', ylabel='voltage (mV)', title='A simple plot')
ax.plot(t, s)
plt.show()

What is still not so nice is that you still need the plt.figure().subplots( chaining for the simple single-Axes case. While

ax = plt.axes(xlabel='time (s)', ylabel='voltage (mV)', title='A simple plot')

works with the same result, there's a caveat in that plt.axes() reuses an existing Figure if possible whereas plt.figure().subplot() always creates a new figure. That's a potential footgun and we may want to think whether there's a way around this. Otherwise

ax = plt.axes(xlabel='time (s)', ylabel='voltage (mV)', title='A simple plot')
ax.plot(t, s)
plt.show()

would look quite clean to me:

  • create an Axes with some properties set
  • add data
  • show

In jupyter notebooks you'd even not need the show and could (as an abbreviation, not as a universally recommended pattern) create the plot in one line:

plt.axes(xlabel='time (s)', ylabel='voltage (mV)', title='A simple plot').plot(t, s)

Activity

  1. anntzer commented on Jan 7, 2026

    @anntzer
    Contributor

    In the single axes case, this already exists as

    ax = plt.figure(layout="constrained", figsize=...).add_subplot(xlabel="foo", xlim=bar, ...)

    which is actually the pattern I use most of the time (In fact, this is the underlying motivation for #30585). Having a separate figure() call is useful because you can put the relevant kwargs there (figsize, layout, ...).

  2. jklymak commented on Jan 7, 2026

    @jklymak
    Member

    I use the figure handle all the time to save the figure: fig.savefig(). (EDIT: as pointed out below, also fig.colorbar is used a lot)

    ax.set(foo="") is fine, but doesn't allow any customization. eg I often pass extra args to ax.set_title('my title', loc='left', fontsize='medium').

    I personally find it cumbersome to read large argument lists inside of brackets, but wouldn't object to some keywords being added there. However, note that 95% of the time I am actually doing
    fig, axs = plt.subplots(2, 2, ...); ax[0, 0].set_xlabel() so passing labels in doesn't usually make any sense in this case.

  3. story645 commented on Jan 7, 2026

    @story645
    Member

    Possible improvement 1: Replace ax.set(...) by kwargs during Axes creation:

    I mostly like the tradeoff we currently have of more verbosity but tighter scoping. It's super easy to teach (roughly: subplots for creating things, axes methods for plotting, sets for labeling) and document, vs everything all at once methods. Which what we've been doing is recommending the third party APIs that provide the all at once methods.

  4. story645 commented on Jan 7, 2026

    @story645
    Member

    I use the figure handle all the time to save the figure: fig.savefig().

    Same. Also, there's no colorbar method on Axes (#25880) so fig.colorbar is as far as I know still the simplest way to add one.

  5. timhoffm commented on Jan 8, 2026

    @timhoffm
    MemberAuthor

    To clarify:

    • The API is all there. All of the above code works. axes(), subplots(), add_subplot() etc. all accept Axes properties (i.e. the names from set_* as kwargs. This is more a question of what we want to advertise/teach.
    • The considerations focus on the common simple case that you just want some lines/bars with some labelling on a single Axes. I can understand people if they feel it's a bit annoying to write 4-6 lines of code for that. The more complex cases remain valid and untouched untouched.

    In the single axes case, this already exists as

    ax = plt.figure(layout="constrained", figsize=...).add_subplot(xlabel="foo", xlim=bar, ...)

    As said,

     ax = plt.figure(layout="constrained", figsize=...).subplots(xlabel="foo", xlim=bar, ...)

    works as well with advantages:

    • being few letters shorter
    • being more analogous with plt.subplots()
      And you could do axs = plt.figure().subplots(2, 2), which does not help with kwargs (unless they are the same for all subplots), but still saves you some typing if you don't need the figure.

    Also, there's no colorbar method on Axes (#25880) so fig.colorbar is as far as I know still the simplest way to add one.

    Semi-OT, but it may be worth considering to add one. After all, it's the typical case that a colorbar is "semantically associated" with an Axes; and even technically, it's associated to a ScalarMappable that lives in one Axes.

  6. jklymak commented on Jan 8, 2026

    @jklymak
    Member

    I'd argue teaching one main way to do things is best, and leave shortcuts for more advanced treatment.

  7. rcomer commented on Jan 11, 2026

    @rcomer
    Member

    A tutorial or User Guide page specifically about shortcuts would be useful. It was embarrassingly recent that I learned about the set method.

  8. pinned this issue on Jan 11, 2026
  9. added this to the v3.10-doc milestone on Jan 15, 2026
  10. unpinned this issue on Feb 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions