Adding custom attributes to matplotlib Figure and Axes instances: ill-advised?
Question:
I noticed that I can add my own attributes to matplotlib.axes.Axes()
and matplotlib.figure.Figure()
instances. For instance,
import matplotlib as mpl
fig = mpl.figure.Figure()
ax = fig.add_subplot()
ax.foo = 'bar'
In practical terms, I might like to add a basemap instance to an axes object using something like
import mpl_toolkits.basemap as basemap
ax.basemap = basemap.Basemap('mollweide', ax=ax)
So that I can add geographical features in a more object-oriented, intuitive way. Is this a documented/reliable feature of these objects, or is it accidental? In other words, can I "safely" use this?
Answers:
According to this example you can add any new property for custom Figure class. However for Axes class such task is more complicated. You have to make own Axes class
from matplotlib.pyplot import figure, show
from matplotlib.figure import Figure
from matplotlib.axes import Subplot
from mpl_toolkits.basemap import Basemap
# custom Figure class with foo property
class MyFigure(Figure):
def __init__(self, *args, **kwargs):
self.foo = kwargs.pop('foo', 'bar')
Figure.__init__(self, *args, **kwargs)
# custom Axes class
class MyAxes(Subplot):
def __init__(self, *args, **kwargs):
super(MyAxes, self).__init__(*args, **kwargs)
# add my axes
@staticmethod
def from_ax(ax=None, fig=None, *args, **kwargs):
if ax is None:
if fig is None: fig = figure(facecolor='w', edgecolor='w')
ax = MyAxes(fig, 1, 1, 1, *args, **kwargs)
fig.add_axes(ax)
return ax
# test run
# custom figure
fig = figure(FigureClass=MyFigure, foo='foo')
print (type(fig), fig.foo)
# add to figure custom axes
ax = MyAxes.from_ax(fig=fig)
print (type(fig.gca()))
# create Basemap instance and store it to 'm' property
ax.m = Basemap(llcrnrlon=-119, llcrnrlat=22, urcrnrlon=-64, urcrnrlat=49,
projection='lcc', lat_1=33, lat_2=45, lon_0=-95, resolution='c')
ax.m.drawcoastlines(linewidth=.25)
ax.m.drawcountries(linewidth=.25)
ax.m.fillcontinents(color='coral', lake_color='aqua')
print (ax.m)
show()
Output:
<class '__main__.MyFigure'> foo
<class '__main__.MyAxes'>
<mpl_toolkits.basemap.Basemap object at 0x107fc0358>
However my opinion is that you do not need to make m
property of axes class. You have to call all functions according to basemap in custom axes class i.e. in __init___
(or make special method like set_basemap
).
I noticed that I can add my own attributes to matplotlib.axes.Axes()
and matplotlib.figure.Figure()
instances. For instance,
import matplotlib as mpl
fig = mpl.figure.Figure()
ax = fig.add_subplot()
ax.foo = 'bar'
In practical terms, I might like to add a basemap instance to an axes object using something like
import mpl_toolkits.basemap as basemap
ax.basemap = basemap.Basemap('mollweide', ax=ax)
So that I can add geographical features in a more object-oriented, intuitive way. Is this a documented/reliable feature of these objects, or is it accidental? In other words, can I "safely" use this?
According to this example you can add any new property for custom Figure class. However for Axes class such task is more complicated. You have to make own Axes class
from matplotlib.pyplot import figure, show
from matplotlib.figure import Figure
from matplotlib.axes import Subplot
from mpl_toolkits.basemap import Basemap
# custom Figure class with foo property
class MyFigure(Figure):
def __init__(self, *args, **kwargs):
self.foo = kwargs.pop('foo', 'bar')
Figure.__init__(self, *args, **kwargs)
# custom Axes class
class MyAxes(Subplot):
def __init__(self, *args, **kwargs):
super(MyAxes, self).__init__(*args, **kwargs)
# add my axes
@staticmethod
def from_ax(ax=None, fig=None, *args, **kwargs):
if ax is None:
if fig is None: fig = figure(facecolor='w', edgecolor='w')
ax = MyAxes(fig, 1, 1, 1, *args, **kwargs)
fig.add_axes(ax)
return ax
# test run
# custom figure
fig = figure(FigureClass=MyFigure, foo='foo')
print (type(fig), fig.foo)
# add to figure custom axes
ax = MyAxes.from_ax(fig=fig)
print (type(fig.gca()))
# create Basemap instance and store it to 'm' property
ax.m = Basemap(llcrnrlon=-119, llcrnrlat=22, urcrnrlon=-64, urcrnrlat=49,
projection='lcc', lat_1=33, lat_2=45, lon_0=-95, resolution='c')
ax.m.drawcoastlines(linewidth=.25)
ax.m.drawcountries(linewidth=.25)
ax.m.fillcontinents(color='coral', lake_color='aqua')
print (ax.m)
show()
Output:
<class '__main__.MyFigure'> foo
<class '__main__.MyAxes'>
<mpl_toolkits.basemap.Basemap object at 0x107fc0358>
However my opinion is that you do not need to make m
property of axes class. You have to call all functions according to basemap in custom axes class i.e. in __init___
(or make special method like set_basemap
).