]> granicus.if.org Git - python/commitdiff
* Checkin remaining documentation
authorRaymond Hettinger <python@rcn.com>
Mon, 24 Nov 2003 02:57:33 +0000 (02:57 +0000)
committerRaymond Hettinger <python@rcn.com>
Mon, 24 Nov 2003 02:57:33 +0000 (02:57 +0000)
* Add more tests
* Refactor and neaten the code a bit.
* Rename union_update() to update().
* Improve the algorithms (making them a closer to sets.py).

Doc/lib/libstdtypes.tex
Include/setobject.h
Lib/test/test_set.py
Objects/setobject.c

index 917ca4bccc7d0edc11fb79e85f4d362340e6a23d..4eb45958597ee2e87619ad37312d1d63e70910df 100644 (file)
@@ -1071,6 +1071,126 @@ Notes:
   \versionadded{2.4}
 \end{description}
 
+\subsection{Set Types \label{types-set}}
+\obindex{set}
+
+A \dfn{set} object is an unordered collection of immutable values.
+Common uses include membership testing, removing duplicates from a sequence,
+and computing mathematical operations such as intersection, union, difference,
+and symmetric difference.
+\versionadded{2.4}     
+
+Like other collections, sets support \code{\var{x} in \var{set}},
+\code{len(\var{set})}, and \code{for \var{x} in \var{set}}.  Being an
+unordered collection, sets do not record element position or order of
+insertion.  Accordingly, sets do not support indexing, slicing, or
+other sequence-like behavior.     
+
+There are currently two builtin set types, \class{set} and \class{frozenset}.
+The \class{set} type is mutable --- the contents can be changed using methods
+like \method{add()} and \method{remove()}.  Since it is mutable, it has no
+hash value and cannot be used as either a dictionary key or as an element of
+another set.  The \class{frozenset} type is immutable and hashable --- its
+contents cannot be altered after is created; however, it can be used as
+a dictionary key or as an element of another set.
+
+Instances of \class{set} and \class{frozenset} provide the following operations:
+
+\begin{tableiii}{c|c|l}{code}{Operation}{Equivalent}{Result}
+  \lineiii{len(\var{s})}{}{cardinality of set \var{s}}
+
+  \hline
+  \lineiii{\var{x} in \var{s}}{}
+         {test \var{x} for membership in \var{s}}
+  \lineiii{\var{x} not in \var{s}}{}
+         {test \var{x} for non-membership in \var{s}}
+  \lineiii{\var{s}.issubset(\var{t})}{\code{\var{s} <= \var{t}}}
+         {test whether every element in \var{s} is in \var{t}}
+  \lineiii{\var{s}.issuperset(\var{t})}{\code{\var{s} >= \var{t}}}
+         {test whether every element in \var{t} is in \var{s}}
+
+  \hline
+  \lineiii{\var{s}.union(\var{t})}{\var{s} | \var{t}}
+         {new set with elements from both \var{s} and \var{t}}
+  \lineiii{\var{s}.intersection(\var{t})}{\var{s} \&\ \var{t}}
+         {new set with elements common to \var{s} and \var{t}}
+  \lineiii{\var{s}.difference(\var{t})}{\var{s} - \var{t}}
+         {new set with elements in \var{s} but not in \var{t}}
+  \lineiii{\var{s}.symmetric_difference(\var{t})}{\var{s} \^\ \var{t}}
+         {new set with elements in either \var{s} or \var{t} but not both}
+  \lineiii{\var{s}.copy()}{}
+         {new set with a shallow copy of \var{s}}
+\end{tableiii}
+
+Note, the non-operator versions of \method{union()}, \method{intersection()},
+\method{difference()}, and \method{symmetric_difference()},
+\method{issubset()}, and \method{issuperset()} methods will accept any
+iterable as an argument.  In contrast, their operator based counterparts
+require their arguments to be sets.  This precludes error-prone constructions
+like \code{set('abc') \&\ 'cbs'} in favor of the more readable
+\code{set('abc').intersection('cbs')}.
+
+Both \class{set} and \class{frozenset} support set to set comparisons.
+Two sets are equal if and only if every element of each set is contained in
+the other (each is a subset of the other).
+A set is less than another set if and only if the first set is a proper
+subset of the second set (is a subset, but is not equal).
+A set is greater than another set if and only if the first set is a proper
+superset of the second set (is a superset, but is not equal).
+
+The subset and equality comparisons do not generalize to a complete
+ordering function.  For example, any two disjoint sets are not equal and
+are not subsets of each other, so \emph{all} of the following return
+\code{False}:  \code{\var{a}<\var{b}}, \code{\var{a}==\var{b}}, or
+\code{\var{a}>\var{b}}.
+Accordingly, sets do not implement the \method{__cmp__} method.
+
+Since sets only define partial ordering (subset relationships), the output
+of the \method{list.sort()} method is undefined for lists of sets.
+
+For convenience in implementing sets of sets, the \method{__contains__()},
+\method{remove()}, and \method{discard()} methods automatically match
+instances of the \class{set} class their \class{frozenset} counterparts
+inside a set.  For example, \code{set('abc') in set([frozenset('abc')])}
+returns \code{True}.
+
+The following table lists operations available for \class{set}
+that do not apply to immutable instances of \class{frozenset}:
+
+\begin{tableiii}{c|c|l}{code}{Operation}{Equivalent}{Result}
+  \lineiii{\var{s}.update(\var{t})}
+         {\var{s} |= \var{t}}
+         {return set \var{s} with elements added from \var{t}}
+  \lineiii{\var{s}.intersection_update(\var{t})}
+         {\var{s} \&= \var{t}}
+         {return set \var{s} keeping only elements also found in \var{t}}
+  \lineiii{\var{s}.difference_update(\var{t})}
+         {\var{s} -= \var{t}}
+         {return set \var{s} after removing elements found in \var{t}}
+  \lineiii{\var{s}.symmetric_difference_update(\var{t})}
+         {\var{s} \textasciicircum= \var{t}}
+         {return set \var{s} with elements from \var{s} or \var{t}
+          but not both}
+
+  \hline
+  \lineiii{\var{s}.add(\var{x})}{}
+         {add element \var{x} to set \var{s}}
+  \lineiii{\var{s}.remove(\var{x})}{}
+         {remove \var{x} from set \var{s}; raises KeyError if not present}
+  \lineiii{\var{s}.discard(\var{x})}{}
+         {removes \var{x} from set \var{s} if present}
+  \lineiii{\var{s}.pop()}{}
+         {remove and return an arbitrary element from \var{s}; raises
+         \exception{KeyError} if empty}
+  \lineiii{\var{s}.clear()}{}
+         {remove all elements from set \var{s}}
+\end{tableiii}
+
+Note, the non-operator versions of the \method{update()},
+\method{intersection_update()}, \method{difference_update()}, and
+\method{symmetric_difference_update()} methods will accept any iterable
+as an argument.
+
 
 \subsection{Mapping Types \label{typesmapping}}
 \obindex{mapping}
index 6289f9c6d48974ad96393349d9c985eedb1551bd..267e3b0956552d33126936d5a3b190fe83448039 100644 (file)
@@ -20,7 +20,7 @@ typedef struct {
 PyAPI_DATA(PyTypeObject) PySet_Type;
 PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
 
-
+#define PyFrozenSet_CheckExact(ob) ((ob)->ob_type == &PyFrozenSet_Type)
 #define PyAnySet_Check(ob) \
        ((ob)->ob_type == &PySet_Type || (ob)->ob_type == &PyFrozenSet_Type || \
          PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \
index 939e49097e415f078d30c4cbdb1aedc5fbeaa4c2..f3cdc173147a666d106883ee4b74a1bbfa8c0571 100644 (file)
@@ -46,6 +46,11 @@ class TestJointOps(unittest.TestCase):
         self.assertEqual(type(u), self.thetype)
         self.assertRaises(PassThru, self.s.union, check_pass_thru())
         self.assertRaises(TypeError, self.s.union, [[]])
+        for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+            self.assertEqual(self.thetype('abcba').union(C('cdc')), set('abcd'))
+            self.assertEqual(self.thetype('abcba').union(C('efgfe')), set('abcefg'))
+            self.assertEqual(self.thetype('abcba').union(C('ccb')), set('abc'))
+            self.assertEqual(self.thetype('abcba').union(C('ef')), set('abcef'))
 
     def test_or(self):
         i = self.s.union(self.otherword)
@@ -65,6 +70,11 @@ class TestJointOps(unittest.TestCase):
         self.assertEqual(self.s, self.thetype(self.word))
         self.assertEqual(type(i), self.thetype)
         self.assertRaises(PassThru, self.s.intersection, check_pass_thru())
+        for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+            self.assertEqual(self.thetype('abcba').intersection(C('cdc')), set('cc'))
+            self.assertEqual(self.thetype('abcba').intersection(C('efgfe')), set(''))
+            self.assertEqual(self.thetype('abcba').intersection(C('ccb')), set('bc'))
+            self.assertEqual(self.thetype('abcba').intersection(C('ef')), set(''))
 
     def test_and(self):
         i = self.s.intersection(self.otherword)
@@ -85,6 +95,11 @@ class TestJointOps(unittest.TestCase):
         self.assertEqual(type(i), self.thetype)
         self.assertRaises(PassThru, self.s.difference, check_pass_thru())
         self.assertRaises(TypeError, self.s.difference, [[]])
+        for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+            self.assertEqual(self.thetype('abcba').difference(C('cdc')), set('ab'))
+            self.assertEqual(self.thetype('abcba').difference(C('efgfe')), set('abc'))
+            self.assertEqual(self.thetype('abcba').difference(C('ccb')), set('a'))
+            self.assertEqual(self.thetype('abcba').difference(C('ef')), set('abc'))
 
     def test_sub(self):
         i = self.s.difference(self.otherword)
@@ -105,6 +120,11 @@ class TestJointOps(unittest.TestCase):
         self.assertEqual(type(i), self.thetype)
         self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru())
         self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
+        for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+            self.assertEqual(self.thetype('abcba').symmetric_difference(C('cdc')), set('abd'))
+            self.assertEqual(self.thetype('abcba').symmetric_difference(C('efgfe')), set('abcefg'))
+            self.assertEqual(self.thetype('abcba').symmetric_difference(C('ccb')), set('a'))
+            self.assertEqual(self.thetype('abcba').symmetric_difference(C('ef')), set('abcef'))
 
     def test_xor(self):
         i = self.s.symmetric_difference(self.otherword)
@@ -191,7 +211,8 @@ class TestSet(TestJointOps):
 
     def test_clear(self):
         self.s.clear()
-        self.assertEqual(self.s, set([]))
+        self.assertEqual(self.s, set())
+        self.assertEqual(len(self.s), 0)
 
     def test_copy(self):
         dup = self.s.copy()
@@ -201,6 +222,9 @@ class TestSet(TestJointOps):
     def test_add(self):
         self.s.add('Q')
         self.assert_('Q' in self.s)
+        dup = self.s.copy()
+        self.s.add('Q')
+        self.assertEqual(self.s, dup)
         self.assertRaises(TypeError, self.s.add, [])
 
     def test_remove(self):
@@ -231,13 +255,18 @@ class TestSet(TestJointOps):
             self.assert_(elem not in self.s)
         self.assertRaises(KeyError, self.s.pop)
 
-    def test_union_update(self):
-        retval = self.s.union_update(self.otherword)
+    def test_update(self):
+        retval = self.s.update(self.otherword)
         self.assertEqual(retval, None)
         for c in (self.word + self.otherword):
             self.assert_(c in self.s)
-        self.assertRaises(PassThru, self.s.union_update, check_pass_thru())
-        self.assertRaises(TypeError, self.s.union_update, [[]])
+        self.assertRaises(PassThru, self.s.update, check_pass_thru())
+        self.assertRaises(TypeError, self.s.update, [[]])
+        for p, q in (('cdc', 'abcd'), ('efgfe', 'abcefg'), ('ccb', 'abc'), ('ef', 'abcef')):
+            for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+                s = self.thetype('abcba')
+                self.assertEqual(s.update(C(p)), None)
+                self.assertEqual(s, set(q))
 
     def test_ior(self):
         self.s |= set(self.otherword)
@@ -254,6 +283,11 @@ class TestSet(TestJointOps):
                 self.assert_(c not in self.s)
         self.assertRaises(PassThru, self.s.intersection_update, check_pass_thru())
         self.assertRaises(TypeError, self.s.intersection_update, [[]])
+        for p, q in (('cdc', 'c'), ('efgfe', ''), ('ccb', 'bc'), ('ef', '')):
+            for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+                s = self.thetype('abcba')
+                self.assertEqual(s.intersection_update(C(p)), None)
+                self.assertEqual(s, set(q))
 
     def test_iand(self):
         self.s &= set(self.otherword)
@@ -273,6 +307,12 @@ class TestSet(TestJointOps):
                 self.assert_(c not in self.s)
         self.assertRaises(PassThru, self.s.difference_update, check_pass_thru())
         self.assertRaises(TypeError, self.s.difference_update, [[]])
+        self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
+        for p, q in (('cdc', 'ab'), ('efgfe', 'abc'), ('ccb', 'a'), ('ef', 'abc')):
+            for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+                s = self.thetype('abcba')
+                self.assertEqual(s.difference_update(C(p)), None)
+                self.assertEqual(s, set(q))
 
     def test_isub(self):
         self.s -= set(self.otherword)
@@ -292,6 +332,11 @@ class TestSet(TestJointOps):
                 self.assert_(c not in self.s)
         self.assertRaises(PassThru, self.s.symmetric_difference_update, check_pass_thru())
         self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
+        for p, q in (('cdc', 'abd'), ('efgfe', 'abcefg'), ('ccb', 'a'), ('ef', 'abcef')):
+            for C in set, frozenset, dict.fromkeys, str, unicode, list, tuple:
+                s = self.thetype('abcba')
+                self.assertEqual(s.symmetric_difference_update(C(p)), None)
+                self.assertEqual(s, set(q))
 
     def test_ixor(self):
         self.s ^= set(self.otherword)
@@ -635,7 +680,7 @@ class TestUpdateOps(unittest.TestCase):
         self.assertEqual(self.set, set([2, 4, 6, 8]))
 
     def test_union_method_call(self):
-        self.set.union_update(set([3, 4, 5]))
+        self.set.update(set([3, 4, 5]))
         self.assertEqual(self.set, set([2, 3, 4, 5, 6]))
 
     def test_intersection_subset(self):
@@ -761,15 +806,15 @@ class TestMutate(unittest.TestCase):
             self.failUnless(v in popped)
 
     def test_update_empty_tuple(self):
-        self.set.union_update(())
+        self.set.update(())
         self.assertEqual(self.set, set(self.values))
 
     def test_update_unit_tuple_overlap(self):
-        self.set.union_update(("a",))
+        self.set.update(("a",))
         self.assertEqual(self.set, set(self.values))
 
     def test_update_unit_tuple_non_overlap(self):
-        self.set.union_update(("a", "z"))
+        self.set.update(("a", "z"))
         self.assertEqual(self.set, set(self.values + ["z"]))
 
 #==============================================================================
@@ -872,7 +917,7 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
         self.assertRaises(TypeError, lambda: self.other > self.set)
         self.assertRaises(TypeError, lambda: self.other >= self.set)
 
-    def test_union_update_operator(self):
+    def test_update_operator(self):
         try:
             self.set |= self.other
         except TypeError:
@@ -880,11 +925,11 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
         else:
             self.fail("expected TypeError")
 
-    def test_union_update(self):
+    def test_update(self):
         if self.otherIsIterable:
-            self.set.union_update(self.other)
+            self.set.update(self.other)
         else:
-            self.assertRaises(TypeError, self.set.union_update, self.other)
+            self.assertRaises(TypeError, self.set.update, self.other)
 
     def test_union(self):
         self.assertRaises(TypeError, lambda: self.set | self.other)
@@ -1215,7 +1260,7 @@ class TestVariousIteratorArgs(unittest.TestCase):
 
     def test_inplace_methods(self):
         for data in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5), 'december'):
-            for methname in ('union_update', 'intersection_update',
+            for methname in ('update', 'intersection_update',
                              'difference_update', 'symmetric_difference_update'):
                 for g in (G, I, Ig, S, L, R):
                     s = set('january')
index fab07fbe76aceb49751f14b512c8cd364ef0cfc8..01f05883e232bba7ff48eb7af9ea2590a68ae9de 100644 (file)
 static PyObject *
 make_new_set(PyTypeObject *type, PyObject *iterable)
 {
-       PyObject *data;
+       PyObject *data = NULL;
        PyObject *it = NULL;
        PyObject *item;
-       PySetObject *so;
+       PySetObject *so = NULL;
 
        /* Get iterator. */
        if (iterable != NULL) {
                it = PyObject_GetIter(iterable);
                if (it == NULL)
-                       return NULL;
+                       goto done;
        }
 
        data = PyDict_New();
-       if (data == NULL) {
-               Py_DECREF(it);
-               return NULL;
-       }
+       if (data == NULL) 
+               goto done;
 
        while (it != NULL && (item = PyIter_Next(it)) != NULL) {
                 if (PyDict_SetItem(data, item, Py_True) == -1) {
-                       Py_DECREF(it);
-                       Py_DECREF(data);
                        Py_DECREF(item);
-                       return NULL;
+                       goto done;
                 } 
                Py_DECREF(item);
        }
-       Py_XDECREF(it);
-       if (PyErr_Occurred()) {
-               Py_DECREF(data);
-               return NULL;
-       }
+       if (PyErr_Occurred())
+               goto done;
 
        /* create PySetObject structure */
        so = (PySetObject *)type->tp_alloc(type, 0);
-       if (so == NULL) {
-               Py_DECREF(data);
-               return NULL;
-       }
+       if (so == NULL) 
+               goto done;
+
+       Py_INCREF(data);
        so->data = data;
        so->hash = -1;
 
+done:
+       Py_XDECREF(data);
+       Py_XDECREF(it);
        return (PyObject *)so;
 }
 
@@ -64,7 +60,7 @@ frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
        if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable))
                return NULL;
-       if (iterable != NULL && iterable->ob_type == &PyFrozenSet_Type) {
+       if (iterable != NULL && PyFrozenSet_CheckExact(iterable)) {
                Py_INCREF(iterable);
                return iterable;
        }
@@ -161,7 +157,7 @@ set_copy(PySetObject *so)
 static PyObject *
 frozenset_copy(PySetObject *so)
 {
-       if (so->ob_type == &PyFrozenSet_Type) {
+       if (PyFrozenSet_CheckExact(so)) {
                Py_INCREF(so);
                return (PyObject *)so;
        }
@@ -170,52 +166,6 @@ frozenset_copy(PySetObject *so)
 
 PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set.");
 
-static PyObject *
-set_union(PySetObject *so, PyObject *other)
-{
-       PySetObject *result;
-       PyObject *item, *data, *it;
-
-       result = (PySetObject *)set_copy(so);
-       if (result == NULL)
-               return NULL;
-
-       if (PyAnySet_Check(other)) {
-               if (PyDict_Merge(result->data, ((PySetObject *)other)->data, 0) == -1) {
-                       Py_DECREF(result);
-                       return NULL;
-               }
-               return (PyObject *)result;
-       }
-
-       it = PyObject_GetIter(other);
-       if (it == NULL) {
-               Py_DECREF(result);
-               return NULL;
-       }
-       data = result->data;
-       while ((item = PyIter_Next(it)) != NULL) {
-                if (PyDict_SetItem(data, item, Py_True) == -1) {
-                       Py_DECREF(it);
-                       Py_DECREF(result);
-                       Py_DECREF(item);
-                       return NULL;
-                } 
-               Py_DECREF(item);
-       }
-       Py_DECREF(it);
-       if (PyErr_Occurred()) {
-               Py_DECREF(result);
-               return NULL;
-       }
-       return (PyObject *)result;
-}
-
-PyDoc_STRVAR(union_doc,
- "Return the union of two sets as a new set.\n\
-\n\
-(i.e. all elements that are in either set.)");
-
 static PyObject *
 set_union_update(PySetObject *so, PyObject *other)
 {
@@ -224,8 +174,7 @@ set_union_update(PySetObject *so, PyObject *other)
        if (PyAnySet_Check(other)) {
                if (PyDict_Merge(so->data, ((PySetObject *)other)->data, 0) == -1) 
                        return NULL;
-               Py_INCREF(so);
-               return (PyObject *)so;
+               Py_RETURN_NONE;
        }
 
        it = PyObject_GetIter(other);
@@ -250,6 +199,29 @@ set_union_update(PySetObject *so, PyObject *other)
 PyDoc_STRVAR(union_update_doc, 
 "Update a set with the union of itself and another.");
 
+static PyObject *
+set_union(PySetObject *so, PyObject *other)
+{
+       PySetObject *result;
+       PyObject *rv;
+
+       result = (PySetObject *)set_copy(so);
+       if (result == NULL)
+               return NULL;
+       rv = set_union_update(result, other);
+       if (rv == NULL) {
+               Py_DECREF(result);
+               return NULL;
+       }
+       Py_DECREF(rv);
+       return (PyObject *)result;
+}
+
+PyDoc_STRVAR(union_doc,
+ "Return the union of two sets as a new set.\n\
+\n\
+(i.e. all elements that are in either set.)");
+
 static PyObject *
 set_or(PySetObject *so, PyObject *other)
 {
@@ -286,15 +258,25 @@ set_intersection(PySetObject *so, PyObject *other)
        result = (PySetObject *)make_new_set(so->ob_type, NULL);
        if (result == NULL)
                return NULL;
-       
+       tgtdata = result->data;
+       selfdata = so->data;
+
+       if (PyAnySet_Check(other) && 
+               PyDict_Size(((PySetObject *)other)->data) > PyDict_Size(selfdata)) {
+               selfdata = ((PySetObject *)other)->data;
+               other = (PyObject *)so;
+       } else if (PyDict_Check(other) &&
+               PyDict_Size(other) > PyDict_Size(selfdata)) {
+               selfdata = other;
+               other = so->data;
+       }
+
        it = PyObject_GetIter(other);
        if (it == NULL) {
                Py_DECREF(result);
                return NULL;
        }
 
-       selfdata = so->data;
-       tgtdata = result->data;
        while ((item = PyIter_Next(it)) != NULL) {
                if (PySequence_Contains(selfdata, item)) {
                        if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
@@ -390,27 +372,39 @@ set_iand(PySetObject *so, PyObject *other)
 static PyObject *
 set_difference(PySetObject *so, PyObject *other)
 {
-       PySetObject *result;
-       PyObject *item, *tgtdata, *it;
+       PySetObject *result, *otherset=NULL;
+       PyObject *item, *otherdata, *tgtdata, *it;
 
-       result = (PySetObject *)set_copy(so);
+       result = (PySetObject *)make_new_set(so->ob_type, NULL);
        if (result == NULL)
                return NULL;
-       
-       it = PyObject_GetIter(other);
+       tgtdata = result->data;
+
+       if (PyDict_Check(other))
+               otherdata = other;
+       else if (PyAnySet_Check(other))
+               otherdata = ((PySetObject *)other)->data;
+       else {
+               otherset = (PySetObject *)make_new_set(so->ob_type, other);
+               if (otherset == NULL) {
+                       Py_DECREF(result);
+                       return NULL;
+               }
+               otherdata = otherset->data;
+       }       
+
+       it = PyObject_GetIter(so->data);
        if (it == NULL) {
+               Py_XDECREF(otherset);
                Py_DECREF(result);
                return NULL;
        }
 
-       tgtdata = result->data;
        while ((item = PyIter_Next(it)) != NULL) {
-               if (PyDict_DelItem(tgtdata, item) == -1) {
-                       if (PyErr_ExceptionMatches(PyExc_KeyError))
-                               PyErr_Clear();
-                       else {
+               if (!PySequence_Contains(otherdata, item)) {
+                       if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
+                               Py_XDECREF(otherset);
                                Py_DECREF(it);
-                               Py_DECREF(result);
                                Py_DECREF(item);
                                return NULL;
                        }
@@ -418,6 +412,7 @@ set_difference(PySetObject *so, PyObject *other)
                Py_DECREF(item);
        }
        Py_DECREF(it);
+       Py_XDECREF(otherset);
        if (PyErr_Occurred()) {
                Py_DECREF(result);
                return NULL;
@@ -489,99 +484,112 @@ set_isub(PySetObject *so, PyObject *other)
 }
 
 static PyObject *
-set_symmetric_difference(PySetObject *so, PyObject *other)
+set_symmetric_difference_update(PySetObject *so, PyObject *other)
 {
-       PySetObject *result, *otherset=NULL;
-       PyObject *item, *selfdata, *otherdata, *tgtdata, *it;
+       PyObject *item, *selfdata, *it, *otherdata;
+       PySetObject *otherset = NULL;
 
        selfdata = so->data;
 
-       result = (PySetObject *)set_copy(so);
-       if (result == NULL)
-               return NULL;
-       tgtdata = result->data;
-
        if (PyDict_Check(other))
                otherdata = other;
        else if (PyAnySet_Check(other))
                otherdata = ((PySetObject *)other)->data;
        else {
                otherset = (PySetObject *)make_new_set(so->ob_type, other);
-               if (otherset == NULL) {
-                       Py_DECREF(result);
+               if (otherset == NULL)
                        return NULL;
-               }
                otherdata = otherset->data;
-       }       
+       }
 
        it = PyObject_GetIter(otherdata);
-       if (it == NULL) {
-               Py_XDECREF(otherset);
-               Py_DECREF(result);
+       if (it == NULL)
                return NULL;
-       }
 
        while ((item = PyIter_Next(it)) != NULL) {
-               if (PyDict_DelItem(tgtdata, item) == -1) {
-                       PyErr_Clear();
-                       if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
+               if (PySequence_Contains(selfdata, item)) {
+                       if (PyDict_DelItem(selfdata, item) == -1) {
+                               Py_XDECREF(otherset);
                                Py_DECREF(it);
+                               Py_DECREF(item);
+                               return NULL;
+                       }
+               } else {
+                       if (PyDict_SetItem(selfdata, item, Py_True) == -1) {
                                Py_XDECREF(otherset);
-                               Py_DECREF(result);
+                               Py_DECREF(it);
                                Py_DECREF(item);
                                return NULL;
-                       } 
+                       }
                }
                Py_DECREF(item);
        }
-       Py_DECREF(it);
        Py_XDECREF(otherset);
-       if (PyErr_Occurred()) {
-               Py_DECREF(result);
+       Py_DECREF(it);
+       if (PyErr_Occurred())
                return NULL;
-       }
-       return (PyObject *)result;
+       Py_RETURN_NONE;
 }
 
-PyDoc_STRVAR(symmetric_difference_doc,
-"Return the symmetric difference of two sets as a new set.\n\
-\n\
-(i.e. all elements that are in exactly one of the sets.)");
+PyDoc_STRVAR(symmetric_difference_update_doc,
+"Update a set with the symmetric difference of itself and another.");
 
 static PyObject *
-set_symmetric_difference_update(PySetObject *so, PyObject *other)
+set_symmetric_difference(PySetObject *so, PyObject *other)
 {
-       PyObject *item, *selfdata, *it, *otherdata;
-       PySetObject *otherset = NULL;
-
-       selfdata = so->data;
+       PySetObject *result;
+       PyObject *item, *selfdata, *otherdata, *tgtdata, *it, *rv, *otherset;
 
        if (PyDict_Check(other))
                otherdata = other;
        else if (PyAnySet_Check(other))
                otherdata = ((PySetObject *)other)->data;
        else {
-               otherset = (PySetObject *)make_new_set(so->ob_type, other);
+               otherset = make_new_set(so->ob_type, other);
                if (otherset == NULL)
                        return NULL;
-               otherdata = otherset->data;
-       }
+               rv = set_symmetric_difference_update((PySetObject *)otherset, (PyObject *)so);
+               if (rv == NULL)
+                       return NULL;
+               Py_DECREF(rv);
+               return otherset;
+       }       
 
-       it = PyObject_GetIter(otherdata);
-       if (it == NULL)
+       result = (PySetObject *)make_new_set(so->ob_type, NULL);
+       if (result == NULL)
                return NULL;
+       tgtdata = result->data;
+       selfdata = so->data;
 
+       it = PyObject_GetIter(otherdata);
+       if (it == NULL) {
+               Py_DECREF(result);
+               return NULL;
+       }
        while ((item = PyIter_Next(it)) != NULL) {
-               if (PySequence_Contains(selfdata, item)) {
-                       if (PyDict_DelItem(selfdata, item) == -1) {
-                               Py_XDECREF(otherset);
+               if (!PySequence_Contains(selfdata, item)) {
+                       if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
                                Py_DECREF(it);
                                Py_DECREF(item);
                                return NULL;
                        }
-               } else {
-                       if (PyDict_SetItem(selfdata, item, Py_True) == -1) {
-                               Py_XDECREF(otherset);
+               }
+               Py_DECREF(item);
+       }
+       Py_DECREF(it);
+       if (PyErr_Occurred()) {
+               Py_DECREF(result);
+               return NULL;
+       }
+
+       it = PyObject_GetIter(selfdata);
+       if (it == NULL) {
+               Py_DECREF(result);
+               return NULL;
+       }
+       while ((item = PyIter_Next(it)) != NULL) {
+               if (!PySequence_Contains(otherdata, item)) {
+                       if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
                                Py_DECREF(it);
                                Py_DECREF(item);
                                return NULL;
@@ -589,15 +597,19 @@ set_symmetric_difference_update(PySetObject *so, PyObject *other)
                }
                Py_DECREF(item);
        }
-       Py_XDECREF(otherset);
        Py_DECREF(it);
-       if (PyErr_Occurred())
+       if (PyErr_Occurred()) {
+               Py_DECREF(result);
                return NULL;
-       Py_RETURN_NONE;
+       }
+
+       return (PyObject *)result;
 }
 
-PyDoc_STRVAR(symmetric_difference_update_doc,
-"Update a set with the symmetric difference of itself and another.");
+PyDoc_STRVAR(symmetric_difference_doc,
+"Return the symmetric difference of two sets as a new set.\n\
+\n\
+(i.e. all elements that are in exactly one of the sets.)");
 
 static PyObject *
 set_xor(PySetObject *so, PyObject *other)
@@ -1012,7 +1024,7 @@ static PyMethodDef set_methods[] = {
         symmetric_difference_update_doc},
        {"union",       (PyCFunction)set_union,         METH_O,
         union_doc},
-       {"union_update",(PyCFunction)set_union_update,  METH_O,
+       {"update",      (PyCFunction)set_union_update,  METH_O,
         union_update_doc},
        {NULL,          NULL}   /* sentinel */
 };
@@ -1159,8 +1171,7 @@ PyTypeObject PyFrozenSet_Type = {
        0,                              /* ob_size */
        "frozenset",                    /* tp_name */
        sizeof(PySetObject),            /* tp_basicsize */
-       0,                              /* tp_itemsize */
-       /* methods */
+       0,                              /* tp_itemsize */       /* methods */
        (destructor)set_dealloc,        /* tp_dealloc */
        (printfunc)set_tp_print,        /* tp_print */
        0,                              /* tp_getattr */