[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