
On 01.07.20 13:32, Steven D'Aprano wrote:
On Wed, Jul 01, 2020 at 01:07:34PM +0200, Dominik Vilsmeier wrote:
What is the reason for `dict.items` to return a set-like object? This is the third time I've linked to the PEP:
Thanks for linking to the PEP (to be fair, the second link arrived after my post). However reading that PEP only increases the number of question marks. The PEP speaks mostly about `dict.keys` and occasionally mentions that similar things can be done for `dict.items`. For example:
Because of the set behavior, it will be possible to check whether two dicts have the same keys by simply testing: if a.keys() == b.keys(): ...
which makes perfectly sense. But then, immediately after that, it mentions:
and similarly for .items().
So this means we can do `a.items() == b.items()`. But wait, if the dicts' items are equal, aren't just the dicts themselves equal? So why not just compare `a == b`? Why would I want to compare `a.items() == b.items()`? Then, regarding the equality tests (`==`), the Specification section mentions the following for `dict_keys`:
To specify the semantics, we can specify x == y as: set(x) == set(y) if both x and y are d_keys instances set(x) == y if x is a d_keys instance x == set(y) if y is a d_keys instance
This makes sense again. And then for `dict_items`: # As well as the set operations mentioned for d_keys above. # However the specifications suggested there will not work if # the values aren't hashable. Fortunately, the operations can # still be implemented efficiently. For example, this is how # intersection can be specified: # [...] # And here is equality: def __eq__(self, other): if isinstance(other, (set, frozenset, d_keys)): if len(self) != len(other): return False for item in other: if item not in self: return False return True # [...] handling other cases This is what I expected how `dict_items` would handle equality tests. However it doesn't seem to do that: >>> self = {'a': []}.items() >>> other = {'b'} >>> self == other TypeError: unhashable type: 'list' >>> def __eq__(self, other): ... # paste the function here >>> d_keys = type({}.keys()) >>> __eq__(self, other) False >>> self == self True I don't know if it was always implemented like that but at least the PEP mentions this caveat and I find it surprising as well.