]> granicus.if.org Git - python/commitdiff
Add set.isdisjoint()
authorRaymond Hettinger <python@rcn.com>
Thu, 8 Nov 2007 02:52:43 +0000 (02:52 +0000)
committerRaymond Hettinger <python@rcn.com>
Thu, 8 Nov 2007 02:52:43 +0000 (02:52 +0000)
Doc/library/stdtypes.rst
Lib/test/test_set.py
Misc/NEWS
Objects/setobject.c

index a74758bcfc76843645792b1c3226d952c87d4719..f394b23b8566e7e0d2a9489fd190381578921862 100644 (file)
@@ -1467,6 +1467,13 @@ operations:
 
    Test *x* for non-membership in *s*.
 
+.. method:: set.isdisjoint(other)
+
+   Return True if the set has no elements in common with *other*.
+   Sets are disjoint if and only if their interesection is the empty set.
+
+   .. versionadded:: 2.6
+
 .. method:: set.issubset(other)
             set <= other
 
index 872a467a55b03f99c562b59fd6c1f1924d6830a3..aba046a33b22798c1275cbba0d42f4585b9a2b54 100644 (file)
@@ -102,6 +102,20 @@ class TestJointOps(unittest.TestCase):
             self.assertEqual(self.thetype('abcba').intersection(C('ccb')), set('bc'))
             self.assertEqual(self.thetype('abcba').intersection(C('ef')), set(''))
 
+    def test_isdisjoint(self):
+        def f(s1, s2):
+            'Pure python equivalent of isdisjoint()'
+            return not set(s1).intersection(s2)
+        for larg in '', 'a', 'ab', 'abc', 'ababac', 'cdc', 'cc', 'efgfe', 'ccb', 'ef':
+            s1 = self.thetype(larg)
+            for rarg in '', 'a', 'ab', 'abc', 'ababac', 'cdc', 'cc', 'efgfe', 'ccb', 'ef':
+                for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+                    s2 = C(rarg)
+                    actual = s1.isdisjoint(s2)
+                    expected = f(s1, s2)
+                    self.assertEqual(actual, expected)
+                    self.assert_(actual is True or actual is False)
+
     def test_and(self):
         i = self.s.intersection(self.otherword)
         self.assertEqual(self.s & set(self.otherword), i)
@@ -657,6 +671,18 @@ class TestBasicOps(unittest.TestCase):
         result = empty_set & self.set
         self.assertEqual(result, empty_set)
 
+    def test_self_isdisjoint(self):
+        result = self.set.isdisjoint(self.set)
+        self.assertEqual(result, not self.set)
+
+    def test_empty_isdisjoint(self):
+        result = self.set.isdisjoint(empty_set)
+        self.assertEqual(result, True)
+
+    def test_isdisjoint_empty(self):
+        result = empty_set.isdisjoint(self.set)
+        self.assertEqual(result, True)
+
     def test_self_symmetric_difference(self):
         result = self.set ^ self.set
         self.assertEqual(result, empty_set)
@@ -835,6 +861,22 @@ class TestBinaryOps(unittest.TestCase):
         result = self.set & set([8])
         self.assertEqual(result, empty_set)
 
+    def test_isdisjoint_subset(self):
+        result = self.set.isdisjoint(set((2, 4)))
+        self.assertEqual(result, False)
+
+    def test_isdisjoint_superset(self):
+        result = self.set.isdisjoint(set([2, 4, 6, 8]))
+        self.assertEqual(result, False)
+
+    def test_isdisjoint_overlap(self):
+        result = self.set.isdisjoint(set([3, 4, 5]))
+        self.assertEqual(result, False)
+
+    def test_isdisjoint_non_overlap(self):
+        result = self.set.isdisjoint(set([8]))
+        self.assertEqual(result, True)
+
     def test_sym_difference_subset(self):
         result = self.set ^ set((2, 4))
         self.assertEqual(result, set([6]))
@@ -1456,11 +1498,14 @@ class TestVariousIteratorArgs(unittest.TestCase):
     def test_inline_methods(self):
         s = set('november')
         for data in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5), 'december'):
-            for meth in (s.union, s.intersection, s.difference, s.symmetric_difference):
+            for meth in (s.union, s.intersection, s.difference, s.symmetric_difference, s.isdisjoint):
                 for g in (G, I, Ig, L, R):
                     expected = meth(data)
                     actual = meth(G(data))
-                    self.assertEqual(sorted(actual), sorted(expected))
+                    if isinstance(expected, bool):
+                        self.assertEqual(actual, expected)
+                    else:
+                        self.assertEqual(sorted(actual), sorted(expected))
                 self.assertRaises(TypeError, meth, X(s))
                 self.assertRaises(TypeError, meth, N(s))
                 self.assertRaises(ZeroDivisionError, meth, E(s))
index ff42c1b04109135b0811048150e122a1a7557e10..17b78331e8e5be8eab598acadc787a9578a5e633 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,8 @@ What's New in Python 2.6 alpha 1?
 Core and builtins
 -----------------
 
+- sets and frozensets now have an isdisjoint() method.
+
 - optimize the performance of builtin.sum().
 
 - Fix warnings found by the new version of the Coverity checker.
index 025a79b6dcba8e6a81ccc1628f3ba95cc491a810..140d9456ee72a9be10a07253532474f235590a3b 100644 (file)
@@ -1332,6 +1332,72 @@ set_iand(PySetObject *so, PyObject *other)
        return (PyObject *)so;
 }
 
+static PyObject *
+set_isdisjoint(PySetObject *so, PyObject *other)
+{
+       PyObject *key, *it, *tmp;
+
+       if ((PyObject *)so == other) {
+               if (PySet_GET_SIZE(so) == 0)
+                       Py_RETURN_TRUE;
+               else
+                       Py_RETURN_FALSE;
+       }
+
+       if (PyAnySet_CheckExact(other)) {               
+               Py_ssize_t pos = 0;
+               setentry *entry;
+
+               if (PySet_GET_SIZE(other) > PySet_GET_SIZE(so)) {
+                       tmp = (PyObject *)so;
+                       so = (PySetObject *)other;
+                       other = tmp;
+               }
+               while (set_next((PySetObject *)other, &pos, &entry)) {
+                       int rv = set_contains_entry(so, entry);
+                       if (rv == -1)
+                               return NULL;
+                       if (rv)
+                               Py_RETURN_FALSE;
+               }
+               Py_RETURN_TRUE;
+       }
+
+       it = PyObject_GetIter(other);
+       if (it == NULL)
+               return NULL;
+
+       while ((key = PyIter_Next(it)) != NULL) {
+               int rv;
+               setentry entry;
+               long hash = PyObject_Hash(key);
+
+               Py_DECREF(key);
+               if (hash == -1) {
+                       Py_DECREF(it);
+                       return NULL;
+               }
+               entry.hash = hash;
+               entry.key = key;
+               rv = set_contains_entry(so, &entry);
+               if (rv == -1) {
+                       Py_DECREF(it);
+                       return NULL;
+               }
+               if (rv) {
+                       Py_DECREF(it);
+                       Py_RETURN_FALSE;
+               }
+       }
+       Py_DECREF(it);
+       if (PyErr_Occurred())
+               return NULL;
+       Py_RETURN_TRUE;
+}
+
+PyDoc_STRVAR(isdisjoint_doc,
+"Return True if two sets have a null intersection.");
+
 static int
 set_difference_update_internal(PySetObject *so, PyObject *other)
 {
@@ -1861,6 +1927,8 @@ static PyMethodDef set_methods[] = {
         intersection_doc},
        {"intersection_update",(PyCFunction)set_intersection_update,    METH_O,
         intersection_update_doc},
+       {"isdisjoint",  (PyCFunction)set_isdisjoint,    METH_O,
+        isdisjoint_doc},
        {"issubset",    (PyCFunction)set_issubset,      METH_O,
         issubset_doc},
        {"issuperset",  (PyCFunction)set_issuperset,    METH_O,
@@ -1984,6 +2052,8 @@ static PyMethodDef frozenset_methods[] = {
         difference_doc},
        {"intersection",(PyCFunction)set_intersection,  METH_O,
         intersection_doc},
+       {"isdisjoint",  (PyCFunction)set_isdisjoint,    METH_O,
+        isdisjoint_doc},
        {"issubset",    (PyCFunction)set_issubset,      METH_O,
         issubset_doc},
        {"issuperset",  (PyCFunction)set_issuperset,    METH_O,