Specifying dictionary argument with dict() or braces matters in set
In matplotlib home page, there is a link to a tutorial by Nicolas Rougier. In the section of the tutorial entitled "Devil is in the details", the script:
http://www.loria.fr/~rougier/teaching/matplotlib/scripts/exercice_10.py
produces the figure displayed on the web page. Line 48 of the script is:
label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65 ))
If we replace this line by:
label.set_bbox({"facecolor": "white", "edgecolor": "None","alpha":0.65})
then the edgecolor request is not taken into account. I would have thought that the two statements above were equivalent. I have asked the author of the tutorial, Nicolas Rougier, about this and he is surprised too. Is this a bug of Matplotlib ?
You can easily determine if the two dicts
are equivalent:
dict(facecolor='white', edgecolor='None', alpha=0.65 ) ==
{"facecolor": "white", "edgecolor": "None", "alpha":0.65}
This is True
.
However, if you type these literals into a Python interpreter, the resulting dictionary repr
s have the values in different orders.
{'alpha': 0.65000000000000002, 'facecolor': 'white', 'edgecolor': 'None'}
{'edgecolor': 'None', 'facecolor': 'white', 'alpha': 0.65000000000000002}
This may vary based on the version of Python you use, and I believe that in newer versions of Python it varies from run to run of the interpreter; the hash seeding is randomized to prevent dictionaries from being constructed with maliciously poor performance. The above output is from Python 2.6.6 (Win32).
Python dict
s are unordered, by which we mean that you can't rely on the order. However, when iterating over a dictionary, elements have to come out in some order. This order is influenced by the order in which the items are inserted, and though it isn't obvious, they are inserted in two different orders in these two dictionaries: the dict()
constructor gets a dictionary of keywords, which it then inserts into the constructed dict
, so in effect the elements of the first dict
are inserted twice! First in the order you specify them, then in whatever order they ended up in the dictionary constructed in that step.
Hypothesis: There is something in matplotlib
(or the version of Python you're using) that cares what order dictionary items come out in. In fact, since the second one has its edgecolor
key first, perhaps it's skipping the first value, or maybe one of the later values has a side effect that causes it to override edgecolor
(eg maybe facecolor
also sets edgecolor
to make sure there aren't gaps between faces). This might reasonably be called a bug since the behavior can differ based on the order in which the keywords happen to come out of the dictionary.
If you replace the line with:
label.set_bbox({"facecolor": "white", "alpha":0.65, "edgecolor": "None",})
it works correctly.
I think this is a bug in set_alpha
or possibly down in the renderer.
As a work-around you can set the line width to 0:
label.set_bbox({"facecolor": "white", "edgecolor": "None","alpha":0.65, 'lw':0})
which will make sure that the line won't be drawn, independent of what the color/alpha is.
The way the code works, you pass set_bbox
a dict
which is then stored in _bbox
. At draw time, if _bbox
is not None
(and there is no _bbox_patch
) _bbox
is passed to patches.bbox_artist
which is a function (labeled as a debugging function in the docstring!) which uses the dict
to generate a Rectangle
object on the fly (which is not returned!). Someplace in the set_*
and draw
in the rectangle is where the bug is.
上一篇: Symfony生成一个白页
下一篇: 用dict()或大括号指定字典参数