Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d9214d1

Browse files
committed
Make dict.keys() and dict.items() comparable to sets, using == and !=.
(PEP 3106 requires subset comparisons too, those will come later if someone really wants them. :-)
1 parent e19aad4 commit d9214d1

2 files changed

Lines changed: 107 additions & 4 deletions

File tree

Lib/test/test_dictviews.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,51 @@ def test_constructors_not_callable(self):
1717
def test_dict_keys(self):
1818
d = {1: 10, "a": "ABC"}
1919
keys = d.keys()
20-
self.assertEqual(set(keys), {1, "a"})
2120
self.assertEqual(len(keys), 2)
21+
self.assertEqual(set(keys), {1, "a"})
22+
self.assertEqual(keys, {1, "a"})
23+
self.assertNotEqual(keys, {1, "a", "b"})
24+
self.assertNotEqual(keys, {1, "b"})
25+
self.assertNotEqual(keys, {1})
26+
self.assertNotEqual(keys, 42)
2227
self.assert_(1 in keys)
2328
self.assert_("a" in keys)
2429
self.assert_(10 not in keys)
2530
self.assert_("Z" not in keys)
31+
self.assertEqual(d.keys(), d.keys())
32+
e = {1: 11, "a": "def"}
33+
self.assertEqual(d.keys(), e.keys())
34+
del e["a"]
35+
self.assertNotEqual(d.keys(), e.keys())
2636

2737
def test_dict_items(self):
2838
d = {1: 10, "a": "ABC"}
2939
items = d.items()
30-
self.assertEqual(set(items), {(1, 10), ("a", "ABC")})
3140
self.assertEqual(len(items), 2)
41+
self.assertEqual(set(items), {(1, 10), ("a", "ABC")})
42+
self.assertEqual(items, {(1, 10), ("a", "ABC")})
43+
self.assertNotEqual(items, {(1, 10), ("a", "ABC"), "junk"})
44+
self.assertNotEqual(items, {(1, 10), ("a", "def")})
45+
self.assertNotEqual(items, {(1, 10)})
46+
self.assertNotEqual(items, 42)
3247
self.assert_((1, 10) in items)
3348
self.assert_(("a", "ABC") in items)
3449
self.assert_((1, 11) not in items)
3550
self.assert_(1 not in items)
3651
self.assert_(() not in items)
3752
self.assert_((1,) not in items)
3853
self.assert_((1, 2, 3) not in items)
54+
self.assertEqual(d.items(), d.items())
55+
e = d.copy()
56+
self.assertEqual(d.items(), e.items())
57+
e["a"] = "def"
58+
self.assertNotEqual(d.items(), e.items())
59+
60+
def test_dict_mixed_keys_items(self):
61+
d = {(1, 1): 11, (2, 2): 22}
62+
e = {1: 1, 2: 2}
63+
self.assertEqual(d.keys(), e.items())
64+
self.assertNotEqual(d.items(), e.keys())
3965

4066
def test_dict_values(self):
4167
d = {1: 10, "a": "ABC"}

Objects/dictobject.c

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,83 @@ dictview_new(PyObject *dict, PyTypeObject *type)
23992399
return (PyObject *)dv;
24002400
}
24012401

2402+
/* Forward */
2403+
PyTypeObject PyDictKeys_Type;
2404+
PyTypeObject PyDictItems_Type;
2405+
PyTypeObject PyDictValues_Type;
2406+
2407+
#define PyDictKeys_Check(obj) ((obj)->ob_type == &PyDictKeys_Type)
2408+
#define PyDictItems_Check(obj) ((obj)->ob_type == &PyDictItems_Type)
2409+
#define PyDictValues_Check(obj) ((obj)->ob_type == &PyDictValues_Type)
2410+
2411+
/* This excludes Values, since they are not sets. */
2412+
# define PyDictViewSet_Check(obj) \
2413+
(PyDictKeys_Check(obj) || PyDictItems_Check(obj))
2414+
2415+
static int
2416+
all_contained_in(PyObject *self, PyObject *other)
2417+
{
2418+
PyObject *iter = PyObject_GetIter(self);
2419+
int ok = 1;
2420+
2421+
if (iter == NULL)
2422+
return -1;
2423+
for (;;) {
2424+
PyObject *next = PyIter_Next(iter);
2425+
if (next == NULL) {
2426+
if (PyErr_Occurred())
2427+
ok = -1;
2428+
break;
2429+
}
2430+
ok = PySequence_Contains(other, next);
2431+
Py_DECREF(next);
2432+
if (ok <= 0)
2433+
break;
2434+
}
2435+
Py_DECREF(iter);
2436+
return ok;
2437+
}
2438+
2439+
static PyObject *
2440+
dictview_richcompare(PyObject *self, PyObject *other, int op)
2441+
{
2442+
assert(self != NULL);
2443+
assert(PyDictViewSet_Check(self));
2444+
assert(other != NULL);
2445+
if ((op == Py_EQ || op == Py_NE) &&
2446+
(PyAnySet_Check(other) || PyDictViewSet_Check(other)))
2447+
{
2448+
Py_ssize_t len_self, len_other;
2449+
int ok;
2450+
PyObject *result;
2451+
2452+
len_self = PyObject_Size(self);
2453+
if (len_self < 0)
2454+
return NULL;
2455+
len_other = PyObject_Size(other);
2456+
if (len_other < 0)
2457+
return NULL;
2458+
if (len_self != len_other)
2459+
ok = 0;
2460+
else if (len_self == 0)
2461+
ok = 1;
2462+
else
2463+
ok = all_contained_in(self, other);
2464+
if (ok < 0)
2465+
return NULL;
2466+
if (ok == (op == Py_EQ))
2467+
result = Py_True;
2468+
else
2469+
result = Py_False;
2470+
Py_INCREF(result);
2471+
return result;
2472+
}
2473+
else {
2474+
Py_INCREF(Py_NotImplemented);
2475+
return Py_NotImplemented;
2476+
}
2477+
}
2478+
24022479
/*** dict_keys ***/
24032480

24042481
static PyObject *
@@ -2459,7 +2536,7 @@ PyTypeObject PyDictKeys_Type = {
24592536
0, /* tp_doc */
24602537
0, /* tp_traverse */
24612538
0, /* tp_clear */
2462-
0, /* tp_richcompare */
2539+
dictview_richcompare, /* tp_richcompare */
24632540
0, /* tp_weaklistoffset */
24642541
(getiterfunc)dictkeys_iter, /* tp_iter */
24652542
0, /* tp_iternext */
@@ -2544,7 +2621,7 @@ PyTypeObject PyDictItems_Type = {
25442621
0, /* tp_doc */
25452622
0, /* tp_traverse */
25462623
0, /* tp_clear */
2547-
0, /* tp_richcompare */
2624+
dictview_richcompare, /* tp_richcompare */
25482625
0, /* tp_weaklistoffset */
25492626
(getiterfunc)dictitems_iter, /* tp_iter */
25502627
0, /* tp_iternext */

0 commit comments

Comments
 (0)