Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MNT: when clearing an Axes via clear/cla fully detach children #24627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1315,7 +1315,9 @@ def __clear(self):
self._get_patches_for_fill = _process_plot_var_args(self, 'fill')

self._gridOn = mpl.rcParams['axes.grid']
self._children = []
old_children, self._children = self._children, []
for chld in old_children:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for not doing for chld in getattr(self, "_children", []): ... before resetting _children?

Copy link
Member Author

@tacaswell tacaswell Dec 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am more worried about the state where the Axes thinks the Artist is in the draw list rather than the case where the Artist still thinks it is in the Axes (which has been the status quo).

My first attempt was

old_children, self._children = self._children, []

but that had issues where either you check self._children before it exists or you get other attribute errors because other parts of the code are using the existence of self._children to determine if we are still in the init method or not.

I also thought about doing for chld in self._chidren: chld.remove() but was trying to avoid a lot of thrashing / maybe some premature optimization.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if compressing to one line is better or worse ....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a good idea to first get rid of this "not having _children" problem by pre-initing it early in __init__? e.g. something like

diff --git i/lib/matplotlib/axes/_base.py w/lib/matplotlib/axes/_base.py
index 3e665cda42..9eb65d3fc4 100644
--- i/lib/matplotlib/axes/_base.py
+++ w/lib/matplotlib/axes/_base.py
@@ -665,6 +665,8 @@ class _AxesBase(martist.Artist):
         self.set_box_aspect(box_aspect)
         self._axes_locator = None  # Optionally set via update(kwargs).
 
+        self._children = []
+
         # placeholder for any colorbars added that use this Axes.
         # (see colorbar.py):
         self._colorbars = []
@@ -1078,7 +1080,7 @@ class _AxesBase(martist.Artist):
         self.transScale.set(
             mtransforms.blended_transform_factory(
                 self.xaxis.get_transform(), self.yaxis.get_transform()))
-        for line in getattr(self, "_children", []):  # Not set during init.
+        for line in self._children:
             if not isinstance(line, mlines.Line2D):
                 continue
             try:
@@ -2936,22 +2938,15 @@ class _AxesBase(martist.Artist):
 
         x_stickies = y_stickies = np.array([])
         if self.use_sticky_edges:
-            # Only iterate over Axes and artists if needed.  The check for
-            # ``hasattr(ax, "_children")`` is necessary because this can be
-            # called very early in the Axes init process (e.g., for twin Axes)
-            # when these attributes don't even exist yet, in which case
-            # `get_children` would raise an AttributeError.
             if self._xmargin and scalex and self.get_autoscalex_on():
                 x_stickies = np.sort(np.concatenate([
                     artist.sticky_edges.x
                     for ax in self._shared_axes["x"].get_siblings(self)
-                    if hasattr(ax, "_children")
                     for artist in ax.get_children()]))
             if self._ymargin and scaley and self.get_autoscaley_on():
                 y_stickies = np.sort(np.concatenate([
                     artist.sticky_edges.y
                     for ax in self._shared_axes["y"].get_siblings(self)
-                    if hasattr(ax, "_children")
                     for artist in ax.get_children()]))
         if self.get_xscale() == 'log':
             x_stickies = x_stickies[x_stickies > 0]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will lead to AttributeErrors related to the Axes title attributes (and that was the point where I stopped debugging and used getattr) 🤣

I think the choice is either accept getattr or start pulling this string and see what we have to unravel which might spin out to "re-do all of the init logic in the Axes family".

chld.axes = chld.figure = None
self._mouseover_set = _OrderedSet()
self.child_axes = []
self._current_image = None # strictly for pyplot via _sci, _gci
Expand Down
13 changes: 13 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8359,6 +8359,19 @@ def test_extent_units():
im.set_extent([2, 12, date_first, date_last], clip=False)


def test_cla_clears_children_axes_and_fig():
fig, ax = plt.subplots()
lines = ax.plot([], [], [], [])
img = ax.imshow([[1]])
for art in lines + [img]:
assert art.axes is ax
assert art.figure is fig
ax.clear()
for art in lines + [img]:
assert art.axes is None
assert art.figure is None


def test_scatter_color_repr_error():

def get_next_color():
Expand Down