* 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).
This commit is contained in:
Raymond Hettinger 2003-11-24 02:57:33 +00:00
parent ceca5d2924
commit f5f41bf087
4 changed files with 337 additions and 161 deletions

View File

@ -1071,6 +1071,126 @@ Notes:
\versionadded{2.4} \versionadded{2.4}
\end{description} \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}} \subsection{Mapping Types \label{typesmapping}}
\obindex{mapping} \obindex{mapping}

View File

@ -20,7 +20,7 @@ typedef struct {
PyAPI_DATA(PyTypeObject) PySet_Type; PyAPI_DATA(PyTypeObject) PySet_Type;
PyAPI_DATA(PyTypeObject) PyFrozenSet_Type; PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
#define PyFrozenSet_CheckExact(ob) ((ob)->ob_type == &PyFrozenSet_Type)
#define PyAnySet_Check(ob) \ #define PyAnySet_Check(ob) \
((ob)->ob_type == &PySet_Type || (ob)->ob_type == &PyFrozenSet_Type || \ ((ob)->ob_type == &PySet_Type || (ob)->ob_type == &PyFrozenSet_Type || \
PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \ PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \

View File

@ -46,6 +46,11 @@ class TestJointOps(unittest.TestCase):
self.assertEqual(type(u), self.thetype) self.assertEqual(type(u), self.thetype)
self.assertRaises(PassThru, self.s.union, check_pass_thru()) self.assertRaises(PassThru, self.s.union, check_pass_thru())
self.assertRaises(TypeError, self.s.union, [[]]) 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): def test_or(self):
i = self.s.union(self.otherword) i = self.s.union(self.otherword)
@ -65,6 +70,11 @@ class TestJointOps(unittest.TestCase):
self.assertEqual(self.s, self.thetype(self.word)) self.assertEqual(self.s, self.thetype(self.word))
self.assertEqual(type(i), self.thetype) self.assertEqual(type(i), self.thetype)
self.assertRaises(PassThru, self.s.intersection, check_pass_thru()) 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): def test_and(self):
i = self.s.intersection(self.otherword) i = self.s.intersection(self.otherword)
@ -85,6 +95,11 @@ class TestJointOps(unittest.TestCase):
self.assertEqual(type(i), self.thetype) self.assertEqual(type(i), self.thetype)
self.assertRaises(PassThru, self.s.difference, check_pass_thru()) self.assertRaises(PassThru, self.s.difference, check_pass_thru())
self.assertRaises(TypeError, self.s.difference, [[]]) 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): def test_sub(self):
i = self.s.difference(self.otherword) i = self.s.difference(self.otherword)
@ -105,6 +120,11 @@ class TestJointOps(unittest.TestCase):
self.assertEqual(type(i), self.thetype) self.assertEqual(type(i), self.thetype)
self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru()) self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru())
self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) 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): def test_xor(self):
i = self.s.symmetric_difference(self.otherword) i = self.s.symmetric_difference(self.otherword)
@ -191,7 +211,8 @@ class TestSet(TestJointOps):
def test_clear(self): def test_clear(self):
self.s.clear() self.s.clear()
self.assertEqual(self.s, set([])) self.assertEqual(self.s, set())
self.assertEqual(len(self.s), 0)
def test_copy(self): def test_copy(self):
dup = self.s.copy() dup = self.s.copy()
@ -201,6 +222,9 @@ class TestSet(TestJointOps):
def test_add(self): def test_add(self):
self.s.add('Q') self.s.add('Q')
self.assert_('Q' in self.s) 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, []) self.assertRaises(TypeError, self.s.add, [])
def test_remove(self): def test_remove(self):
@ -231,13 +255,18 @@ class TestSet(TestJointOps):
self.assert_(elem not in self.s) self.assert_(elem not in self.s)
self.assertRaises(KeyError, self.s.pop) self.assertRaises(KeyError, self.s.pop)
def test_union_update(self): def test_update(self):
retval = self.s.union_update(self.otherword) retval = self.s.update(self.otherword)
self.assertEqual(retval, None) self.assertEqual(retval, None)
for c in (self.word + self.otherword): for c in (self.word + self.otherword):
self.assert_(c in self.s) self.assert_(c in self.s)
self.assertRaises(PassThru, self.s.union_update, check_pass_thru()) self.assertRaises(PassThru, self.s.update, check_pass_thru())
self.assertRaises(TypeError, self.s.union_update, [[]]) 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): def test_ior(self):
self.s |= set(self.otherword) self.s |= set(self.otherword)
@ -254,6 +283,11 @@ class TestSet(TestJointOps):
self.assert_(c not in self.s) self.assert_(c not in self.s)
self.assertRaises(PassThru, self.s.intersection_update, check_pass_thru()) self.assertRaises(PassThru, self.s.intersection_update, check_pass_thru())
self.assertRaises(TypeError, self.s.intersection_update, [[]]) 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): def test_iand(self):
self.s &= set(self.otherword) self.s &= set(self.otherword)
@ -273,6 +307,12 @@ class TestSet(TestJointOps):
self.assert_(c not in self.s) self.assert_(c not in self.s)
self.assertRaises(PassThru, self.s.difference_update, check_pass_thru()) self.assertRaises(PassThru, self.s.difference_update, check_pass_thru())
self.assertRaises(TypeError, self.s.difference_update, [[]]) 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): def test_isub(self):
self.s -= set(self.otherword) self.s -= set(self.otherword)
@ -292,6 +332,11 @@ class TestSet(TestJointOps):
self.assert_(c not in self.s) self.assert_(c not in self.s)
self.assertRaises(PassThru, self.s.symmetric_difference_update, check_pass_thru()) self.assertRaises(PassThru, self.s.symmetric_difference_update, check_pass_thru())
self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) 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): def test_ixor(self):
self.s ^= set(self.otherword) self.s ^= set(self.otherword)
@ -635,7 +680,7 @@ class TestUpdateOps(unittest.TestCase):
self.assertEqual(self.set, set([2, 4, 6, 8])) self.assertEqual(self.set, set([2, 4, 6, 8]))
def test_union_method_call(self): 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])) self.assertEqual(self.set, set([2, 3, 4, 5, 6]))
def test_intersection_subset(self): def test_intersection_subset(self):
@ -761,15 +806,15 @@ class TestMutate(unittest.TestCase):
self.failUnless(v in popped) self.failUnless(v in popped)
def test_update_empty_tuple(self): def test_update_empty_tuple(self):
self.set.union_update(()) self.set.update(())
self.assertEqual(self.set, set(self.values)) self.assertEqual(self.set, set(self.values))
def test_update_unit_tuple_overlap(self): def test_update_unit_tuple_overlap(self):
self.set.union_update(("a",)) self.set.update(("a",))
self.assertEqual(self.set, set(self.values)) self.assertEqual(self.set, set(self.values))
def test_update_unit_tuple_non_overlap(self): 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"])) 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)
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: try:
self.set |= self.other self.set |= self.other
except TypeError: except TypeError:
@ -880,11 +925,11 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
else: else:
self.fail("expected TypeError") self.fail("expected TypeError")
def test_union_update(self): def test_update(self):
if self.otherIsIterable: if self.otherIsIterable:
self.set.union_update(self.other) self.set.update(self.other)
else: else:
self.assertRaises(TypeError, self.set.union_update, self.other) self.assertRaises(TypeError, self.set.update, self.other)
def test_union(self): def test_union(self):
self.assertRaises(TypeError, lambda: self.set | self.other) self.assertRaises(TypeError, lambda: self.set | self.other)
@ -1215,7 +1260,7 @@ class TestVariousIteratorArgs(unittest.TestCase):
def test_inplace_methods(self): def test_inplace_methods(self):
for data in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5), 'december'): 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'): 'difference_update', 'symmetric_difference_update'):
for g in (G, I, Ig, S, L, R): for g in (G, I, Ig, S, L, R):
s = set('january') s = set('january')

View File

@ -12,48 +12,44 @@
static PyObject * static PyObject *
make_new_set(PyTypeObject *type, PyObject *iterable) make_new_set(PyTypeObject *type, PyObject *iterable)
{ {
PyObject *data; PyObject *data = NULL;
PyObject *it = NULL; PyObject *it = NULL;
PyObject *item; PyObject *item;
PySetObject *so; PySetObject *so = NULL;
/* Get iterator. */ /* Get iterator. */
if (iterable != NULL) { if (iterable != NULL) {
it = PyObject_GetIter(iterable); it = PyObject_GetIter(iterable);
if (it == NULL) if (it == NULL)
return NULL; goto done;
} }
data = PyDict_New(); data = PyDict_New();
if (data == NULL) { if (data == NULL)
Py_DECREF(it); goto done;
return NULL;
}
while (it != NULL && (item = PyIter_Next(it)) != NULL) { while (it != NULL && (item = PyIter_Next(it)) != NULL) {
if (PyDict_SetItem(data, item, Py_True) == -1) { if (PyDict_SetItem(data, item, Py_True) == -1) {
Py_DECREF(it);
Py_DECREF(data);
Py_DECREF(item); Py_DECREF(item);
return NULL; goto done;
} }
Py_DECREF(item); Py_DECREF(item);
} }
Py_XDECREF(it); if (PyErr_Occurred())
if (PyErr_Occurred()) { goto done;
Py_DECREF(data);
return NULL;
}
/* create PySetObject structure */ /* create PySetObject structure */
so = (PySetObject *)type->tp_alloc(type, 0); so = (PySetObject *)type->tp_alloc(type, 0);
if (so == NULL) { if (so == NULL)
Py_DECREF(data); goto done;
return NULL;
} Py_INCREF(data);
so->data = data; so->data = data;
so->hash = -1; so->hash = -1;
done:
Py_XDECREF(data);
Py_XDECREF(it);
return (PyObject *)so; 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)) if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable))
return NULL; return NULL;
if (iterable != NULL && iterable->ob_type == &PyFrozenSet_Type) { if (iterable != NULL && PyFrozenSet_CheckExact(iterable)) {
Py_INCREF(iterable); Py_INCREF(iterable);
return iterable; return iterable;
} }
@ -161,7 +157,7 @@ set_copy(PySetObject *so)
static PyObject * static PyObject *
frozenset_copy(PySetObject *so) frozenset_copy(PySetObject *so)
{ {
if (so->ob_type == &PyFrozenSet_Type) { if (PyFrozenSet_CheckExact(so)) {
Py_INCREF(so); Py_INCREF(so);
return (PyObject *)so; return (PyObject *)so;
} }
@ -170,52 +166,6 @@ frozenset_copy(PySetObject *so)
PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set."); 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 * static PyObject *
set_union_update(PySetObject *so, PyObject *other) set_union_update(PySetObject *so, PyObject *other)
{ {
@ -224,8 +174,7 @@ set_union_update(PySetObject *so, PyObject *other)
if (PyAnySet_Check(other)) { if (PyAnySet_Check(other)) {
if (PyDict_Merge(so->data, ((PySetObject *)other)->data, 0) == -1) if (PyDict_Merge(so->data, ((PySetObject *)other)->data, 0) == -1)
return NULL; return NULL;
Py_INCREF(so); Py_RETURN_NONE;
return (PyObject *)so;
} }
it = PyObject_GetIter(other); it = PyObject_GetIter(other);
@ -250,6 +199,29 @@ set_union_update(PySetObject *so, PyObject *other)
PyDoc_STRVAR(union_update_doc, PyDoc_STRVAR(union_update_doc,
"Update a set with the union of itself and another."); "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 * static PyObject *
set_or(PySetObject *so, PyObject *other) set_or(PySetObject *so, PyObject *other)
{ {
@ -286,6 +258,18 @@ set_intersection(PySetObject *so, PyObject *other)
result = (PySetObject *)make_new_set(so->ob_type, NULL); result = (PySetObject *)make_new_set(so->ob_type, NULL);
if (result == NULL) if (result == NULL)
return 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); it = PyObject_GetIter(other);
if (it == NULL) { if (it == NULL) {
@ -293,8 +277,6 @@ set_intersection(PySetObject *so, PyObject *other)
return NULL; return NULL;
} }
selfdata = so->data;
tgtdata = result->data;
while ((item = PyIter_Next(it)) != NULL) { while ((item = PyIter_Next(it)) != NULL) {
if (PySequence_Contains(selfdata, item)) { if (PySequence_Contains(selfdata, item)) {
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) { if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
@ -390,27 +372,39 @@ set_iand(PySetObject *so, PyObject *other)
static PyObject * static PyObject *
set_difference(PySetObject *so, PyObject *other) set_difference(PySetObject *so, PyObject *other)
{ {
PySetObject *result; PySetObject *result, *otherset=NULL;
PyObject *item, *tgtdata, *it; PyObject *item, *otherdata, *tgtdata, *it;
result = (PySetObject *)set_copy(so); result = (PySetObject *)make_new_set(so->ob_type, NULL);
if (result == NULL) if (result == NULL)
return NULL; return NULL;
tgtdata = result->data;
it = PyObject_GetIter(other); 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) { if (it == NULL) {
Py_XDECREF(otherset);
Py_DECREF(result); Py_DECREF(result);
return NULL; return NULL;
} }
tgtdata = result->data;
while ((item = PyIter_Next(it)) != NULL) { while ((item = PyIter_Next(it)) != NULL) {
if (PyDict_DelItem(tgtdata, item) == -1) { if (!PySequence_Contains(otherdata, item)) {
if (PyErr_ExceptionMatches(PyExc_KeyError)) if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
PyErr_Clear(); Py_XDECREF(otherset);
else {
Py_DECREF(it); Py_DECREF(it);
Py_DECREF(result);
Py_DECREF(item); Py_DECREF(item);
return NULL; return NULL;
} }
@ -418,6 +412,7 @@ set_difference(PySetObject *so, PyObject *other)
Py_DECREF(item); Py_DECREF(item);
} }
Py_DECREF(it); Py_DECREF(it);
Py_XDECREF(otherset);
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
Py_DECREF(result); Py_DECREF(result);
return NULL; return NULL;
@ -488,66 +483,6 @@ set_isub(PySetObject *so, PyObject *other)
return (PyObject *)so; 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 * static PyObject *
set_symmetric_difference_update(PySetObject *so, PyObject *other) 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, PyDoc_STRVAR(symmetric_difference_update_doc,
"Update a set with the symmetric difference of itself and another."); "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 * static PyObject *
set_xor(PySetObject *so, PyObject *other) set_xor(PySetObject *so, PyObject *other)
{ {
@ -1012,7 +1024,7 @@ static PyMethodDef set_methods[] = {
symmetric_difference_update_doc}, symmetric_difference_update_doc},
{"union", (PyCFunction)set_union, METH_O, {"union", (PyCFunction)set_union, METH_O,
union_doc}, union_doc},
{"union_update",(PyCFunction)set_union_update, METH_O, {"update", (PyCFunction)set_union_update, METH_O,
union_update_doc}, union_update_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
@ -1159,8 +1171,7 @@ PyTypeObject PyFrozenSet_Type = {
0, /* ob_size */ 0, /* ob_size */
"frozenset", /* tp_name */ "frozenset", /* tp_name */
sizeof(PySetObject), /* tp_basicsize */ sizeof(PySetObject), /* tp_basicsize */
0, /* tp_itemsize */ 0, /* tp_itemsize */ /* methods */
/* methods */
(destructor)set_dealloc, /* tp_dealloc */ (destructor)set_dealloc, /* tp_dealloc */
(printfunc)set_tp_print, /* tp_print */ (printfunc)set_tp_print, /* tp_print */
0, /* tp_getattr */ 0, /* tp_getattr */