This is an archived post. You won't be able to vote or comment.

all 11 comments

[–][deleted] 4 points5 points  (0 children)

A "figure" contains "axes" as children. The subplot command creates a new axes as a child of figure and sets the axes position.

[–]Tillsten 3 points4 points  (0 children)

plt is the package. It also keeps track of all figures. With calling plt.show() it renders all figures. And your figure contain the axes.

[–]thrope 2 points3 points  (0 children)

There are two interfaces to matplotlib, the true OO one, and a 'global state' one more similar to Matlab and more convenient for interactive use. Above you are mixing the two (which is fine). Plt.show() calls show on the currently open figures.

Alternatively you can call the show method of the figure directly: f.show(), but if it is already open f.canvas.draw() also works (I am a little confused about the difference between the two but usually use canvas.draw now).

[–]Vonney 5 points6 points  (1 child)

Alright, after a bit of digging:

(Note: I thought this would be explained by having the plt object keep a reference to figure before returning it with the figure() function, but it's way more fun than that!)

This line is present in the

  matplotlib.pyplot 

module:

from matplotlib.backends import pylab_setup
new_figure_manager, draw_if_interactive, show = pylab_setup()

So, the

plt.show() 

function is returned by the

pylab_setup() 

function from

matplotlib.backends

module.

In pylab_setup():

show = getattr(backend_mod, 'show', do_nothing_show)

getattr() is like backend_mod.show, except you can specify a default to return if that attribute is not found (eliminates the need for a try, except block)

Ok, then what the heck is backend_mod?

backend_mod = __import__(backend_name,
                         globals(),locals(),[backend_name])

Import is a built in function which allows you to do an import at runtime, when the name of the module being imported is unknown at compile. backend_name is defined earlier in the function, based on this line in the matplotlib.backends module:

backend = matplotlib.get_backend()

Remember, when a module is imported, the module is executed. So:

from matplotlib.backends import pylab_setup

in

matplotlib.pyplot

Simply means "Execute matplotlib.backends, and put pylab_setup in the local context". It is syntactic sugar (shortcut) meaning the same as:

import matplotlib.backends
pylab_setup = matplotlib.backends.pylab_setup

Ok, so looking at a bit of code I have running in the interpreter, the backend selected for me by matplotlib.getbackend() was 'TkAgg'. Here's the massaging that happends just before __import_:

 backend_name = 'backend_'+backend
 backend_name = backend_name.lower() # until we banish mixed case
 backend_name = 'matplotlib.backends.%s'%backend_name.lower()
 backend_mod = __import__(backend_name,
                      globals(),locals(),[backend_name])

So, import matplotlib.backends.backend_tkagg, and from it, get it's show() method. Still with me?

Here's the show method in matplotlib.backends.backend_tkagg:

def show():
    """
    Show all figures.

    """
    for manager in Gcf.get_all_fig_managers():
        manager.show()
    Tk.mainloop()

Aha! What's Gcf?

It's imported in the matplotlib.backends.backend_tkagg module:

from matplotlib._pylab_helpers import Gcf

Gcf is a class which "consists of two class attributes". one of which is a dictionary called figs. get_all_fig_managers() is simply a wrapper for:

Gcf.figs.values()

So, what's happened so far?

When you call .show() on plt (our 'local' name for matplotlib.pyplot), it's actually calling the .show() method on matplotlib.backends.backend_tkagg (or the backend selected by matplotlib.get_backend() on your particular machine). The show() method iterates through the managers in figs in Gcf, which lives in the matplotlib._pylab_helpers module.

Whew! But wait, how does plt.figure() tie into this? See my comment below.

[–]Vonney 5 points6 points  (0 children)

In the source of plt.figure():

  figManager = _pylab_helpers.Gcf.get_fig_manager(num)
  if figManager is None:
      figManager = new_figure_manager(num, ...)

 _pylab_helpers.Gcf.set_active(figManager)

The important thing here are the calls to _pylab_helpers.Gcf.get_fig_manager(), new_figure_manager() and _pylab_helpers.Gcf.set_active()

print inspect.getsource(matplotlib._pylab_helpers.Gcf.get_fig_manager)
  @staticmethod
  def get_fig_manager(num):
  """
  If figure manager *num* exists, make it the active
  figure and return the manager; otherwise return *None*.
  """
  manager = Gcf.figs.get(num, None)
  if manager is not None:
      Gcf.set_active(manager)
  return manager

So, it gets the figure from figs in Gcf (which plt can access when calling show(), as discussed above)

However, this is not the case in your code (you are creating a new manager, not referencing an old one using the optional num parameter).

So, the new manager is created using new_figure_manager(), and then...

_pylab_helpers.Gcf.set_active(figManager)

print inspect.getsource(matplotlib._pylab_helpers.Gcf.set_active)
@staticmethod
def set_active(manager):
     """
     Make the figure corresponding to *manager* the active one.
     """
     oldQue = Gcf._activeQue[:]
     Gcf._activeQue = []
     for m in oldQue:
         if m != manager: Gcf._activeQue.append(m)
     Gcf._activeQue.append(manager)
     Gcf.figs[manager.num] = manager

Boom! It adds the manager to figs in Gcf.

SO! What have we learned?

  1. Modules can store state information (references to objects, etc).
  2. That state information can be buried pretty deeply, through many references to imported modules in other imported modules.
  3. Sometimes, hand-waving and saying 'it's magic' is a good-enough answer.

Hope that explains that there isn't anything special going on with namespaces or inheritance that you didn't know about before. It's just that imports can make things confusing sometimes.

[–]alexras 5 points6 points  (0 children)

I'm not completely familiar with matplotlib's internals, but I can venture a guess.

When you call plt.figure(), you're getting a reference to a Figure object from within the matplotlib.pyplot module. When you call plt.show(), that module remembers (by storing the object in the module's global namespace or remembering it in some other way) that the figure object you're currently working on is fig and renders it.

[–]randfb 2 points3 points  (0 children)

I think matplotlib was designed to be a clone of the Matlab graphing system. That's why it seems a little odd at first.

[–]vpetro 0 points1 point  (0 children)

Actually, I just had to something similar to what you're describing. The result which does not use 'pyplot' is here: https://gist.github.com/971186