[Python-checkins] r54593 - sandbox/trunk/abc sandbox/trunk/abc/abc.py
guido.van.rossum
python-checkins at python.org
Wed Mar 28 02:46:06 CEST 2007
Author: guido.van.rossum
Date: Wed Mar 28 02:46:04 2007
New Revision: 54593
Added:
sandbox/trunk/abc/
sandbox/trunk/abc/abc.py (contents, props changed)
Log:
Checkpoint.
Added: sandbox/trunk/abc/abc.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/abc/abc.py Wed Mar 28 02:46:04 2007
@@ -0,0 +1,257 @@
+#!/usr/bin/env python3.0
+
+"""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).
+"""
+
+__author__ = "Guido van Rossum <guido at python.org>"
+
+
+class Iterable:
+
+ """An iterable has one method, __iter__()."""
+
+ def __iter__(self):
+ return Iterator()
+
+
+class Iterator(Iterable):
+
+ """An iterator has two methods, __iter__() and next()."""
+
+ def next(self):
+ raise StopIteration
+
+ def __iter__(self):
+ return self
+
+
+class Sizeable:
+
+ def __len__(self):
+ return 0
+
+
+class BasicSet:
+
+ # XXX Alternative name: Container? Oracle (as in Delphi's Oracle)?
+
+ """A basic set has __contains__() and that's it."""
+
+ def __contains__(self, elem):
+ return False
+
+
+class IterableSet(BasicSet, Iterable):
+
+ """An iterable set is a basic set that is also iterable.
+
+ It may not have a length though; it may be infinite!
+
+ XXX Do we care about potentially infinite sets, or sets of
+ indeterminate size?
+ """
+
+
+class SizeableSet(IterableSet, Sizeable):
+
+ """A sizeable set is an iterable set that has a finite, known size.
+
+ This enables a generic implementation of equality and ordering based
+ on set inclusion.
+
+ I don't see a use case for a non-iterable set that has a size.
+
+ 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):
+ if not isinstance(other, SizeableSet):
+ return NotImplemented
+ if len(self) > len(other):
+ return False
+ for elem in self:
+ if elem not in other:
+ return False
+ return True
+
+ def __lt__(self, other):
+ if not isinstance(other, SizeableSet):
+ return NotImplemented
+ return len(self) < len(other) and self.__le__(other)
+
+ def __eq__(self, other):
+ if not isinstance(other, SizeableSet):
+ 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)
+
+ def __gt__(self, other):
+ if not isinstance(other, SizeableSet):
+ return NotImplemented
+ return other.__lt__(self)
+
+
+class BasicMapping:
+
+ # XXX derive from (BasicSet)?
+
+ """A basic mapping has __getitem__(), __contains__() and get().
+
+ The idea is that you only need to override __getitem__().
+
+ Other dict methods are not supported.
+ """
+
+ def __getitem__(self, key):
+ raise KeyError
+
+ def get(self, key, default=None):
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def __contains__(self, key):
+ try:
+ self[key]
+ return True
+ except KeyError:
+ return False
+
+
+class IterableMapping(BasicMapping, Iterable):
+
+ def keys(self):
+ return KeysView(self)
+
+ def items(self):
+ return ItemsView(self)
+
+ def values(self):
+ return ValuesView(self)
+
+
+class _MappingView:
+
+ def __init__(self, mapping):
+ self._mapping = mapping
+
+
+class KeysView(_MappingView, BasicSet):
+
+ def __iter__(self):
+ for key in self._mapping:
+ yield key
+
+ def __contains__(self, key):
+ return key in self._mapping
+
+
+class ItemsView(_MappingView, BasicSet):
+
+ def __iter__(self):
+ for key in self._mapping:
+ yield key, self._mapping[key]
+
+ def __contains__(self, (key, value)):
+ try:
+ val = self._mapping[key]
+ except KeyError:
+ return False
+ return value == val
+
+
+class ValuesView(_MappingView):
+
+ # Note: does not derive from BasicSet, and does not implement __contains__!
+
+ def __iter__(self):
+ for key in self._mapping:
+ yield self._mapping[key]
+
+
+class SizeableMapping(IterableMapping, Sizeable):
+
+ def keys(self):
+ return SizeableKeysView(self)
+
+ def items(self):
+ return SizeableItemsView(self)
+
+ def values(self):
+ return SizeableValuesView(self)
+
+ def __eq__(self, other):
+ if not isinstance(other, SizeableMapping):
+ return NotImplemented
+ if len(other) != len(self):
+ return False
+ # XXX Or: for key, value1 in self.items(): ?
+ for key in self:
+ value1 = self[key]
+ try:
+ value2 = other[key]
+ except KeyError:
+ return False
+ if value1 != value2:
+ return False
+ return True
+
+
+class _SizeableMappingView(_MappingView, Sizeable):
+
+ def __len__(self):
+ return len(self._mapping)
+
+
+class SizeableKeysView(_SizeableMappingView, KeysView, SizeableSet):
+ pass
+
+
+class SizeableItemsView(_SizeableMappingView, ItemsView, SizeableSet):
+ pass
+
+
+class SizeableValuesView(_SizeableMappingView, ValuesView):
+
+ def __eq__(self, other):
+ if not (isinstance(other, Sizeable) and isinstance(other, Iterable)):
+ return NotImplemented
+ if len(self) != len(other):
+ return False
+ # XXX This is slow. Sometimes this could be optimized, but these
+ # are the semantics: we can't depend on the values to be hashable
+ # or comparable.
+ o_values = list(other)
+ for value in self:
+ for i, o_value in enumerate(o_values):
+ if value == o_value:
+ del o_values[i]
+ break
+ else:
+ return False
+ assert not o_values # self must have mutated somehow
+ return True
+
+ def __contains__(self, value):
+ # This is slow, but these are the semantics.
+ for elem in self:
+ if elem == value:
+ return True
+ return False
More information about the Python-checkins
mailing list