On 05.07.20 16:56, Stephen J. Turnbull wrote:
Steven D'Aprano writes:
> Regarding your observation that dict views behave poorly if they
> have unhashable values, I agree, it is both odd and makes them less
> useful. Possibly at some point between the PEP and the release of
> the feature something changed, or perhaps it's just an oversight.
I'm not sure what you expect from views, though:
Python 3.8.3 (default, May 15 2020, 14:39:37)
set([[1]])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
{'a' : [1]}.keys() <= {'a' : [1], 'b' : 2}.keys()
True
{'a' : [1]}.values() <= {'a' : [1], 'b' : 2}.values()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<=' not supported between instances of 'dict_values' and
'dict_values'
{'a' : [1]}.items() <= {'a' : [1], 'b' : 2}.items()
True
So all of the above are consistent with the behavior of sets, except
that items views do some part of comparisons themselves to deal with
non-hashables which is an extension to set behavior. And values views
don't pretend to be sets. The ValuesView ABC is not derived from Set,
presumably because dict.values returns something like a multiset.
Most set operations on key and item views seem to convert to set and
where appropriate return set (which makes sense, since returning a
view would require synthesizing a dict to be the view of!) This means
you can't do set operations (except comparisons) on items views if any
values aren't hashable.
Well, the point is that this "except comparisons" is not quite true:
>>> i = {'a': []}.items()
>>> s = {('a', 1)}
>>> i == s
TypeError: unhashable type: 'list'
If passed a set as `other` operand, dict_items seems to decide to
convert itself to a set, for no obvious reasons since, as you mentioned,
it does know how to compare itself to another view containing
non-hashable values:
>>> i == {'a': {}}.items()
False
So if you're dealing with items views and want to compare them to a set
representing dict items, then you need an extra `try/except` in order to
handle non-hashable values in the items view. Not only does this require
an extra precautionary step, it also seems strange given that in Python
you can compare all sorts of objects without exceptions being raised. I
can't think of any another built-in type that would raise an exception
on equality `==` comparison. dict_items seems to make an exception to
that rule.
I'm not sure what I think about this:
{'a' : 1, 'b' : 2}.values() == {'b' : 2, 'a' : 1}.values()
False
That does seem less than useful. But I guess a multiset comparison
requires an auxiliary data structure that can be sorted or a
complicated, possibly O(n^2), comparison in place.
dict_values seems to rely on object.__eq__ since they always compare
unequal (except when it is the same object); this behavior is mentioned
by the docs:
> An equality comparison between one `dict.values()` view and another
will always return `False`. This also applies when comparing
`dict.values()` to itself.
Surely that must be a relic from pre-3.7 days where dicts were unordered
and hence order-based comparison wouldn't be possible (though PEP 3106
describes an O(n*m) algorithm). However the current behavior is
unfortunate because it might trick users into believing that this is a
meaningful comparison between distinct objects (given that it works with
`dict.keys` and `dict.items`) when it isn't.
So why not make dict_values a Sequence, providing __getitem__ and
additionally order-based __eq__ comparison?
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/OVBZ5OXJ33XYAWC5HS4UEQYQHPD56TXS/
Code of Conduct: http://python.org/psf/codeofconduct/