]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport improvements to set.py so that the interface will remain
authorRaymond Hettinger <python@rcn.com>
Sun, 17 Aug 2003 22:08:58 +0000 (22:08 +0000)
committerRaymond Hettinger <python@rcn.com>
Sun, 17 Aug 2003 22:08:58 +0000 (22:08 +0000)
consistent across versions.

* Relaxed the argument restrictions for non-operator methods.  They now
  allow any iterable instead of requiring a set.  This makes the module
  a little easier to use and paves the way for an efficient C
  implementation which can take better advantage of iterable arguments
  while screening out immutables.

* Added a PendingDeprecationWarning for Set.update() because it now
  duplicates Set.union_update()

* Adapted the tests and docs to include the above changes.

* Added more test coverage including testing identities and checking
  to make sure non-restartable generators work as arguments.

Doc/lib/libsets.tex
Lib/sets.py
Lib/test/test_sets.py
Misc/NEWS

index 4d87a4fb2d61e2b2008134720cf6d296d82a270e..8551ab64b0c8628f8034274c8e4d00774f031261 100644 (file)
@@ -65,41 +65,40 @@ elements must be known when the constructor is called.
 Instances of \class{Set} and \class{ImmutableSet} both provide
 the following operations:
 
-\begin{tableii}{c|l}{code}{Operation}{Result}
-  \lineii{len(\var{s})}{cardinality of set \var{s}}
+\begin{tableiii}{c|c|l}{code}{Operation}{Equivalent}{Result}
+  \lineiii{len(\var{s})}{}{cardinality of set \var{s}}
 
   \hline
-  \lineii{\var{x} in \var{s}}
+  \lineiii{\var{x} in \var{s}}{}
          {test \var{x} for membership in \var{s}}
-  \lineii{\var{x} not in \var{s}}
+  \lineiii{\var{x} not in \var{s}}{}
          {test \var{x} for non-membership in \var{s}}
-  \lineii{\var{s}.issubset(\var{t})}
-         {test whether every element in \var{s} is in \var{t};
-         \code{\var{s} <= \var{t}} is equivalent}
-  \lineii{\var{s}.issuperset(\var{t})}
-         {test whether every element in \var{t} is in \var{s};
-         \code{\var{s} >= \var{t}} is equivalent}
+  \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
-  \lineii{\var{s} | \var{t}}
-         {new set with elements from both \var{s} and \var{t}}
-  \lineii{\var{s}.union(\var{t})}
+  \lineiii{\var{s}.union(\var{t})}{\var{s} | \var{t}}
          {new set with elements from both \var{s} and \var{t}}
-  \lineii{\var{s} \&\ \var{t}}
-         {new set with elements common to \var{s} and \var{t}}
-  \lineii{\var{s}.intersection(\var{t})}
+  \lineiii{\var{s}.intersection(\var{t})}{\var{s} \&\ \var{t}}
          {new set with elements common to \var{s} and \var{t}}
-  \lineii{\var{s} - \var{t}}
+  \lineiii{\var{s}.difference(\var{t})}{\var{s} - \var{t}}
          {new set with elements in \var{s} but not in \var{t}}
-  \lineii{\var{s}.difference(\var{t})}
-         {new set with elements in \var{s} but not in \var{t}}
-  \lineii{\var{s} \^\ \var{t}}
-         {new set with elements in either \var{s} or \var{t} but not both}
-  \lineii{\var{s}.symmetric_difference(\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}
-  \lineii{\var{s}.copy()}
+  \lineiii{\var{s}.copy()}{}
          {new set with a shallow copy of \var{s}}
-\end{tableii}
+\end{tableiii}
+
+Note, this non-operator versions of \method{union()},
+\method{intersection()}, \method{difference()}, and
+\method{symmetric_difference()} 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')}.
+\versionchanged[Formerly all arguments were required to be sets]{2.3.1}
 
 In addition, both \class{Set} and \class{ImmutableSet}
 support set to set comparisons.  Two sets are equal if and only if
@@ -112,8 +111,9 @@ 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{none} of the following are true:
-\code{\var{a}<\var{b}}, \code{\var{a}==\var{b}}, or \code{\var{a}>\var{b}}.
+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
@@ -122,47 +122,50 @@ of the \method{list.sort()} method is undefined for lists of sets.
 The following table lists operations available in \class{ImmutableSet}
 but not found in \class{Set}:
 
-\begin{tableii}{c|l|c}{code}{Operation}{Result}
+\begin{tableii}{c|l}{code}{Operation}{Result}
   \lineii{hash(\var{s})}{returns a hash value for \var{s}}
 \end{tableii}
 
 The following table lists operations available in \class{Set}
 but not found in \class{ImmutableSet}:
 
-\begin{tableii}{c|l}{code}{Operation}{Result}
-  \lineii{\var{s} |= \var{t}}
+\begin{tableiii}{c|c|l}{code}{Operation}{Equivalent}{Result}
+  \lineiii{\var{s}.union_update(\var{t})}
+         {\var{s} |= \var{t}}
          {return set \var{s} with elements added from \var{t}}
-  \lineii{\var{s}.union_update(\var{t})}
-         {return set \var{s} with elements added from \var{t}}
-  \lineii{\var{s} \&= \var{t}}
-         {return set \var{s} keeping only elements also found in \var{t}}
-  \lineii{\var{s}.intersection_update(\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}}
-  \lineii{\var{s} -= \var{t}}
+  \lineiii{\var{s}.difference_update(\var{t})}
+         {\var{s} -= \var{t}}
          {return set \var{s} after removing elements found in \var{t}}
-  \lineii{\var{s}.difference_update(\var{t})}
-         {return set \var{s} after removing elements found in \var{t}}
-  \lineii{\var{s} \textasciicircum= \var{t}}
-         {return set \var{s} with elements from \var{s} or \var{t}
-          but not both}
-  \lineii{\var{s}.symmetric_difference_update(\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
-  \lineii{\var{s}.add(\var{x})}
+  \lineiii{\var{s}.add(\var{x})}{}
          {add element \var{x} to set \var{s}}
-  \lineii{\var{s}.remove(\var{x})}
-         {remove \var{x} from set \var{s}}
-  \lineii{\var{s}.discard(\var{x})}
+  \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}
-  \lineii{\var{s}.pop()}
-         {remove and return an arbitrary element from \var{s}}
-  \lineii{\var{s}.update(\var{t})}
-         {add elements from \var{t} to set \var{s}}
-  \lineii{\var{s}.clear()}
+  \lineiii{\var{s}.pop()}{}
+         {remove and return an arbitrary element from \var{s}; raises
+         KeyError if empty}
+  \lineiii{\var{s}.clear()}{}
          {remove all elements from set \var{s}}
-\end{tableii}
+\end{tableiii}
+
+\versionchanged[Earlier versions had an \method{update()} method; use
+                \method{union_update()} instead]{2.3.1}
+
+Note, this non-operator versions of \method{union_update()},
+\method{intersection_update()}, \method{difference_update()}, and
+\method{symmetric_difference_update()} will accept any iterable as
+an argument.
+\versionchanged[Formerly all arguments were required to be sets]{2.3.1}
 
 
 \subsection{Example \label{set-example}}
@@ -171,16 +174,16 @@ but not found in \class{ImmutableSet}:
 >>> from sets import Set
 >>> engineers = Set(['John', 'Jane', 'Jack', 'Janice'])
 >>> programmers = Set(['Jack', 'Sam', 'Susan', 'Janice'])
->>> management = Set(['Jane', 'Jack', 'Susan', 'Zack'])
->>> employees = engineers | programmers | management           # union
->>> engineering_management = engineers & programmers           # intersection
->>> fulltime_management = management - engineers - programmers # difference
->>> engineers.add('Marvin')                                    # add element
+>>> managers = Set(['Jane', 'Jack', 'Susan', 'Zack'])
+>>> employees = engineers | programmers | managers           # union
+>>> engineering_management = engineers & managers            # intersection
+>>> fulltime_management = managers - engineers - programmers # difference
+>>> engineers.add('Marvin')                                  # add element
 >>> print engineers
 Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
 >>> employees.issuperset(engineers)           # superset test
 False
->>> employees.update(engineers)               # update from another set
+>>> employees.union_update(engineers)         # update from another set
 >>> employees.issuperset(engineers)
 True
 >>> for group in [engineers, programmers, management, employees]:
index 32eb0aa6f6bca3ebd41954f1b8d2dc4d37d55bd5..0da8f9fd3d05cd8981e60cf10c8b885c85b92aad 100644 (file)
@@ -196,17 +196,16 @@ class BaseSet(object):
         """
         if not isinstance(other, BaseSet):
             return NotImplemented
-        result = self.__class__()
-        result._data = self._data.copy()
-        result._data.update(other._data)
-        return result
+        return self.union(other)
 
     def union(self, other):
         """Return the union of two sets as a new set.
 
         (I.e. all elements that are in either set.)
         """
-        return self | other
+        result = self.__class__(self)
+        result._update(other)
+        return result
 
     def __and__(self, other):
         """Return the intersection of two sets as a new set.
@@ -215,19 +214,21 @@ class BaseSet(object):
         """
         if not isinstance(other, BaseSet):
             return NotImplemented
-        if len(self) <= len(other):
-            little, big = self, other
-        else:
-            little, big = other, self
-        common = ifilter(big._data.has_key, little)
-        return self.__class__(common)
+        return self.intersection(other)
 
     def intersection(self, other):
         """Return the intersection of two sets as a new set.
 
         (I.e. all elements that are in both sets.)
         """
-        return self & other
+        if not isinstance(other, BaseSet):
+            other = Set(other)
+        if len(self) <= len(other):
+            little, big = self, other
+        else:
+            little, big = other, self
+        common = ifilter(big._data.has_key, little)
+        return self.__class__(common)
 
     def __xor__(self, other):
         """Return the symmetric difference of two sets as a new set.
@@ -236,24 +237,27 @@ class BaseSet(object):
         """
         if not isinstance(other, BaseSet):
             return NotImplemented
+        return self.symmetric_difference(other)
+
+    def symmetric_difference(self, other):
+        """Return the symmetric difference of two sets as a new set.
+
+        (I.e. all elements that are in exactly one of the sets.)
+        """
         result = self.__class__()
         data = result._data
         value = True
         selfdata = self._data
-        otherdata = other._data
+        try:
+            otherdata = other._data
+        except AttributeError:
+            otherdata = Set(other)._data
         for elt in ifilterfalse(otherdata.has_key, selfdata):
             data[elt] = value
         for elt in ifilterfalse(selfdata.has_key, otherdata):
             data[elt] = value
         return result
 
-    def symmetric_difference(self, other):
-        """Return the symmetric difference of two sets as a new set.
-
-        (I.e. all elements that are in exactly one of the sets.)
-        """
-        return self ^ other
-
     def  __sub__(self, other):
         """Return the difference of two sets as a new Set.
 
@@ -261,19 +265,23 @@ class BaseSet(object):
         """
         if not isinstance(other, BaseSet):
             return NotImplemented
-        result = self.__class__()
-        data = result._data
-        value = True
-        for elt in ifilterfalse(other._data.has_key, self):
-            data[elt] = value
-        return result
+        return self.difference(other)
 
     def difference(self, other):
         """Return the difference of two sets as a new Set.
 
         (I.e. all elements that are in this set and not in the other.)
         """
-        return self - other
+        result = self.__class__()
+        data = result._data
+        try:
+            otherdata = other._data
+        except AttributeError:
+            otherdata = Set(other)._data
+        value = True
+        for elt in ifilterfalse(otherdata.has_key, self):
+            data[elt] = value
+        return result
 
     # Membership test
 
@@ -441,7 +449,7 @@ class Set(BaseSet):
 
     def union_update(self, other):
         """Update a set with the union of itself and another."""
-        self |= other
+        self._update(other)
 
     def __iand__(self, other):
         """Update a set with the intersection of itself and another."""
@@ -451,40 +459,51 @@ class Set(BaseSet):
 
     def intersection_update(self, other):
         """Update a set with the intersection of itself and another."""
-        self &= other
+        if isinstance(other, BaseSet):
+            self &= other
+        else:
+            self._data = (self.intersection(other))._data
 
     def __ixor__(self, other):
         """Update a set with the symmetric difference of itself and another."""
         self._binary_sanity_check(other)
+        self.symmetric_difference_update(other)
+        return self
+
+    def symmetric_difference_update(self, other):
+        """Update a set with the symmetric difference of itself and another."""
         data = self._data
         value = True
+        if not isinstance(other, BaseSet):
+            other = Set(other)
         for elt in other:
             if elt in data:
                 del data[elt]
             else:
                 data[elt] = value
-        return self
-
-    def symmetric_difference_update(self, other):
-        """Update a set with the symmetric difference of itself and another."""
-        self ^= other
 
     def __isub__(self, other):
         """Remove all elements of another set from this set."""
         self._binary_sanity_check(other)
-        data = self._data
-        for elt in ifilter(data.has_key, other):
-            del data[elt]
+        self.difference_update(other)
         return self
 
     def difference_update(self, other):
         """Remove all elements of another set from this set."""
-        self -= other
+        data = self._data
+        if not isinstance(other, BaseSet):
+            other = Set(other)
+        for elt in ifilter(data.has_key, other):
+            del data[elt]
 
     # Python dict-like mass mutations: update, clear
 
     def update(self, iterable):
         """Add all values from an iterable (such as a list or file)."""
+        import warnings
+        warnings.warn("The update() method is going to be deprecated; "
+                      "Use union_update() instead",
+                      PendingDeprecationWarning)
         self._update(iterable)
 
     def clear(self):
index 5b9c4e0a70a39d007d3db584a65cac8d4e87dff0..f0a1925d4e01463dfb9f7051e9e88fe97d74b291 100644 (file)
@@ -152,7 +152,7 @@ class TestExceptionPropagation(unittest.TestCase):
         self.assertRaises(TypeError, Set, baditer())
 
     def test_instancesWithoutException(self):
-        """All of these iterables should load without exception."""
+        # All of these iterables should load without exception.
         Set([1,2,3])
         Set((1,2,3))
         Set({'one':1, 'two':2, 'three':3})
@@ -392,15 +392,15 @@ class TestMutate(unittest.TestCase):
             self.failUnless(v in popped)
 
     def test_update_empty_tuple(self):
-        self.set.update(())
+        self.set.union_update(())
         self.assertEqual(self.set, Set(self.values))
 
     def test_update_unit_tuple_overlap(self):
-        self.set.update(("a",))
+        self.set.union_update(("a",))
         self.assertEqual(self.set, Set(self.values))
 
     def test_update_unit_tuple_non_overlap(self):
-        self.set.update(("a", "z"))
+        self.set.union_update(("a", "z"))
         self.assertEqual(self.set, Set(self.values + ["z"]))
 
 #==============================================================================
@@ -503,7 +503,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(self):
+    def test_union_update_operator(self):
         try:
             self.set |= self.other
         except TypeError:
@@ -511,11 +511,21 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
         else:
             self.fail("expected TypeError")
 
+    def test_union_update(self):
+        if self.otherIsIterable:
+            self.set.union_update(self.other)
+        else:
+            self.assertRaises(TypeError, self.set.union_update, self.other)
+
     def test_union(self):
         self.assertRaises(TypeError, lambda: self.set | self.other)
         self.assertRaises(TypeError, lambda: self.other | self.set)
+        if self.otherIsIterable:
+            self.set.union(self.other)
+        else:
+            self.assertRaises(TypeError, self.set.union, self.other)
 
-    def test_intersection_update(self):
+    def test_intersection_update_operator(self):
         try:
             self.set &= self.other
         except TypeError:
@@ -523,11 +533,23 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
         else:
             self.fail("expected TypeError")
 
+    def test_intersection_update(self):
+        if self.otherIsIterable:
+            self.set.intersection_update(self.other)
+        else:
+            self.assertRaises(TypeError,
+                              self.set.intersection_update,
+                              self.other)
+
     def test_intersection(self):
         self.assertRaises(TypeError, lambda: self.set & self.other)
         self.assertRaises(TypeError, lambda: self.other & self.set)
+        if self.otherIsIterable:
+            self.set.intersection(self.other)
+        else:
+            self.assertRaises(TypeError, self.set.intersection, self.other)
 
-    def test_sym_difference_update(self):
+    def test_sym_difference_update_operator(self):
         try:
             self.set ^= self.other
         except TypeError:
@@ -535,11 +557,23 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
         else:
             self.fail("expected TypeError")
 
+    def test_sym_difference_update(self):
+        if self.otherIsIterable:
+            self.set.symmetric_difference_update(self.other)
+        else:
+            self.assertRaises(TypeError,
+                              self.set.symmetric_difference_update,
+                              self.other)
+
     def test_sym_difference(self):
         self.assertRaises(TypeError, lambda: self.set ^ self.other)
         self.assertRaises(TypeError, lambda: self.other ^ self.set)
+        if self.otherIsIterable:
+            self.set.symmetric_difference(self.other)
+        else:
+            self.assertRaises(TypeError, self.set.symmetric_difference, self.other)
 
-    def test_difference_update(self):
+    def test_difference_update_operator(self):
         try:
             self.set -= self.other
         except TypeError:
@@ -547,16 +581,28 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
         else:
             self.fail("expected TypeError")
 
+    def test_difference_update(self):
+        if self.otherIsIterable:
+            self.set.difference_update(self.other)
+        else:
+            self.assertRaises(TypeError,
+                              self.set.difference_update,
+                              self.other)
+
     def test_difference(self):
         self.assertRaises(TypeError, lambda: self.set - self.other)
         self.assertRaises(TypeError, lambda: self.other - self.set)
-
+        if self.otherIsIterable:
+            self.set.difference(self.other)
+        else:
+            self.assertRaises(TypeError, self.set.difference, self.other)
 #------------------------------------------------------------------------------
 
 class TestOnlySetsNumeric(TestOnlySetsInBinaryOps):
     def setUp(self):
         self.set   = Set((1, 2, 3))
         self.other = 19
+        self.otherIsIterable = False
 
 #------------------------------------------------------------------------------
 
@@ -564,6 +610,7 @@ class TestOnlySetsDict(TestOnlySetsInBinaryOps):
     def setUp(self):
         self.set   = Set((1, 2, 3))
         self.other = {1:2, 3:4}
+        self.otherIsIterable = True
 
 #------------------------------------------------------------------------------
 
@@ -571,6 +618,34 @@ class TestOnlySetsOperator(TestOnlySetsInBinaryOps):
     def setUp(self):
         self.set   = Set((1, 2, 3))
         self.other = operator.add
+        self.otherIsIterable = False
+
+#------------------------------------------------------------------------------
+
+class TestOnlySetsTuple(TestOnlySetsInBinaryOps):
+    def setUp(self):
+        self.set   = Set((1, 2, 3))
+        self.other = (2, 4, 6)
+        self.otherIsIterable = True
+
+#------------------------------------------------------------------------------
+
+class TestOnlySetsString(TestOnlySetsInBinaryOps):
+    def setUp(self):
+        self.set   = Set((1, 2, 3))
+        self.other = 'abc'
+        self.otherIsIterable = True
+
+#------------------------------------------------------------------------------
+
+class TestOnlySetsGenerator(TestOnlySetsInBinaryOps):
+    def setUp(self):
+        def gen():
+            for i in xrange(0, 10, 2):
+                yield i
+        self.set   = Set((1, 2, 3))
+        self.other = gen()
+        self.otherIsIterable = True
 
 #==============================================================================
 
@@ -625,6 +700,49 @@ class TestCopyingNested(TestCopying):
 
 #==============================================================================
 
+class TestIdentities(unittest.TestCase):
+    def setUp(self):
+        self.a = Set('abracadabra')
+        self.b = Set('alacazam')
+
+    def test_binopsVsSubsets(self):
+        a, b = self.a, self.b
+        self.assert_(a - b < a)
+        self.assert_(b - a < b)
+        self.assert_(a & b < a)
+        self.assert_(a & b < b)
+        self.assert_(a | b > a)
+        self.assert_(a | b > b)
+        self.assert_(a ^ b < a | b)
+
+    def test_commutativity(self):
+        a, b = self.a, self.b
+        self.assertEqual(a&b, b&a)
+        self.assertEqual(a|b, b|a)
+        self.assertEqual(a^b, b^a)
+        if a != b:
+            self.assertNotEqual(a-b, b-a)
+
+    def test_summations(self):
+        # check that sums of parts equal the whole
+        a, b = self.a, self.b
+        self.assertEqual((a-b)|(a&b)|(b-a), a|b)
+        self.assertEqual((a&b)|(a^b), a|b)
+        self.assertEqual(a|(b-a), a|b)
+        self.assertEqual((a-b)|b, a|b)
+        self.assertEqual((a-b)|(a&b), a)
+        self.assertEqual((b-a)|(a&b), b)
+        self.assertEqual((a-b)|(b-a), a^b)
+
+    def test_exclusion(self):
+        # check that inverse operations show non-overlap
+        a, b, zero = self.a, self.b, Set()
+        self.assertEqual((a-b)&b, zero)
+        self.assertEqual((b-a)&a, zero)
+        self.assertEqual((a&b)&(a^b), zero)
+
+#==============================================================================
+
 libreftest = """
 Example from the Library Reference:  Doc/lib/libsets.tex
 
@@ -634,19 +752,19 @@ Example from the Library Reference:  Doc/lib/libsets.tex
 ...         return Base._repr(self, sorted=True)
 >>> engineers = Set(['John', 'Jane', 'Jack', 'Janice'])
 >>> programmers = Set(['Jack', 'Sam', 'Susan', 'Janice'])
->>> management = Set(['Jane', 'Jack', 'Susan', 'Zack'])
->>> employees = engineers | programmers | management           # union
->>> engineering_management = engineers & programmers           # intersection
->>> fulltime_management = management - engineers - programmers # difference
+>>> managers = Set(['Jane', 'Jack', 'Susan', 'Zack'])
+>>> employees = engineers | programmers | managers           # union
+>>> engineering_management = engineers & managers            # intersection
+>>> fulltime_management = managers - engineers - programmers # difference
 >>> engineers.add('Marvin')
 >>> print engineers
 Set(['Jack', 'Jane', 'Janice', 'John', 'Marvin'])
 >>> employees.issuperset(engineers)           # superset test
 False
->>> employees.update(engineers)               # update from another set
+>>> employees.union_update(engineers)         # update from another set
 >>> employees.issuperset(engineers)
 True
->>> for group in [engineers, programmers, management, employees]:
+>>> for group in [engineers, programmers, managers, employees]:
 ...     group.discard('Susan')                # unconditionally remove element
 ...     print group
 ...
@@ -680,11 +798,15 @@ def test_main(verbose=None):
         TestOnlySetsNumeric,
         TestOnlySetsDict,
         TestOnlySetsOperator,
+        TestOnlySetsTuple,
+        TestOnlySetsString,
+        TestOnlySetsGenerator,
         TestCopyingEmpty,
         TestCopyingSingleton,
         TestCopyingTriple,
         TestCopyingTuple,
-        TestCopyingNested
+        TestCopyingNested,
+        TestIdentities,
     )
     test_support.run_doctest(test_sets, verbose)
 
index a16679442f54d15fc84f81ed6e170844634a1887..4c75f014727cd1188eb30cd4cfc4aab1ac0aa0ac 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,7 +27,10 @@ Extension modules
 Library
 -------
 
-- sets.py can now run under Py2.2
+- sets.py now runs under Py2.2.  In addition, the argument restrictions
+  for most set methods (but not the operators) have been relaxed to
+  allow any iterable.  Also the Set.update() has been deprecated because
+  it duplicates Set.union_update().
 
 - Bug #778964:  random.seed() now uses fractional seconds so that
   rapid successive, seeding calls will produce different sequences.