[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