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

Skip to content

Clarify the implementation of _process_plot_var_args. #12969

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 1 commit into from
Jan 2, 2019
Merged
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
43 changes: 13 additions & 30 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,27 +235,15 @@ def _xy_from_xy(self, x, y):
y = y[:, np.newaxis]
return x, y

def _getdefaults(self, ignore, *kwargs):
def _getdefaults(self, ignore, kw):
"""
Only advance the cycler if the cycler has information that
is not specified in any of the supplied tuple of dicts.
Ignore any keys specified in the `ignore` set.

Returns a copy of defaults dictionary if there are any
keys that are not found in any of the supplied dictionaries.
If the supplied dictionaries have non-None values for
everything the property cycler has, then just return
an empty dictionary. Ignored keys are excluded from the
returned dictionary.

If some keys in the property cycle (excluding those in the set
*ignore*) are absent or set to None in the dict *kw*, return a copy
of the next entry in the property cycle, excluding keys in *ignore*.
Otherwise, don't advance the property cycle, and return an empty dict.
"""
prop_keys = self._prop_keys
if ignore is None:
ignore = set()
prop_keys = prop_keys - ignore

if any(all(kw.get(k, None) is None for kw in kwargs)
for k in prop_keys):
prop_keys = self._prop_keys - ignore
if any(kw.get(k, None) is None for k in prop_keys):
# Need to copy this dictionary or else the next time around
# in the cycle, the dictionary could be missing entries.
default_dict = next(self.prop_cycler).copy()
Expand All @@ -265,21 +253,18 @@ def _getdefaults(self, ignore, *kwargs):
default_dict = {}
return default_dict

def _setdefaults(self, defaults, *kwargs):
def _setdefaults(self, defaults, kw):
"""
Given a defaults dictionary, and any other dictionaries,
update those other dictionaries with information in defaults if
none of the other dictionaries contains that information.

Add to the dict *kw* the entries in the dict *default* that are absent
or set to None in *kw*.
"""
for k in defaults:
if all(kw.get(k, None) is None for kw in kwargs):
for kw in kwargs:
kw[k] = defaults[k]
if kw.get(k, None) is None:
Copy link
Member

@timhoffm timhoffm Dec 10, 2018

Choose a reason for hiding this comment

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

thats

kw.update({k: v
           for k, v in defaults.items()
           if kw.get(k) is None})

Not sure if that is more readable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can bet that if anything is writable as a comprehension I will likely have tried doing so. In this case I thought the appearance of kw twice, outside and inside, hinders legibility.

Copy link
Member

@timhoffm timhoffm Dec 10, 2018

Choose a reason for hiding this comment

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

The double appearance in the expression is inherent to the problem. Essentially what we have todto (and what the expression also says):

Update kw
with keys and values from defaults
where the key is not in kw or maps to None

The more I think about it, the more I like the comprehension 😄.

Note also that kw.get(k, None) is the same as kw.get(k).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually I thought the clearest was way to just drop out the Nones first, ie

    def _getdefaults(self, ignore, kw):
        """
        If some keys in the property cycle (excluding those in the set
        *ignore*) are absent or set to None in the dict *kw*, return a copy
        of the next entry in the property cycle, excluding keys in *ignore*.
        Otherwise, don't advance the property cycle, and return an empty dict.
        """
        prop_keys = self._prop_keys - ignore
        keys_no_none = {k for k, v in kw.items() if v is not None}
        if prop_keys - keys_no_none:  # Some keys in prop_keys are not in kw.
            # Copy the default dict to avoid modifying the reference held by
            # the cycler.
            default_dict = next(self.prop_cycler).copy()
            for p in ignore:
                default_dict.pop(p, None)
        else:
            default_dict = {}
        return default_dict

and

    def _setdefaults(self, defaults, kw):
        """
        Add to the dict *kw* the entries in the dict *default* that are absent
        or set to None in *kw*.
        """
        kw_no_none = {k: v for k, v in kw.items() if v is not None}
        kw.clear()
        kw.update(defaults)
        kw.update(kw_no_none)

How does that look to you?

Copy link
Member

@timhoffm timhoffm Dec 10, 2018

Choose a reason for hiding this comment

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

Actually, self._setdefaults(default_dict, kw) is basically kw = {**default_dict, **kw}. So if we can replace the kw instance by a new one (which I suppose we can), we can get rid of the function alltogether. Or you could use a collections.ChainMap.

Copy link
Member

@timhoffm timhoffm Dec 10, 2018

Choose a reason for hiding this comment

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

Better but still a bit verbose. It's less efficient than the comprehension, but with few keywords that probably doesn't matter.

What about replacing the function with:

def _no_none(kw):
    return {k: v for k, v in kw.items() if v is not None}

kw = {**defaults, **_no_none(kw)}

or ChainMap(defaults, _no_none(kw))?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, introducing the _no_none helper makes the whole thing much simpler. Pushed a new version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, that doesn't work: we don't want to drop all the None entries in kw, only those that are present in defaults. Those that are not in defaults should be kept.
(This is needed e.g. for the closed kwarg to fill.)

Returning to the previous version...

kw[k] = defaults[k]

def _makeline(self, x, y, kw, kwargs):
kw = {**kw, **kwargs} # Don't modify the original kw.
default_dict = self._getdefaults(None, kw)
default_dict = self._getdefaults(set(), kw)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
default_dict = self._getdefaults(set(), kw)
default_dict = self._getdefaults(ignore=set(), kw=kw)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The function is defined just above, and it's not as if you can really guess what it does from the name, so in practice you're going to scroll up and see what it does anyways.

Copy link
Member

Choose a reason for hiding this comment

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

ok.

self._setdefaults(default_dict, kw)
seg = mlines.Line2D(x, y, **kw)
return seg
Expand Down Expand Up @@ -391,8 +376,6 @@ def _grab_next_args(self, *args, **kwargs):


class _AxesBase(martist.Artist):
"""
Copy link
Member

Choose a reason for hiding this comment

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

Was this maybe here to prevent docstring inheritance? OTOH, doesn't make much sense since the class is private and thus neither shows up in the HTML docs nor should a user encounter it in an interactive session. So probably fine.

"""
name = "rectilinear"

_shared_x_axes = cbook.Grouper()
Expand Down