diff --git a/doc/api/next_api_changes/removals/19033-AL.rst b/doc/api/next_api_changes/removals/19033-AL.rst
new file mode 100644
index 000000000000..258950c1b86a
--- /dev/null
+++ b/doc/api/next_api_changes/removals/19033-AL.rst
@@ -0,0 +1,6 @@
+The private ``matplotlib.axes._subplots._subplot_classes`` dict has been removed
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Support for passing ``None`` to ``subplot_class_factory`` has been removed
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Explicitly pass in the base `~matplotlib.axes.Axes` class instead.
diff --git a/doc/missing-references.json b/doc/missing-references.json
index 5a5d6237a549..6cc524e98b59 100644
--- a/doc/missing-references.json
+++ b/doc/missing-references.json
@@ -20,7 +20,6 @@
       "doc/users/prev_whats_new/whats_new_3.1.0.rst:335"
     ],
     "cbar_axes": [
-      "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid.__init__:45",
       "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:45",
       "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:45"
     ],
@@ -79,39 +78,35 @@
     ],
     "matplotlib.axes.Axes.lines": [
       "doc/tutorials/intermediate/artists.rst:100",
-      "doc/tutorials/intermediate/artists.rst:440"
+      "doc/tutorials/intermediate/artists.rst:442"
     ],
     "matplotlib.axes.Axes.patch": [
       "doc/api/prev_api_changes/api_changes_0.98.x.rst:89",
-      "doc/tutorials/intermediate/artists.rst:186",
-      "doc/tutorials/intermediate/artists.rst:424"
+      "doc/tutorials/intermediate/artists.rst:187",
+      "doc/tutorials/intermediate/artists.rst:426"
     ],
     "matplotlib.axes.Axes.patches": [
-      "doc/tutorials/intermediate/artists.rst:463"
+      "doc/tutorials/intermediate/artists.rst:465"
     ],
     "matplotlib.axes.Axes.transAxes": [
-      "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows.__init__:8",
       "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows:8"
     ],
     "matplotlib.axes.Axes.transData": [
-      "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredAuxTransformBox.__init__:11",
       "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredAuxTransformBox:11",
-      "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredEllipse.__init__:8",
       "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredEllipse:8",
-      "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar.__init__:8",
       "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar:8"
     ],
     "matplotlib.axes.Axes.viewLim": [
       "doc/api/prev_api_changes/api_changes_0.99.x.rst:23"
     ],
     "matplotlib.axes.Axes.xaxis": [
-      "doc/tutorials/intermediate/artists.rst:608"
+      "doc/tutorials/intermediate/artists.rst:610"
     ],
     "matplotlib.axes.Axes.yaxis": [
-      "doc/tutorials/intermediate/artists.rst:608"
+      "doc/tutorials/intermediate/artists.rst:610"
     ],
     "matplotlib.axis.Axis.label": [
-      "doc/tutorials/intermediate/artists.rst:655"
+      "doc/tutorials/intermediate/artists.rst:657"
     ],
     "matplotlib.cm.ScalarMappable.callbacksSM": [
       "doc/api/prev_api_changes/api_changes_0.98.0.rst:10"
@@ -124,11 +119,11 @@
     ],
     "matplotlib.figure.Figure.patch": [
       "doc/api/prev_api_changes/api_changes_0.98.x.rst:89",
-      "doc/tutorials/intermediate/artists.rst:186",
-      "doc/tutorials/intermediate/artists.rst:319"
+      "doc/tutorials/intermediate/artists.rst:187",
+      "doc/tutorials/intermediate/artists.rst:320"
     ],
     "matplotlib.figure.Figure.transFigure": [
-      "doc/tutorials/intermediate/artists.rst:368"
+      "doc/tutorials/intermediate/artists.rst:369"
     ],
     "max": [
       "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Bbox.p1:4"
@@ -256,15 +251,6 @@
       "doc/tutorials/intermediate/artists.rst:44",
       "doc/tutorials/intermediate/artists.rst:67"
     ],
-    "matplotlib.axes._axes.Axes": [
-      "doc/api/artist_api.rst:189",
-      "doc/api/axes_api.rst:609",
-      "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.GeoAxes:1",
-      "lib/matplotlib/projections/polar.py:docstring of matplotlib.projections.polar.PolarAxes:1",
-      "lib/mpl_toolkits/axes_grid1/mpl_axes.py:docstring of mpl_toolkits.axes_grid1.mpl_axes.Axes:1",
-      "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.Axes:1",
-      "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D:1"
-    ],
     "matplotlib.axes._base._AxesBase": [
       "doc/api/artist_api.rst:189",
       "doc/api/axes_api.rst:609",
@@ -428,16 +414,6 @@
       "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.BlendedAffine2D:1",
       "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.BlendedGenericTransform:1"
     ],
-    "matplotlib.tri.trifinder.TriFinder": [
-      "lib/matplotlib/tri/trifinder.py:docstring of matplotlib.tri.trifinder.TrapezoidMapTriFinder:1"
-    ],
-    "matplotlib.tri.triinterpolate.TriInterpolator": [
-      "lib/matplotlib/tri/triinterpolate.py:docstring of matplotlib.tri.triinterpolate.CubicTriInterpolator:1",
-      "lib/matplotlib/tri/triinterpolate.py:docstring of matplotlib.tri.triinterpolate.LinearTriInterpolator:1"
-    ],
-    "matplotlib.tri.trirefine.TriRefiner": [
-      "lib/matplotlib/tri/trirefine.py:docstring of matplotlib.tri.trirefine.UniformTriRefiner:1"
-    ],
     "matplotlib.widgets._SelectorWidget": [
       "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.LassoSelector:1",
       "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.PolygonSelector:1",
@@ -485,7 +461,7 @@
       "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.AxisArtistHelper.Fixed:1",
       "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.AxisArtistHelper.Floating:1"
     ],
-    "mpl_toolkits.axisartist.floating_axes.Floating AxesHostAxes": [
+    "mpl_toolkits.axisartist.floating_axes.FloatingAxesHostAxes": [
       "<unknown>:1",
       "doc/api/_as_gen/mpl_toolkits.axisartist.floating_axes.rst:31:<autosummary>:1"
     ],
@@ -498,11 +474,11 @@
   },
   "py:data": {
     "matplotlib.axes.Axes.transAxes": [
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:219",
-      "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:220",
-      "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:179",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:220",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:219"
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:222",
+      "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:223",
+      "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:182",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:223",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:222"
     ]
   },
   "py:func": {
@@ -541,7 +517,6 @@
     ],
     "_iter_collection": [
       "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12",
-      "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.draw_path_collection:12",
       "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12",
       "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12",
       "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12",
@@ -549,7 +524,6 @@
     ],
     "_iter_collection_raw_paths": [
       "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12",
-      "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.draw_path_collection:12",
       "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12",
       "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12",
       "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12",
@@ -574,24 +548,22 @@
       "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Affine2DBase:13"
     ],
     "matplotlib.collections._CollectionWithSizes.set_sizes": [
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.barbs:172",
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.broken_barh:82",
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_between:112",
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_betweenx:112",
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.hexbin:168",
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.pcolor:165",
-      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.quiver:202",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.barbs:172",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.broken_barh:82",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_between:112",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_betweenx:112",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.hexbin:168",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.pcolor:165",
-      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.quiver:202",
-      "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs.__init__:176",
-      "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:206",
-      "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver.__init__:206",
-      "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver:239"
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.barbs:171",
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.broken_barh:81",
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_between:111",
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_betweenx:111",
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.hexbin:167",
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.pcolor:164",
+      "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.quiver:201",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.barbs:171",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.broken_barh:81",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_between:111",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_betweenx:111",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.hexbin:167",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.pcolor:164",
+      "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.quiver:201",
+      "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:205",
+      "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver:238"
     ],
     "matplotlib.dates.DateFormatter.__call__": [
       "doc/users/prev_whats_new/whats_new_1.5.rst:497"
@@ -727,7 +699,6 @@
       "doc/users/event_handling.rst:179"
     ],
     "Size.from_any": [
-      "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid.__init__:57",
       "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:57",
       "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:57"
     ],
diff --git a/lib/matplotlib/axes/_subplots.py b/lib/matplotlib/axes/_subplots.py
index 5e8d3822fa53..beefc077f703 100644
--- a/lib/matplotlib/axes/_subplots.py
+++ b/lib/matplotlib/axes/_subplots.py
@@ -1,6 +1,4 @@
-import functools
-
-from matplotlib import _api
+from matplotlib import _api, cbook
 from matplotlib.axes._axes import Axes
 from matplotlib.gridspec import GridSpec, SubplotSpec
 
@@ -36,15 +34,6 @@ def __init__(self, fig, *args, **kwargs):
         # This will also update the axes position.
         self.set_subplotspec(SubplotSpec._from_subplot_args(fig, args))
 
-    def __reduce__(self):
-        # get the first axes class which does not inherit from a subplotbase
-        axes_class = next(
-            c for c in type(self).__mro__
-            if issubclass(c, Axes) and not issubclass(c, SubplotBase))
-        return (_picklable_subplot_class_constructor,
-                (axes_class,),
-                self.__getstate__())
-
     @_api.deprecated(
         "3.4", alternative="get_subplotspec",
         addendum="(get_subplotspec returns a SubplotSpec instance.)")
@@ -169,53 +158,6 @@ def _make_twin_axes(self, *args, **kwargs):
         return twin
 
 
-# this here to support cartopy which was using a private part of the
-# API to register their Axes subclasses.
-
-# In 3.1 this should be changed to a dict subclass that warns on use
-# In 3.3 to a dict subclass that raises a useful exception on use
-# In 3.4 should be removed
-
-# The slow timeline is to give cartopy enough time to get several
-# release out before we break them.
-_subplot_classes = {}
-
-
-@functools.lru_cache(None)
-def subplot_class_factory(axes_class=None):
-    """
-    Make a new class that inherits from `.SubplotBase` and the
-    given axes_class (which is assumed to be a subclass of `.axes.Axes`).
-    This is perhaps a little bit roundabout to make a new class on
-    the fly like this, but it means that a new Subplot class does
-    not have to be created for every type of Axes.
-    """
-    if axes_class is None:
-        _api.warn_deprecated(
-            "3.3", message="Support for passing None to subplot_class_factory "
-            "is deprecated since %(since)s; explicitly pass the default Axes "
-            "class instead. This will become an error %(removal)s.")
-        axes_class = Axes
-    try:
-        # Avoid creating two different instances of GeoAxesSubplot...
-        # Only a temporary backcompat fix.  This should be removed in
-        # 3.4
-        return next(cls for cls in SubplotBase.__subclasses__()
-                    if cls.__bases__ == (SubplotBase, axes_class))
-    except StopIteration:
-        return type("%sSubplot" % axes_class.__name__,
-                    (SubplotBase, axes_class),
-                    {'_axes_class': axes_class})
-
-
+subplot_class_factory = cbook._make_class_factory(
+    SubplotBase, "{}Subplot", "_axes_class")
 Subplot = subplot_class_factory(Axes)  # Provided for backward compatibility.
-
-
-def _picklable_subplot_class_constructor(axes_class):
-    """
-    Stub factory that returns an empty instance of the appropriate subplot
-    class when called with an axes class. This is purely to allow pickling of
-    Axes and Subplots.
-    """
-    subplot_class = subplot_class_factory(axes_class)
-    return subplot_class.__new__(subplot_class)
diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py
index d334d48eafb2..4f080f8e6fe1 100644
--- a/lib/matplotlib/cbook/__init__.py
+++ b/lib/matplotlib/cbook/__init__.py
@@ -2188,3 +2188,53 @@ def _unikey_or_keysym_to_mplkey(unikey, keysym):
         "next": "pagedown",  # Used by tk.
     }.get(key, key)
     return key
+
+
+@functools.lru_cache(None)
+def _make_class_factory(mixin_class, fmt, attr_name=None):
+    """
+    Return a function that creates picklable classes inheriting from a mixin.
+
+    After ::
+
+        factory = _make_class_factory(FooMixin, fmt, attr_name)
+        FooAxes = factory(Axes)
+
+    ``Foo`` is a class that inherits from ``FooMixin`` and ``Axes`` and **is
+    picklable** (picklability is what differentiates this from a plain call to
+    `type`).  Its ``__name__`` is set to ``fmt.format(Axes.__name__)`` and the
+    base class is stored in the ``attr_name`` attribute, if not None.
+
+    Moreover, the return value of ``factory`` is memoized: calls with the same
+    ``Axes`` class always return the same subclass.
+    """
+
+    @functools.lru_cache(None)
+    def class_factory(axes_class):
+        # The parameter is named "axes_class" for backcompat but is really just
+        # a base class; no axes semantics are used.
+        base_class = axes_class
+
+        class subcls(mixin_class, base_class):
+            # Better approximation than __module__ = "matplotlib.cbook".
+            __module__ = mixin_class.__module__
+
+            def __reduce__(self):
+                return (_picklable_class_constructor,
+                        (mixin_class, fmt, attr_name, base_class),
+                        self.__getstate__())
+
+        subcls.__name__ = subcls.__qualname__ = fmt.format(base_class.__name__)
+        if attr_name is not None:
+            setattr(subcls, attr_name, base_class)
+        return subcls
+
+    class_factory.__module__ = mixin_class.__module__
+    return class_factory
+
+
+def _picklable_class_constructor(mixin_class, fmt, attr_name, base_class):
+    """Internal helper for _make_class_factory."""
+    factory = _make_class_factory(mixin_class, fmt, attr_name)
+    cls = factory(base_class)
+    return cls.__new__(cls)
diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py
index 3736888fdde5..184e513d5a69 100644
--- a/lib/matplotlib/tests/test_axes.py
+++ b/lib/matplotlib/tests/test_axes.py
@@ -6340,21 +6340,6 @@ def test_spines_properbbox_after_zoom():
     np.testing.assert_allclose(bb.get_points(), bb2.get_points(), rtol=1e-6)
 
 
-def test_cartopy_backcompat():
-
-    class Dummy(matplotlib.axes.Axes):
-        ...
-
-    class DummySubplot(matplotlib.axes.SubplotBase, Dummy):
-        _axes_class = Dummy
-
-    matplotlib.axes._subplots._subplot_classes[Dummy] = DummySubplot
-
-    FactoryDummySubplot = matplotlib.axes.subplot_class_factory(Dummy)
-
-    assert DummySubplot is FactoryDummySubplot
-
-
 def test_gettightbbox_ignore_nan():
     fig, ax = plt.subplots()
     remove_ticks_and_titles(fig)
diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py
index 5c169cb23303..c8b9260bc291 100644
--- a/lib/matplotlib/tests/test_pickle.py
+++ b/lib/matplotlib/tests/test_pickle.py
@@ -10,6 +10,7 @@
 import matplotlib.pyplot as plt
 import matplotlib.transforms as mtransforms
 import matplotlib.figure as mfigure
+from mpl_toolkits.axes_grid1 import parasite_axes
 
 
 def test_simple():
@@ -212,3 +213,8 @@ def test_unpickle_canvas():
     out.seek(0)
     fig2 = pickle.load(out)
     assert fig2.canvas is not None
+
+
+def test_mpl_toolkits():
+    ax = parasite_axes.host_axes([0, 0, 1, 1])
+    assert type(pickle.loads(pickle.dumps(ax))) == parasite_axes.HostAxes
diff --git a/lib/mpl_toolkits/axes_grid1/parasite_axes.py b/lib/mpl_toolkits/axes_grid1/parasite_axes.py
index 7aba5d9efa19..a140be9ef260 100644
--- a/lib/mpl_toolkits/axes_grid1/parasite_axes.py
+++ b/lib/mpl_toolkits/axes_grid1/parasite_axes.py
@@ -1,6 +1,6 @@
 import functools
 
-from matplotlib import _api
+from matplotlib import _api, cbook
 import matplotlib.artist as martist
 import matplotlib.image as mimage
 import matplotlib.transforms as mtransforms
@@ -95,12 +95,8 @@ def apply_aspect(self, position=None):
     # end of aux_transform support
 
 
-@functools.lru_cache(None)
-def parasite_axes_class_factory(axes_class):
-    return type("%sParasite" % axes_class.__name__,
-                (ParasiteAxesBase, axes_class), {})
-
-
+parasite_axes_class_factory = cbook._make_class_factory(
+    ParasiteAxesBase, "{}Parasite")
 ParasiteAxes = parasite_axes_class_factory(Axes)
 
 
@@ -277,7 +273,7 @@ def _add_twin_axes(self, axes_class, **kwargs):
         *kwargs* are forwarded to the parasite axes constructor.
         """
         if axes_class is None:
-            axes_class = self._get_base_axes()
+            axes_class = self._base_axes_class
         ax = parasite_axes_class_factory(axes_class)(self, **kwargs)
         self.parasites.append(ax)
         ax._remove_method = self._remove_any_twin
@@ -304,11 +300,10 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
         return Bbox.union([b for b in bbs if b.width != 0 or b.height != 0])
 
 
-@functools.lru_cache(None)
-def host_axes_class_factory(axes_class):
-    return type("%sHostAxes" % axes_class.__name__,
-                (HostAxesBase, axes_class),
-                {'_get_base_axes': lambda self: axes_class})
+host_axes_class_factory = cbook._make_class_factory(
+    HostAxesBase, "{}HostAxes", "_base_axes_class")
+HostAxes = host_axes_class_factory(Axes)
+SubplotHost = subplot_class_factory(HostAxes)
 
 
 def host_subplot_class_factory(axes_class):
@@ -317,10 +312,6 @@ def host_subplot_class_factory(axes_class):
     return subplot_host_class
 
 
-HostAxes = host_axes_class_factory(Axes)
-SubplotHost = subplot_class_factory(HostAxes)
-
-
 def host_axes(*args, axes_class=Axes, figure=None, **kwargs):
     """
     Create axes that can act as a hosts to parasitic axes.
diff --git a/lib/mpl_toolkits/axisartist/floating_axes.py b/lib/mpl_toolkits/axisartist/floating_axes.py
index 32fdedad0d27..ac146729b8d0 100644
--- a/lib/mpl_toolkits/axisartist/floating_axes.py
+++ b/lib/mpl_toolkits/axisartist/floating_axes.py
@@ -5,10 +5,9 @@
 # TODO :
 # see if tick_iterator method can be simplified by reusing the parent method.
 
-import functools
-
 import numpy as np
 
+from matplotlib import cbook
 import matplotlib.patches as mpatches
 from matplotlib.path import Path
 import matplotlib.axes as maxes
@@ -351,12 +350,8 @@ def adjust_axes_lim(self):
         self.set_ylim(ymin-dy, ymax+dy)
 
 
-@functools.lru_cache(None)
-def floatingaxes_class_factory(axes_class):
-    return type("Floating %s" % axes_class.__name__,
-                (FloatingAxesBase, axes_class), {})
-
-
+floatingaxes_class_factory = cbook._make_class_factory(
+    FloatingAxesBase, "Floating{}")
 FloatingAxes = floatingaxes_class_factory(
     host_axes_class_factory(axislines.Axes))
 FloatingSubplot = maxes.subplot_class_factory(FloatingAxes)