From f5f41bf087c28500df9399335e7452da3a06caae Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 24 Nov 2003 02:57:33 +0000 Subject: [PATCH] * Checkin remaining documentation * 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 | 120 ++++++++++++++++ Include/setobject.h | 2 +- Lib/test/test_set.py | 73 ++++++++-- Objects/setobject.c | 303 +++++++++++++++++++++------------------- 4 files changed, 337 insertions(+), 161 deletions(-) diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index 917ca4bccc7..4eb45958597 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -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} diff --git a/Include/setobject.h b/Include/setobject.h index 6289f9c6d48..267e3b09565 100644 --- a/Include/setobject.h +++ b/Include/setobject.h @@ -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) || \ diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 939e49097e4..f3cdc173147 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -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') diff --git a/Objects/setobject.c b/Objects/setobject.c index fab07fbe76a..01f05883e23 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -12,48 +12,44 @@ 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; @@ -488,66 +483,6 @@ set_isub(PySetObject *so, PyObject *other) return (PyObject *)so; } -static PyObject * -set_symmetric_difference(PySetObject *so, PyObject *other) -{ - PySetObject *result, *otherset=NULL; - PyObject *item, *selfdata, *otherdata, *tgtdata, *it; - - 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); - return NULL; - } - otherdata = otherset->data; - } - - it = PyObject_GetIter(otherdata); - if (it == NULL) { - Py_XDECREF(otherset); - Py_DECREF(result); - return NULL; - } - - while ((item = PyIter_Next(it)) != NULL) { - if (PyDict_DelItem(tgtdata, item) == -1) { - PyErr_Clear(); - if (PyDict_SetItem(tgtdata, item, Py_True) == -1) { - Py_DECREF(it); - Py_XDECREF(otherset); - Py_DECREF(result); - Py_DECREF(item); - return NULL; - } - } - Py_DECREF(item); - } - Py_DECREF(it); - Py_XDECREF(otherset); - if (PyErr_Occurred()) { - Py_DECREF(result); - return NULL; - } - return (PyObject *)result; -} - -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_symmetric_difference_update(PySetObject *so, PyObject *other) { @@ -599,6 +534,83 @@ set_symmetric_difference_update(PySetObject *so, PyObject *other) PyDoc_STRVAR(symmetric_difference_update_doc, "Update a set with the symmetric difference of itself and another."); +static PyObject * +set_symmetric_difference(PySetObject *so, PyObject *other) +{ + 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 = make_new_set(so->ob_type, other); + if (otherset == NULL) + return NULL; + rv = set_symmetric_difference_update((PySetObject *)otherset, (PyObject *)so); + if (rv == NULL) + return NULL; + Py_DECREF(rv); + return otherset; + } + + 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_SetItem(tgtdata, item, Py_True) == -1) { + Py_DECREF(it); + Py_DECREF(item); + return NULL; + } + } + 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; + } + } + Py_DECREF(item); + } + Py_DECREF(it); + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } + + return (PyObject *)result; +} + +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 */