python-2.5.2/win32/Lib/sets.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """Classes to represent arbitrary sets (including sets of sets).
       
     2 
       
     3 This module implements sets using dictionaries whose values are
       
     4 ignored.  The usual operations (union, intersection, deletion, etc.)
       
     5 are provided as both methods and operators.
       
     6 
       
     7 Important: sets are not sequences!  While they support 'x in s',
       
     8 'len(s)', and 'for x in s', none of those operations are unique for
       
     9 sequences; for example, mappings support all three as well.  The
       
    10 characteristic operation for sequences is subscripting with small
       
    11 integers: s[i], for i in range(len(s)).  Sets don't support
       
    12 subscripting at all.  Also, sequences allow multiple occurrences and
       
    13 their elements have a definite order; sets on the other hand don't
       
    14 record multiple occurrences and don't remember the order of element
       
    15 insertion (which is why they don't support s[i]).
       
    16 
       
    17 The following classes are provided:
       
    18 
       
    19 BaseSet -- All the operations common to both mutable and immutable
       
    20     sets. This is an abstract class, not meant to be directly
       
    21     instantiated.
       
    22 
       
    23 Set -- Mutable sets, subclass of BaseSet; not hashable.
       
    24 
       
    25 ImmutableSet -- Immutable sets, subclass of BaseSet; hashable.
       
    26     An iterable argument is mandatory to create an ImmutableSet.
       
    27 
       
    28 _TemporarilyImmutableSet -- A wrapper around a Set, hashable,
       
    29     giving the same hash value as the immutable set equivalent
       
    30     would have.  Do not use this class directly.
       
    31 
       
    32 Only hashable objects can be added to a Set. In particular, you cannot
       
    33 really add a Set as an element to another Set; if you try, what is
       
    34 actually added is an ImmutableSet built from it (it compares equal to
       
    35 the one you tried adding).
       
    36 
       
    37 When you ask if `x in y' where x is a Set and y is a Set or
       
    38 ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and
       
    39 what's tested is actually `z in y'.
       
    40 
       
    41 """
       
    42 
       
    43 # Code history:
       
    44 #
       
    45 # - Greg V. Wilson wrote the first version, using a different approach
       
    46 #   to the mutable/immutable problem, and inheriting from dict.
       
    47 #
       
    48 # - Alex Martelli modified Greg's version to implement the current
       
    49 #   Set/ImmutableSet approach, and make the data an attribute.
       
    50 #
       
    51 # - Guido van Rossum rewrote much of the code, made some API changes,
       
    52 #   and cleaned up the docstrings.
       
    53 #
       
    54 # - Raymond Hettinger added a number of speedups and other
       
    55 #   improvements.
       
    56 
       
    57 from __future__ import generators
       
    58 try:
       
    59     from itertools import ifilter, ifilterfalse
       
    60 except ImportError:
       
    61     # Code to make the module run under Py2.2
       
    62     def ifilter(predicate, iterable):
       
    63         if predicate is None:
       
    64             def predicate(x):
       
    65                 return x
       
    66         for x in iterable:
       
    67             if predicate(x):
       
    68                 yield x
       
    69     def ifilterfalse(predicate, iterable):
       
    70         if predicate is None:
       
    71             def predicate(x):
       
    72                 return x
       
    73         for x in iterable:
       
    74             if not predicate(x):
       
    75                 yield x
       
    76     try:
       
    77         True, False
       
    78     except NameError:
       
    79         True, False = (0==0, 0!=0)
       
    80 
       
    81 __all__ = ['BaseSet', 'Set', 'ImmutableSet']
       
    82 
       
    83 class BaseSet(object):
       
    84     """Common base class for mutable and immutable sets."""
       
    85 
       
    86     __slots__ = ['_data']
       
    87 
       
    88     # Constructor
       
    89 
       
    90     def __init__(self):
       
    91         """This is an abstract class."""
       
    92         # Don't call this from a concrete subclass!
       
    93         if self.__class__ is BaseSet:
       
    94             raise TypeError, ("BaseSet is an abstract class.  "
       
    95                               "Use Set or ImmutableSet.")
       
    96 
       
    97     # Standard protocols: __len__, __repr__, __str__, __iter__
       
    98 
       
    99     def __len__(self):
       
   100         """Return the number of elements of a set."""
       
   101         return len(self._data)
       
   102 
       
   103     def __repr__(self):
       
   104         """Return string representation of a set.
       
   105 
       
   106         This looks like 'Set([<list of elements>])'.
       
   107         """
       
   108         return self._repr()
       
   109 
       
   110     # __str__ is the same as __repr__
       
   111     __str__ = __repr__
       
   112 
       
   113     def _repr(self, sorted=False):
       
   114         elements = self._data.keys()
       
   115         if sorted:
       
   116             elements.sort()
       
   117         return '%s(%r)' % (self.__class__.__name__, elements)
       
   118 
       
   119     def __iter__(self):
       
   120         """Return an iterator over the elements or a set.
       
   121 
       
   122         This is the keys iterator for the underlying dict.
       
   123         """
       
   124         return self._data.iterkeys()
       
   125 
       
   126     # Three-way comparison is not supported.  However, because __eq__ is
       
   127     # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and
       
   128     # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this
       
   129     # case).
       
   130 
       
   131     def __cmp__(self, other):
       
   132         raise TypeError, "can't compare sets using cmp()"
       
   133 
       
   134     # Equality comparisons using the underlying dicts.  Mixed-type comparisons
       
   135     # are allowed here, where Set == z for non-Set z always returns False,
       
   136     # and Set != z always True.  This allows expressions like "x in y" to
       
   137     # give the expected result when y is a sequence of mixed types, not
       
   138     # raising a pointless TypeError just because y contains a Set, or x is
       
   139     # a Set and y contain's a non-set ("in" invokes only __eq__).
       
   140     # Subtle:  it would be nicer if __eq__ and __ne__ could return
       
   141     # NotImplemented instead of True or False.  Then the other comparand
       
   142     # would get a chance to determine the result, and if the other comparand
       
   143     # also returned NotImplemented then it would fall back to object address
       
   144     # comparison (which would always return False for __eq__ and always
       
   145     # True for __ne__).  However, that doesn't work, because this type
       
   146     # *also* implements __cmp__:  if, e.g., __eq__ returns NotImplemented,
       
   147     # Python tries __cmp__ next, and the __cmp__ here then raises TypeError.
       
   148 
       
   149     def __eq__(self, other):
       
   150         if isinstance(other, BaseSet):
       
   151             return self._data == other._data
       
   152         else:
       
   153             return False
       
   154 
       
   155     def __ne__(self, other):
       
   156         if isinstance(other, BaseSet):
       
   157             return self._data != other._data
       
   158         else:
       
   159             return True
       
   160 
       
   161     # Copying operations
       
   162 
       
   163     def copy(self):
       
   164         """Return a shallow copy of a set."""
       
   165         result = self.__class__()
       
   166         result._data.update(self._data)
       
   167         return result
       
   168 
       
   169     __copy__ = copy # For the copy module
       
   170 
       
   171     def __deepcopy__(self, memo):
       
   172         """Return a deep copy of a set; used by copy module."""
       
   173         # This pre-creates the result and inserts it in the memo
       
   174         # early, in case the deep copy recurses into another reference
       
   175         # to this same set.  A set can't be an element of itself, but
       
   176         # it can certainly contain an object that has a reference to
       
   177         # itself.
       
   178         from copy import deepcopy
       
   179         result = self.__class__()
       
   180         memo[id(self)] = result
       
   181         data = result._data
       
   182         value = True
       
   183         for elt in self:
       
   184             data[deepcopy(elt, memo)] = value
       
   185         return result
       
   186 
       
   187     # Standard set operations: union, intersection, both differences.
       
   188     # Each has an operator version (e.g. __or__, invoked with |) and a
       
   189     # method version (e.g. union).
       
   190     # Subtle:  Each pair requires distinct code so that the outcome is
       
   191     # correct when the type of other isn't suitable.  For example, if
       
   192     # we did "union = __or__" instead, then Set().union(3) would return
       
   193     # NotImplemented instead of raising TypeError (albeit that *why* it
       
   194     # raises TypeError as-is is also a bit subtle).
       
   195 
       
   196     def __or__(self, other):
       
   197         """Return the union of two sets as a new set.
       
   198 
       
   199         (I.e. all elements that are in either set.)
       
   200         """
       
   201         if not isinstance(other, BaseSet):
       
   202             return NotImplemented
       
   203         return self.union(other)
       
   204 
       
   205     def union(self, other):
       
   206         """Return the union of two sets as a new set.
       
   207 
       
   208         (I.e. all elements that are in either set.)
       
   209         """
       
   210         result = self.__class__(self)
       
   211         result._update(other)
       
   212         return result
       
   213 
       
   214     def __and__(self, other):
       
   215         """Return the intersection of two sets as a new set.
       
   216 
       
   217         (I.e. all elements that are in both sets.)
       
   218         """
       
   219         if not isinstance(other, BaseSet):
       
   220             return NotImplemented
       
   221         return self.intersection(other)
       
   222 
       
   223     def intersection(self, other):
       
   224         """Return the intersection of two sets as a new set.
       
   225 
       
   226         (I.e. all elements that are in both sets.)
       
   227         """
       
   228         if not isinstance(other, BaseSet):
       
   229             other = Set(other)
       
   230         if len(self) <= len(other):
       
   231             little, big = self, other
       
   232         else:
       
   233             little, big = other, self
       
   234         common = ifilter(big._data.has_key, little)
       
   235         return self.__class__(common)
       
   236 
       
   237     def __xor__(self, other):
       
   238         """Return the symmetric difference of two sets as a new set.
       
   239 
       
   240         (I.e. all elements that are in exactly one of the sets.)
       
   241         """
       
   242         if not isinstance(other, BaseSet):
       
   243             return NotImplemented
       
   244         return self.symmetric_difference(other)
       
   245 
       
   246     def symmetric_difference(self, other):
       
   247         """Return the symmetric difference of two sets as a new set.
       
   248 
       
   249         (I.e. all elements that are in exactly one of the sets.)
       
   250         """
       
   251         result = self.__class__()
       
   252         data = result._data
       
   253         value = True
       
   254         selfdata = self._data
       
   255         try:
       
   256             otherdata = other._data
       
   257         except AttributeError:
       
   258             otherdata = Set(other)._data
       
   259         for elt in ifilterfalse(otherdata.has_key, selfdata):
       
   260             data[elt] = value
       
   261         for elt in ifilterfalse(selfdata.has_key, otherdata):
       
   262             data[elt] = value
       
   263         return result
       
   264 
       
   265     def  __sub__(self, other):
       
   266         """Return the difference of two sets as a new Set.
       
   267 
       
   268         (I.e. all elements that are in this set and not in the other.)
       
   269         """
       
   270         if not isinstance(other, BaseSet):
       
   271             return NotImplemented
       
   272         return self.difference(other)
       
   273 
       
   274     def difference(self, other):
       
   275         """Return the difference of two sets as a new Set.
       
   276 
       
   277         (I.e. all elements that are in this set and not in the other.)
       
   278         """
       
   279         result = self.__class__()
       
   280         data = result._data
       
   281         try:
       
   282             otherdata = other._data
       
   283         except AttributeError:
       
   284             otherdata = Set(other)._data
       
   285         value = True
       
   286         for elt in ifilterfalse(otherdata.has_key, self):
       
   287             data[elt] = value
       
   288         return result
       
   289 
       
   290     # Membership test
       
   291 
       
   292     def __contains__(self, element):
       
   293         """Report whether an element is a member of a set.
       
   294 
       
   295         (Called in response to the expression `element in self'.)
       
   296         """
       
   297         try:
       
   298             return element in self._data
       
   299         except TypeError:
       
   300             transform = getattr(element, "__as_temporarily_immutable__", None)
       
   301             if transform is None:
       
   302                 raise # re-raise the TypeError exception we caught
       
   303             return transform() in self._data
       
   304 
       
   305     # Subset and superset test
       
   306 
       
   307     def issubset(self, other):
       
   308         """Report whether another set contains this set."""
       
   309         self._binary_sanity_check(other)
       
   310         if len(self) > len(other):  # Fast check for obvious cases
       
   311             return False
       
   312         for elt in ifilterfalse(other._data.has_key, self):
       
   313             return False
       
   314         return True
       
   315 
       
   316     def issuperset(self, other):
       
   317         """Report whether this set contains another set."""
       
   318         self._binary_sanity_check(other)
       
   319         if len(self) < len(other):  # Fast check for obvious cases
       
   320             return False
       
   321         for elt in ifilterfalse(self._data.has_key, other):
       
   322             return False
       
   323         return True
       
   324 
       
   325     # Inequality comparisons using the is-subset relation.
       
   326     __le__ = issubset
       
   327     __ge__ = issuperset
       
   328 
       
   329     def __lt__(self, other):
       
   330         self._binary_sanity_check(other)
       
   331         return len(self) < len(other) and self.issubset(other)
       
   332 
       
   333     def __gt__(self, other):
       
   334         self._binary_sanity_check(other)
       
   335         return len(self) > len(other) and self.issuperset(other)
       
   336 
       
   337     # Assorted helpers
       
   338 
       
   339     def _binary_sanity_check(self, other):
       
   340         # Check that the other argument to a binary operation is also
       
   341         # a set, raising a TypeError otherwise.
       
   342         if not isinstance(other, BaseSet):
       
   343             raise TypeError, "Binary operation only permitted between sets"
       
   344 
       
   345     def _compute_hash(self):
       
   346         # Calculate hash code for a set by xor'ing the hash codes of
       
   347         # the elements.  This ensures that the hash code does not depend
       
   348         # on the order in which elements are added to the set.  This is
       
   349         # not called __hash__ because a BaseSet should not be hashable;
       
   350         # only an ImmutableSet is hashable.
       
   351         result = 0
       
   352         for elt in self:
       
   353             result ^= hash(elt)
       
   354         return result
       
   355 
       
   356     def _update(self, iterable):
       
   357         # The main loop for update() and the subclass __init__() methods.
       
   358         data = self._data
       
   359 
       
   360         # Use the fast update() method when a dictionary is available.
       
   361         if isinstance(iterable, BaseSet):
       
   362             data.update(iterable._data)
       
   363             return
       
   364 
       
   365         value = True
       
   366 
       
   367         if type(iterable) in (list, tuple, xrange):
       
   368             # Optimized: we know that __iter__() and next() can't
       
   369             # raise TypeError, so we can move 'try:' out of the loop.
       
   370             it = iter(iterable)
       
   371             while True:
       
   372                 try:
       
   373                     for element in it:
       
   374                         data[element] = value
       
   375                     return
       
   376                 except TypeError:
       
   377                     transform = getattr(element, "__as_immutable__", None)
       
   378                     if transform is None:
       
   379                         raise # re-raise the TypeError exception we caught
       
   380                     data[transform()] = value
       
   381         else:
       
   382             # Safe: only catch TypeError where intended
       
   383             for element in iterable:
       
   384                 try:
       
   385                     data[element] = value
       
   386                 except TypeError:
       
   387                     transform = getattr(element, "__as_immutable__", None)
       
   388                     if transform is None:
       
   389                         raise # re-raise the TypeError exception we caught
       
   390                     data[transform()] = value
       
   391 
       
   392 
       
   393 class ImmutableSet(BaseSet):
       
   394     """Immutable set class."""
       
   395 
       
   396     __slots__ = ['_hashcode']
       
   397 
       
   398     # BaseSet + hashing
       
   399 
       
   400     def __init__(self, iterable=None):
       
   401         """Construct an immutable set from an optional iterable."""
       
   402         self._hashcode = None
       
   403         self._data = {}
       
   404         if iterable is not None:
       
   405             self._update(iterable)
       
   406 
       
   407     def __hash__(self):
       
   408         if self._hashcode is None:
       
   409             self._hashcode = self._compute_hash()
       
   410         return self._hashcode
       
   411 
       
   412     def __getstate__(self):
       
   413         return self._data, self._hashcode
       
   414 
       
   415     def __setstate__(self, state):
       
   416         self._data, self._hashcode = state
       
   417 
       
   418 class Set(BaseSet):
       
   419     """ Mutable set class."""
       
   420 
       
   421     __slots__ = []
       
   422 
       
   423     # BaseSet + operations requiring mutability; no hashing
       
   424 
       
   425     def __init__(self, iterable=None):
       
   426         """Construct a set from an optional iterable."""
       
   427         self._data = {}
       
   428         if iterable is not None:
       
   429             self._update(iterable)
       
   430 
       
   431     def __getstate__(self):
       
   432         # getstate's results are ignored if it is not
       
   433         return self._data,
       
   434 
       
   435     def __setstate__(self, data):
       
   436         self._data, = data
       
   437 
       
   438     def __hash__(self):
       
   439         """A Set cannot be hashed."""
       
   440         # We inherit object.__hash__, so we must deny this explicitly
       
   441         raise TypeError, "Can't hash a Set, only an ImmutableSet."
       
   442 
       
   443     # In-place union, intersection, differences.
       
   444     # Subtle:  The xyz_update() functions deliberately return None,
       
   445     # as do all mutating operations on built-in container types.
       
   446     # The __xyz__ spellings have to return self, though.
       
   447 
       
   448     def __ior__(self, other):
       
   449         """Update a set with the union of itself and another."""
       
   450         self._binary_sanity_check(other)
       
   451         self._data.update(other._data)
       
   452         return self
       
   453 
       
   454     def union_update(self, other):
       
   455         """Update a set with the union of itself and another."""
       
   456         self._update(other)
       
   457 
       
   458     def __iand__(self, other):
       
   459         """Update a set with the intersection of itself and another."""
       
   460         self._binary_sanity_check(other)
       
   461         self._data = (self & other)._data
       
   462         return self
       
   463 
       
   464     def intersection_update(self, other):
       
   465         """Update a set with the intersection of itself and another."""
       
   466         if isinstance(other, BaseSet):
       
   467             self &= other
       
   468         else:
       
   469             self._data = (self.intersection(other))._data
       
   470 
       
   471     def __ixor__(self, other):
       
   472         """Update a set with the symmetric difference of itself and another."""
       
   473         self._binary_sanity_check(other)
       
   474         self.symmetric_difference_update(other)
       
   475         return self
       
   476 
       
   477     def symmetric_difference_update(self, other):
       
   478         """Update a set with the symmetric difference of itself and another."""
       
   479         data = self._data
       
   480         value = True
       
   481         if not isinstance(other, BaseSet):
       
   482             other = Set(other)
       
   483         if self is other:
       
   484             self.clear()
       
   485         for elt in other:
       
   486             if elt in data:
       
   487                 del data[elt]
       
   488             else:
       
   489                 data[elt] = value
       
   490 
       
   491     def __isub__(self, other):
       
   492         """Remove all elements of another set from this set."""
       
   493         self._binary_sanity_check(other)
       
   494         self.difference_update(other)
       
   495         return self
       
   496 
       
   497     def difference_update(self, other):
       
   498         """Remove all elements of another set from this set."""
       
   499         data = self._data
       
   500         if not isinstance(other, BaseSet):
       
   501             other = Set(other)
       
   502         if self is other:
       
   503             self.clear()
       
   504         for elt in ifilter(data.has_key, other):
       
   505             del data[elt]
       
   506 
       
   507     # Python dict-like mass mutations: update, clear
       
   508 
       
   509     def update(self, iterable):
       
   510         """Add all values from an iterable (such as a list or file)."""
       
   511         self._update(iterable)
       
   512 
       
   513     def clear(self):
       
   514         """Remove all elements from this set."""
       
   515         self._data.clear()
       
   516 
       
   517     # Single-element mutations: add, remove, discard
       
   518 
       
   519     def add(self, element):
       
   520         """Add an element to a set.
       
   521 
       
   522         This has no effect if the element is already present.
       
   523         """
       
   524         try:
       
   525             self._data[element] = True
       
   526         except TypeError:
       
   527             transform = getattr(element, "__as_immutable__", None)
       
   528             if transform is None:
       
   529                 raise # re-raise the TypeError exception we caught
       
   530             self._data[transform()] = True
       
   531 
       
   532     def remove(self, element):
       
   533         """Remove an element from a set; it must be a member.
       
   534 
       
   535         If the element is not a member, raise a KeyError.
       
   536         """
       
   537         try:
       
   538             del self._data[element]
       
   539         except TypeError:
       
   540             transform = getattr(element, "__as_temporarily_immutable__", None)
       
   541             if transform is None:
       
   542                 raise # re-raise the TypeError exception we caught
       
   543             del self._data[transform()]
       
   544 
       
   545     def discard(self, element):
       
   546         """Remove an element from a set if it is a member.
       
   547 
       
   548         If the element is not a member, do nothing.
       
   549         """
       
   550         try:
       
   551             self.remove(element)
       
   552         except KeyError:
       
   553             pass
       
   554 
       
   555     def pop(self):
       
   556         """Remove and return an arbitrary set element."""
       
   557         return self._data.popitem()[0]
       
   558 
       
   559     def __as_immutable__(self):
       
   560         # Return a copy of self as an immutable set
       
   561         return ImmutableSet(self)
       
   562 
       
   563     def __as_temporarily_immutable__(self):
       
   564         # Return self wrapped in a temporarily immutable set
       
   565         return _TemporarilyImmutableSet(self)
       
   566 
       
   567 
       
   568 class _TemporarilyImmutableSet(BaseSet):
       
   569     # Wrap a mutable set as if it was temporarily immutable.
       
   570     # This only supplies hashing and equality comparisons.
       
   571 
       
   572     def __init__(self, set):
       
   573         self._set = set
       
   574         self._data = set._data  # Needed by ImmutableSet.__eq__()
       
   575 
       
   576     def __hash__(self):
       
   577         return self._set._compute_hash()