-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
Clarify the implementation of _process_plot_var_args. #12969
Conversation
lib/matplotlib/axes/_base.py
Outdated
returned dictionary. | ||
|
||
If some keys in the property cycle (excluding those in *ignore*) are | ||
absent or set to None in *kw*, return a copy of the next entry in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absent or set to None in *kw*, return a copy of the next entry in the | |
absent or set to None in the dict *kw*, return a copy of the next entry in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
lib/matplotlib/axes/_base.py
Outdated
update those other dictionaries with information in defaults if | ||
none of the other dictionaries contains that information. | ||
|
||
Add to *kw* the entries in *default* that are absent or set to None in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add to *kw* the entries in *default* that are absent or set to None in | |
Add to the dict *kw* the entries in the dict *default* that are absent or set to None in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
lib/matplotlib/axes/_base.py
Outdated
an empty dictionary. Ignored keys are excluded from the | ||
returned dictionary. | ||
|
||
If some keys in the property cycle (excluding those in *ignore*) are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If some keys in the property cycle (excluding those in *ignore*) are | |
If some keys in the property cycle (excluding those in the set *ignore*) are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
|
||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default_dict = self._getdefaults(set(), kw) | |
default_dict = self._getdefaults(ignore=set(), kw=kw) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok.
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: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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))
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
Having an argument named `kwargs` that's not a dict but a tuple is just a good way to trip the reader. Fortunately _getdefaults and _setdefaults in only even called with a single kwargs, so we can replace it with a single non-varargs argument and simplify the code at the same time.
3a2abb5
to
d0d36cb
Compare
@@ -391,8 +371,6 @@ def _grab_next_args(self, *args, **kwargs): | |||
|
|||
|
|||
class _AxesBase(martist.Artist): | |||
""" |
There was a problem hiding this comment.
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.
d0d36cb
to
3a2abb5
Compare
There was a problem hiding this comment.
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...
Good catch. Unfortunately, you're right.
We have sought for better solutions enough. Thus approving as is (and pushing my first-reviewer count 😄 ).
Having an argument named
kwargs
that's not a dict but a tuple is justa good way to trip the reader. Fortunately _getdefaults and
_setdefaults in only even called with a single kwargs, so we can replace
it with a single non-varargs argument and simplify the code at the same
time.
PR Summary
PR Checklist