I create a dictionary,
d = {'a': 1, 'b': 2, 'c': 3, 'f': 6}
when I use dir
on its values,
print(dir(d.values()))
it gives,
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__']
but how exactly is the __ge__
supposed to be used? (and similarly, __gt__
, __le__
, ...)
as when I do,
e = {'w': 5, 'x': 6, 'y': 7, 'z': 8}
print(d.values() >= e.values())
it gives,
TypeError: '>=' not supported between instances of 'dict_values' and 'dict_values'
similar error appears if I compare dict_values
with dict
or set
or list
or ...
carrying a similar experiment with d.keys()
gives a result,
print(d.keys() >= e.keys())
gives,
False
and if I change,
e = {1: 2, 3: 4, 5: 6}
print(d.keys() >= e.keys())
gives,
False
but should it not raise an error, comparing string to int, and furthermore, len(d.keys())
is not equal to len(e.keys())
?
CodePudding user response:
Well looking at the python source code, it seems that comparison between dict_keys
is called dictview_richcompare
in the class definition and the code is here, so you see that comparison between keys only takes the length in consideration. Also, if you take a look at dict_vals
definition you will see that tp_richcompare
is not implemented! Which solves the mistery.
CodePudding user response:
If you look at d.values().__gt__.__qualname__
, it says 'object.__gt__'
. So it is probably the same as object().__gt__
, which I guess the only thing it does is that it returns NotImplemented
(which in turn raises an error about uncomparable types). See this example:
In [31]: d.values().__gt__(object())
Out[31]: NotImplemented
In [32]: d.values() > object()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-32-59ecc977c231> in <module>
----> 1 d.values() > object()
TypeError: '>' not supported between instances of 'dict_values' and 'object'
However dict_keys
overrides this behavior, as you can see on d.keys().__gt__.__qualname__
which returns 'dict_keys.__gt__'
.
As you can find in the docs, dict_keys
are dictionary view objects that act like a set (and sets are defined with comparison operations). For example, set A
is greater than B
if B
is a strict subset of A
.
For example:
In [1]: A = set()
In [2]: B = set()
In [3]: A > B
Out[3]: False
In [4]: A >= B
Out[4]: True
In [5]: A.add(3)
In [6]: A > B
Out[6]: True
In [7]: A.add(4); B.add(3)
In [8]: A > B
Out[8]: True
CodePudding user response:
I think the main reasoning is what is mentioned in the docs under Dictionary View Objects:
Keys views are set-like since their entries are unique and hashable. If all values are hashable, so that (key, value) pairs are unique and hashable, then the items view is also set-like. (Values views are not treated as set-like since the entries are generally not unique.) For set-like views, all of the operations defined for the abstract base class collections.abc.Set are available (for example, ==, <, or ^).
Now the documentation for collections.abc.Set didn't really explain anything beyond mentioning which operations are supported, so I looked to the dictobject.c source code . dictview_richcompare
is the place where the operations are defined for Dictionary Views, and after some type and edge case the main logic relies on length of both views and whether one is contained in (i.e. subset of) the other:
ok = 0;
switch(op) {
case Py_NE:
case Py_EQ:
if (len_self == len_other)
ok = all_contained_in(self, other);
if (op == Py_NE && ok >= 0)
ok = !ok;
break;
case Py_LT:
if (len_self < len_other)
ok = all_contained_in(self, other);
break;
case Py_LE:
if (len_self <= len_other)
ok = all_contained_in(self, other);
break;
case Py_GT:
if (len_self > len_other)
ok = all_contained_in(other, self);
break;
case Py_GE:
if (len_self >= len_other)
ok = all_contained_in(other, self);
break;
}
/* Return 1 if self is a subset of other, iterating over self;
0 if not; -1 if an error occurred. */
static int
all_contained_in(PyObject *self, PyObject *other)
Where the PyTypeObject
for keys, values and items are defined dictview_richcompare
is passed for PyDictKeys_Type
and PyDictItems_Type
, but 0
is passed for PyDictValues_Type
. So it's not even plugged into the same comparison method as the other two views.
I couldn't really find any comments in the file about why values was excluded, so I'm assuming it is again related to the "unique and hashable" properties mentioned in the docs. Even intuitively, it wouldn't really make much sense to compare values on the basis of subsets without their keys.