mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Improvements to set.py:
* 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. * Deprecated 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. Will backport to Py2.3.1 so that the interface remains consistent across versions. The deprecation of update() will be changed to a FutureWarning.
This commit is contained in:
parent
236ffba400
commit
6a1801271a
4 changed files with 216 additions and 56 deletions
|
@ -91,6 +91,15 @@ the following operations:
|
||||||
{new set with a shallow copy of \var{s}}
|
{new set with a shallow copy of \var{s}}
|
||||||
\end{tableiii}
|
\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}
|
In addition, both \class{Set} and \class{ImmutableSet}
|
||||||
support set to set comparisons. Two sets are equal if and only if
|
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
|
every element of each set is contained in the other (each is a subset
|
||||||
|
@ -145,12 +154,19 @@ but not found in \class{ImmutableSet}:
|
||||||
\lineiii{\var{s}.pop()}{}
|
\lineiii{\var{s}.pop()}{}
|
||||||
{remove and return an arbitrary element from \var{s}; raises
|
{remove and return an arbitrary element from \var{s}; raises
|
||||||
KeyError if empty}
|
KeyError if empty}
|
||||||
\lineiii{\var{s}.update(\var{t})}{}
|
|
||||||
{add elements from \var{t} to set \var{s}}
|
|
||||||
\lineiii{\var{s}.clear()}{}
|
\lineiii{\var{s}.clear()}{}
|
||||||
{remove all elements from set \var{s}}
|
{remove all elements from set \var{s}}
|
||||||
\end{tableiii}
|
\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}}
|
\subsection{Example \label{set-example}}
|
||||||
|
|
||||||
|
@ -167,7 +183,7 @@ but not found in \class{ImmutableSet}:
|
||||||
Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
|
Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
|
||||||
>>> employees.issuperset(engineers) # superset test
|
>>> employees.issuperset(engineers) # superset test
|
||||||
False
|
False
|
||||||
>>> employees.update(engineers) # update from another set
|
>>> employees.union_update(engineers) # update from another set
|
||||||
>>> employees.issuperset(engineers)
|
>>> employees.issuperset(engineers)
|
||||||
True
|
True
|
||||||
>>> for group in [engineers, programmers, management, employees]:
|
>>> for group in [engineers, programmers, management, employees]:
|
||||||
|
|
101
Lib/sets.py
101
Lib/sets.py
|
@ -196,17 +196,16 @@ class BaseSet(object):
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, BaseSet):
|
if not isinstance(other, BaseSet):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
result = self.__class__()
|
return self.union(other)
|
||||||
result._data = self._data.copy()
|
|
||||||
result._data.update(other._data)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def union(self, other):
|
def union(self, other):
|
||||||
"""Return the union of two sets as a new set.
|
"""Return the union of two sets as a new set.
|
||||||
|
|
||||||
(I.e. all elements that are in either 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):
|
def __and__(self, other):
|
||||||
"""Return the intersection of two sets as a new set.
|
"""Return the intersection of two sets as a new set.
|
||||||
|
@ -215,19 +214,21 @@ class BaseSet(object):
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, BaseSet):
|
if not isinstance(other, BaseSet):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
if len(self) <= len(other):
|
return self.intersection(other)
|
||||||
little, big = self, other
|
|
||||||
else:
|
|
||||||
little, big = other, self
|
|
||||||
common = ifilter(big._data.has_key, little)
|
|
||||||
return self.__class__(common)
|
|
||||||
|
|
||||||
def intersection(self, other):
|
def intersection(self, other):
|
||||||
"""Return the intersection of two sets as a new set.
|
"""Return the intersection of two sets as a new set.
|
||||||
|
|
||||||
(I.e. all elements that are in both sets.)
|
(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):
|
def __xor__(self, other):
|
||||||
"""Return the symmetric difference of two sets as a new set.
|
"""Return the symmetric difference of two sets as a new set.
|
||||||
|
@ -236,23 +237,26 @@ class BaseSet(object):
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, BaseSet):
|
if not isinstance(other, BaseSet):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
result = self.__class__()
|
return self.symmetric_difference(other)
|
||||||
data = result._data
|
|
||||||
value = True
|
|
||||||
selfdata = self._data
|
|
||||||
otherdata = 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):
|
def symmetric_difference(self, other):
|
||||||
"""Return the symmetric difference of two sets as a new set.
|
"""Return the symmetric difference of two sets as a new set.
|
||||||
|
|
||||||
(I.e. all elements that are in exactly one of the sets.)
|
(I.e. all elements that are in exactly one of the sets.)
|
||||||
"""
|
"""
|
||||||
return self ^ other
|
result = self.__class__()
|
||||||
|
data = result._data
|
||||||
|
value = True
|
||||||
|
selfdata = self._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 __sub__(self, other):
|
def __sub__(self, other):
|
||||||
"""Return the difference of two sets as a new Set.
|
"""Return the difference of two sets as a new Set.
|
||||||
|
@ -261,19 +265,23 @@ class BaseSet(object):
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, BaseSet):
|
if not isinstance(other, BaseSet):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
result = self.__class__()
|
return self.difference(other)
|
||||||
data = result._data
|
|
||||||
value = True
|
|
||||||
for elt in ifilterfalse(other._data.has_key, self):
|
|
||||||
data[elt] = value
|
|
||||||
return result
|
|
||||||
|
|
||||||
def difference(self, other):
|
def difference(self, other):
|
||||||
"""Return the difference of two sets as a new Set.
|
"""Return the difference of two sets as a new Set.
|
||||||
|
|
||||||
(I.e. all elements that are in this set and not in the other.)
|
(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
|
# Membership test
|
||||||
|
|
||||||
|
@ -441,7 +449,7 @@ class Set(BaseSet):
|
||||||
|
|
||||||
def union_update(self, other):
|
def union_update(self, other):
|
||||||
"""Update a set with the union of itself and another."""
|
"""Update a set with the union of itself and another."""
|
||||||
self |= other
|
self._update(other)
|
||||||
|
|
||||||
def __iand__(self, other):
|
def __iand__(self, other):
|
||||||
"""Update a set with the intersection of itself and another."""
|
"""Update a set with the intersection of itself and another."""
|
||||||
|
@ -451,40 +459,51 @@ class Set(BaseSet):
|
||||||
|
|
||||||
def intersection_update(self, other):
|
def intersection_update(self, other):
|
||||||
"""Update a set with the intersection of itself and another."""
|
"""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):
|
def __ixor__(self, other):
|
||||||
"""Update a set with the symmetric difference of itself and another."""
|
"""Update a set with the symmetric difference of itself and another."""
|
||||||
self._binary_sanity_check(other)
|
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
|
data = self._data
|
||||||
value = True
|
value = True
|
||||||
|
if not isinstance(other, BaseSet):
|
||||||
|
other = Set(other)
|
||||||
for elt in other:
|
for elt in other:
|
||||||
if elt in data:
|
if elt in data:
|
||||||
del data[elt]
|
del data[elt]
|
||||||
else:
|
else:
|
||||||
data[elt] = value
|
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):
|
def __isub__(self, other):
|
||||||
"""Remove all elements of another set from this set."""
|
"""Remove all elements of another set from this set."""
|
||||||
self._binary_sanity_check(other)
|
self._binary_sanity_check(other)
|
||||||
data = self._data
|
self.difference_update(other)
|
||||||
for elt in ifilter(data.has_key, other):
|
|
||||||
del data[elt]
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def difference_update(self, other):
|
def difference_update(self, other):
|
||||||
"""Remove all elements of another set from this set."""
|
"""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
|
# Python dict-like mass mutations: update, clear
|
||||||
|
|
||||||
def update(self, iterable):
|
def update(self, iterable):
|
||||||
"""Add all values from an iterable (such as a list or file)."""
|
"""Add all values from an iterable (such as a list or file)."""
|
||||||
|
import warnings
|
||||||
|
warnings.warn("The update() method deprecated; "
|
||||||
|
"Use union_update() instead",
|
||||||
|
DeprecationWarning)
|
||||||
self._update(iterable)
|
self._update(iterable)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
|
|
@ -152,7 +152,7 @@ class TestExceptionPropagation(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, Set, baditer())
|
self.assertRaises(TypeError, Set, baditer())
|
||||||
|
|
||||||
def test_instancesWithoutException(self):
|
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((1,2,3))
|
Set((1,2,3))
|
||||||
Set({'one':1, 'two':2, 'three':3})
|
Set({'one':1, 'two':2, 'three':3})
|
||||||
|
@ -392,15 +392,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.update(())
|
self.set.union_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.update(("a",))
|
self.set.union_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.update(("a", "z"))
|
self.set.union_update(("a", "z"))
|
||||||
self.assertEqual(self.set, Set(self.values + ["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)
|
||||||
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:
|
try:
|
||||||
self.set |= self.other
|
self.set |= self.other
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -511,11 +511,21 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("expected TypeError")
|
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):
|
def test_union(self):
|
||||||
self.assertRaises(TypeError, lambda: self.set | self.other)
|
self.assertRaises(TypeError, lambda: self.set | self.other)
|
||||||
self.assertRaises(TypeError, lambda: self.other | self.set)
|
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:
|
try:
|
||||||
self.set &= self.other
|
self.set &= self.other
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -523,11 +533,23 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("expected TypeError")
|
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):
|
def test_intersection(self):
|
||||||
self.assertRaises(TypeError, lambda: self.set & self.other)
|
self.assertRaises(TypeError, lambda: self.set & self.other)
|
||||||
self.assertRaises(TypeError, lambda: self.other & self.set)
|
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:
|
try:
|
||||||
self.set ^= self.other
|
self.set ^= self.other
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -535,11 +557,23 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("expected TypeError")
|
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):
|
def test_sym_difference(self):
|
||||||
self.assertRaises(TypeError, lambda: self.set ^ self.other)
|
self.assertRaises(TypeError, lambda: self.set ^ self.other)
|
||||||
self.assertRaises(TypeError, lambda: self.other ^ self.set)
|
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:
|
try:
|
||||||
self.set -= self.other
|
self.set -= self.other
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -547,16 +581,28 @@ class TestOnlySetsInBinaryOps(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("expected TypeError")
|
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):
|
def test_difference(self):
|
||||||
self.assertRaises(TypeError, lambda: self.set - self.other)
|
self.assertRaises(TypeError, lambda: self.set - self.other)
|
||||||
self.assertRaises(TypeError, lambda: self.other - self.set)
|
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):
|
class TestOnlySetsNumeric(TestOnlySetsInBinaryOps):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.set = Set((1, 2, 3))
|
self.set = Set((1, 2, 3))
|
||||||
self.other = 19
|
self.other = 19
|
||||||
|
self.otherIsIterable = False
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -564,6 +610,7 @@ class TestOnlySetsDict(TestOnlySetsInBinaryOps):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.set = Set((1, 2, 3))
|
self.set = Set((1, 2, 3))
|
||||||
self.other = {1:2, 3:4}
|
self.other = {1:2, 3:4}
|
||||||
|
self.otherIsIterable = True
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -571,6 +618,34 @@ class TestOnlySetsOperator(TestOnlySetsInBinaryOps):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.set = Set((1, 2, 3))
|
self.set = Set((1, 2, 3))
|
||||||
self.other = operator.add
|
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 = """
|
libreftest = """
|
||||||
Example from the Library Reference: Doc/lib/libsets.tex
|
Example from the Library Reference: Doc/lib/libsets.tex
|
||||||
|
|
||||||
|
@ -643,7 +761,7 @@ Example from the Library Reference: Doc/lib/libsets.tex
|
||||||
Set(['Jack', 'Jane', 'Janice', 'John', 'Marvin'])
|
Set(['Jack', 'Jane', 'Janice', 'John', 'Marvin'])
|
||||||
>>> employees.issuperset(engineers) # superset test
|
>>> employees.issuperset(engineers) # superset test
|
||||||
False
|
False
|
||||||
>>> employees.update(engineers) # update from another set
|
>>> employees.union_update(engineers) # update from another set
|
||||||
>>> employees.issuperset(engineers)
|
>>> employees.issuperset(engineers)
|
||||||
True
|
True
|
||||||
>>> for group in [engineers, programmers, managers, employees]:
|
>>> for group in [engineers, programmers, managers, employees]:
|
||||||
|
@ -680,11 +798,15 @@ def test_main(verbose=None):
|
||||||
TestOnlySetsNumeric,
|
TestOnlySetsNumeric,
|
||||||
TestOnlySetsDict,
|
TestOnlySetsDict,
|
||||||
TestOnlySetsOperator,
|
TestOnlySetsOperator,
|
||||||
|
TestOnlySetsTuple,
|
||||||
|
TestOnlySetsString,
|
||||||
|
TestOnlySetsGenerator,
|
||||||
TestCopyingEmpty,
|
TestCopyingEmpty,
|
||||||
TestCopyingSingleton,
|
TestCopyingSingleton,
|
||||||
TestCopyingTriple,
|
TestCopyingTriple,
|
||||||
TestCopyingTuple,
|
TestCopyingTuple,
|
||||||
TestCopyingNested
|
TestCopyingNested,
|
||||||
|
TestIdentities,
|
||||||
)
|
)
|
||||||
test_support.run_doctest(test_sets, verbose)
|
test_support.run_doctest(test_sets, verbose)
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,10 @@ Extension modules
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
- sets.py now runs 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().
|
||||||
|
|
||||||
- random.seed() with no arguments or None uses time.time() as a default
|
- random.seed() with no arguments or None uses time.time() as a default
|
||||||
seed. Modified to match Py2.2 behavior and use fractional seconds so
|
seed. Modified to match Py2.2 behavior and use fractional seconds so
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue