From 045b3ba184f78a1ff0ca96561d8b5651c22233c0 Mon Sep 17 00:00:00 2001 From: Daniel Stutzbach Date: Thu, 2 Sep 2010 15:06:06 +0000 Subject: [PATCH] Issue #9212: Added the missing isdisjoint method to the dict_keys and dict_items views. The method is required by the collections.Set ABC, which the views register as supporting. --- Doc/library/stdtypes.rst | 5 ++++ Lib/test/test_dictviews.py | 35 +++++++++++++++++++++++ Misc/NEWS | 3 ++ Objects/dictobject.c | 58 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index a92d7b9b617..a732fb29446 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2216,6 +2216,11 @@ available ("other" refers either to another view or a set): Return the symmetric difference (all elements either in *dictview* or *other*, but not in both) of the dictview and the other object as a new set. +.. method:: dictview.isdisjoint(other) + + Return True if the view has no elements in common with *other*. Sets are + disjoint if and only if their intersection is the empty set. + An example of dictionary view usage:: diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py index 277e05b3679..4c040796f5e 100644 --- a/Lib/test/test_dictviews.py +++ b/Lib/test/test_dictviews.py @@ -112,6 +112,24 @@ class DictSetTest(unittest.TestCase): self.assertEqual(d1.keys() ^ set(d3.keys()), {'a', 'b', 'd', 'e'}) + self.assertFalse(d1.keys().isdisjoint(d1.keys())) + self.assertFalse(d1.keys().isdisjoint(d2.keys())) + self.assertFalse(d1.keys().isdisjoint(list(d2.keys()))) + self.assertFalse(d1.keys().isdisjoint(set(d2.keys()))) + self.assertTrue(d1.keys().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(d1.keys().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(d1.keys().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(d1.keys().isdisjoint(set(['x', 'y']))) + self.assertTrue(d1.keys().isdisjoint(['x', 'y'])) + self.assertTrue(d1.keys().isdisjoint({})) + self.assertTrue(d1.keys().isdisjoint(d3.keys())) + + de = {} + self.assertTrue(de.keys().isdisjoint(set())) + self.assertTrue(de.keys().isdisjoint([])) + self.assertTrue(de.keys().isdisjoint(de.keys())) + self.assertTrue(de.keys().isdisjoint([1])) + def test_items_set_operations(self): d1 = {'a': 1, 'b': 2} d2 = {'a': 2, 'b': 2} @@ -144,6 +162,23 @@ class DictSetTest(unittest.TestCase): self.assertEqual(d1.items() ^ d3.items(), {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) + self.assertFalse(d1.items().isdisjoint(d1.items())) + self.assertFalse(d1.items().isdisjoint(d2.items())) + self.assertFalse(d1.items().isdisjoint(list(d2.items()))) + self.assertFalse(d1.items().isdisjoint(set(d2.items()))) + self.assertTrue(d1.items().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(d1.items().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(d1.items().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(d1.items().isdisjoint(set(['x', 'y']))) + self.assertTrue(d1.items().isdisjoint({})) + self.assertTrue(d1.items().isdisjoint(d3.items())) + + de = {} + self.assertTrue(de.items().isdisjoint(set())) + self.assertTrue(de.items().isdisjoint([])) + self.assertTrue(de.items().isdisjoint(de.items())) + self.assertTrue(de.items().isdisjoint([1])) + def test_main(): support.run_unittest(DictSetTest) diff --git a/Misc/NEWS b/Misc/NEWS index 92d858a92df..97171e09443 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.2 Alpha 2? Core and Builtins ----------------- +- Issue #9212: dict_keys and dict_items now provide the isdisjoint() + method, to conform to the Set ABC. + - Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 745e2dc9fe8..241790ceba2 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2807,7 +2807,63 @@ static PyNumberMethods dictviews_as_number = { (binaryfunc)dictviews_or, /*nb_or*/ }; +static PyObject* +dictviews_isdisjoint(PyObject *self, PyObject *other) +{ + PyObject *it; + PyObject *item = NULL; + + if (self == other) { + if (dictview_len((dictviewobject *)self) == 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + /* Iterate over the shorter object (only if other is a set, + * because PySequence_Contains may be expensive otherwise): */ + if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { + Py_ssize_t len_self = dictview_len((dictviewobject *)self); + Py_ssize_t len_other = PyObject_Size(other); + if (len_other == -1) + return NULL; + + if ((len_other > len_self)) { + PyObject *tmp = other; + other = self; + self = tmp; + } + } + + it = PyObject_GetIter(other); + if (it == NULL) + return NULL; + + while ((item = PyIter_Next(it)) != NULL) { + int contains = PySequence_Contains(self, item); + Py_DECREF(item); + if (contains == -1) { + Py_DECREF(it); + return NULL; + } + + if (contains) { + Py_DECREF(it); + Py_RETURN_FALSE; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) + return NULL; /* PyIter_Next raised an exception. */ + Py_RETURN_TRUE; +} + +PyDoc_STRVAR(isdisjoint_doc, +"Return True if the view and the given iterable have a null intersection."); + static PyMethodDef dictkeys_methods[] = { + {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, + isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; @@ -2892,6 +2948,8 @@ static PySequenceMethods dictitems_as_sequence = { }; static PyMethodDef dictitems_methods[] = { + {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, + isdisjoint_doc}, {NULL, NULL} /* sentinel */ };