I have a parent class that initializes a matplotlib.axes._axes.Axes object with some configuration that is necessary so that the resulting plots are saved correctly. A child class can extend the parent classes _plot_initial_frame and _update_frame methods in order to specialize the plot. However, with the current design, I do not actually "force" the child class to initialize the plot with the correct configuration, rather I would have to rely on the developer knowing (presumably by providing documentation) that they need to call super()._plot_initial_frame() in the child class to get expected behavior. Is there a more standard approach (pythonic or otherwise) to enforce such a contract between parent and child classes? It seems like my approach is already a bit suspect in terms of design because I am essentially modifying "protected" data---having read discussions on Why is clean code suggesting avoiding protected variables and Are there any legitimate use cases for protected visibility. Below are some dummy classes showing what I mean in code. The image at the end also shows why a particular configuration needs to be enforced: without specifying the rectangular configuration in the Axes, the image (both are 2048 x 1024) is not displayed properly. You can see the left image has lots of white space around it (bbox_inches='tight' is not a solution here btw as it just clips the whitespace and the resulting image is no longer 2048 x 1024) while the right image is much "fuller" (i.e., no whitespace). from matplotlib.pyplot import axes, figure from cartopy.crs import Projection, PlateCarree class WorldMapAnimator: def __init__( self, projection: Projection = PlateCarree(), n_frames: int = 3): self._projection = projection self._n_frames = n_frames @staticmethod def get_rectangle_for_full_plot(): """Emulating a public static const""" rectangle_for_full_plot = [0, 0, 1, 1] return rectangle_for_full_plot def animate(self): self._plot_initial_frame() for frame in range(self._n_frames): self._update_frame(frame) def _plot_initial_frame(self): self._fig = figure() # IMPORTANT: The axes must have this type of rectangle configuration self._ax = axes( self.get_rectangle_for_full_plot(), projection=self._projection) self._ax.coastlines() def _update_frame(self): return class PerlinNoiseAnimator(WorldMapAnimator): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _plot_initial_frame(self): # IMPORTANT: here I am modifying a member of the parent class # and also "soft" requiring the user to call super otherwise they # won't get expected behavior... super()._plot_initial_frame() self._ax.pcolormesh(...) # TODO: write some perlin noise to worldmap return def _update_frame(self): # do something to update the map with new perlin noise here return Edit: Based on comments, I am considering the following change class WorldMapAnimator: def animate(self): self._configure_initial_frame() self._plot_initial_frame() for frame in range(self._n_frames): self._update_frame() def _configure_initial_frame(): self._fig = figure() # IMPORTANT: The axes must have this type of rectangle configuration self._ax = axes( self.get_rectangle_for_full_plot(), projection=self._projection) def _plot_initial_frame(): self._ax.coastlines() In this way, the child classes only need to worry about overriding _plot_initial_frame and the correctly configured Axes object is already handled in the public animate method. Continue reading...