[Python-checkins] r54611 - sandbox/trunk/abc/abc.py

guido.van.rossum python-checkins at python.org
Fri Mar 30 01:15:41 CEST 2007


Author: guido.van.rossum
Date: Fri Mar 30 01:15:37 2007
New Revision: 54611

Modified:
   sandbox/trunk/abc/abc.py
Log:
Another checkpoint.


Modified: sandbox/trunk/abc/abc.py
==============================================================================
--- sandbox/trunk/abc/abc.py	(original)
+++ sandbox/trunk/abc/abc.py	Fri Mar 30 01:15:37 2007
@@ -2,16 +2,31 @@
 
 """Abstract Base Classes experiment.
 
-XXX How to decide the order in which orthogonal base classes are
-listed?  For now, I'm putting the smaller API second.
-
 Note: this depends on the brand new Py3k feature that object.__ne__()
 is implemented by calling object.__eq__() and reversing the outcome
 (unless NotImplemented).
+
+XXX Should we use argument annotations here?
+
+XXX How to decide the order in which orthogonal base classes are
+listed?  For now, I'm putting the smaller API second.
 """
 
 __author__ = "Guido van Rossum <guido at python.org>"
 
+import sys
+
+
+### BASICS ###
+
+
+class Hashable:
+
+  """A hashable has one method, __hash__()."""
+
+  def __hash__(self):
+    return 0
+
 
 class Iterable:
 
@@ -38,6 +53,9 @@
     return 0
 
 
+### SETS ###
+
+
 class BasicSet:
 
   # XXX Alternative name: Container? Oracle (as in Delphi's Oracle)?
@@ -70,10 +88,6 @@
 
   The idea here is that all you have to do is redefine __le__ and then
   the other operations will automatically follow suit.
-
-  XXX However I'm not sure that this always does the right thing,
-  espectially for __ge__ and __gt__, as these defer to the other
-  argument's class; that feels fishy.
   """
 
   def __le__(self, other):
@@ -96,15 +110,58 @@
       return NotImplemented
     return len(self) == len(other) and self.__le__(other)
 
-  def __ge__(self, other):
-    if not isinstance(other, SizeableSet):
-      return NotImplemented
-    return other.__le__(self)
+  # XXX Should we define __ge__ and __gt__ too?
+
+  # XXX The following implementations of &, |, ^, - return frozen sets
+  # because we have to pick a concrete type.  They are allowed to
+  # return any subclass of SizeableSet (but SizeableSet is not a
+  # concrete implementation).
+
+  def __and__(self, other):
+    new = set(self)
+    new.intersection_update(other)
+    return frozenset(new)
+
+  def __or__(self, other):
+    new = set(self)
+    new.update(other)
+    return frozenset(new)
+
+  def __xor__(self, other):
+    new = set(self)
+    new.symmetric_difference_update(other)
+    return frozenset(new)
+
+  def __sub__(self, other):
+    new = set(self)
+    new.difference_update(other)
+    return frozenset(new)
+
+
+class HashableSet(SizeableSet, HashableSet):
+
+  def __hash__(self):
+    """The hash value must match __eq__.
+
+    All sets ought to compare equal if they contain the same elements,
+    regardless of how they are implemented; so there's not much freedom
+    for __eq__ or __hash__.
+    """
+    # This particular algorithm is similar to tuple.__hash__().
+    # Implementations are free to use a different algorithm.
+    mult = 1000003
+    h = 0x345678
+    for i, elem in enumerate(self):
+      h = ((h ^ hash(elem)) * mult) & sys.maxint
+      mult = (mult + (82520 + 2*i)) & sys.maxint
+    return h
+
+
+# class set(SizeableSet)
+# class frozenset(HashableSet)
 
-  def __gt__(self, other):
-    if not isinstance(other, SizeableSet):
-      return NotImplemented
-    return other.__lt__(self)
+
+### MAPPINGS ###
 
 
 class BasicMapping:
@@ -169,7 +226,12 @@
     for key in self._mapping:
       yield key, self._mapping[key]
 
-  def __contains__(self, (key, value)):
+  def __contains__(self, item):
+    if not isinstance(item, Iterable):
+    try:
+      key, value = item
+    except:
+      return False
     try:
       val = self._mapping[key]
     except KeyError:
@@ -255,3 +317,92 @@
       if elem == value:
         return True
     return False
+
+
+### SEQUENCES ###
+
+
+class Sequence(Iterable):
+
+  """A minimal sequence.
+
+  I'm not bothering with an unsized version; I don't see a use case.
+
+  Concrete subclasses must override __new__ or __init__, __getitem__,
+  and __len__; they might want to override __add__ and __mul__.  The
+  constructor signature is expected to support a single argument
+  giving an iterable providing the elements.
+  """
+
+  def __index(self, i):
+    # Internal helper to raise TypeError for non-integer(-castable) values
+    if not isinstance(i, int):
+      if not hasattr(i, "__index__"):
+        raise TypeError
+      i = i.__index__()
+      if not isinstance(i, int):
+        raise TypeError
+    return i
+
+  def __getitem__(self, index):
+    if isinstance(index, slice):
+      return self.__getslice(index)
+    index = self.__index(index)
+    raise IndexError
+
+  def __getslice(self, slc):
+    # XXX Would be nice to make this generally available?
+    start, stop, step = slc.start, slc.stop, slc.step
+    for index in start, stop, step:
+      if index is not None:
+        self.__index(index)
+    if step is None:
+      step = 1
+    if step == 0:
+      raise ValueError
+    if step < 0:
+      if start is None:
+        start = len(self) - 1
+      if stop is None:
+        stop = -1
+    else:
+      if start is None:
+        start = 0
+      if stop is None:
+        stop = len(self)
+    return self.__class__(self[i] for i in range(start, stop, step))
+
+  def __len__(self):
+    return 0
+
+  def __iter__(self):
+    i = 0
+    while i < len(self):
+      yield self[i]
+      i += 1
+
+  def __reversed__(self):
+    i = len(self)
+    while i > 0:
+      i -= 1
+      yield self[i]
+
+  def index(self, value):
+    for i, elem in enumerate(self):
+      if elem == value:
+        return i
+    raise ValueError
+
+  def count(self, value):
+    return sum(1 for elem in self if elem == value)
+
+  def __add__(self, other):
+    if not isinstance(other, Sequence):
+      return NotImplemented
+    return self.__class__(elem for seq in (self, other) for elem in seq)
+
+  def __mul__(self, repeat):
+    if not isinstance(repeat, int) and not hasattr(repeat, "__index__"):
+      return NotImplemented
+    repeat = self.__index(repeat)
+    return self.__class__(elem for i in range(repeat) for elem in self)


More information about the Python-checkins mailing list