[Python-Dev] Proposed Mixins for Wide Interfaces

Raymond Hettinger python@rcn.com
Wed, 4 Sep 2002 17:40:34 -0400

> > How about adding some mixins to simplify the
> > implementation of some of the fatter interfaces?

> Can you suggest implementations for these, to be absolutely clear what
> you mean?
 -- snip --
 > What if the "natural" thing to implement is __le__ instead of __lt__?
> That's the case for sets.  Or __gt__ (less likely)?

Yes.  Here is some code
class CompareMixin:
    Given an __eq__ method in a subclass, adds a __ne__ method
    Given __eq__ and __lt__, adds !=, <=, >, >=.
    If supplied, takes advantage of __lte__ for speed.

    def __eq__(self, other):
        raise NotImplementedError

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        raise NotImplementedError

    def __lte__(self, other):
        return self < other or self == other

    def __gt__(self, other):
        return not (self <= other)

    def __gte__(self, other):
        return not (self < other)
## Example from sets
import mixins

class BaseSet(object, mixins.CompareMixin):
    """Common base class for mutable and immutable sets."""

    __slots__ = ['_data']

    # . . .

    def issubset(self, other):
        """Report whether another set contains this set."""
        if len(self) > len(other):  # Fast check for obvious cases
            return False
        otherdata = other._data
        for elt in self:
            if elt not in otherdata:
                return False
        return True

    def __eq__(self, other):
        return self._data == other._data

    def __lt__(self, other):
        return len(self) < len(other) and self.issubset(other)

    __le__ = issubset   # optional, but recommended for speed.

# Example where gt is the most natural implementation
class Anyhoo(CompareMixin):
    __eq__ = someBigEqualityTest
    __gt__ = someBigComplexOrderingFunction
    def __lt__(self, other):
        return not(self>other or self==other)

> > class MappingMixin:
> >     """
> >     Given __setitem__, __getitem__,  and keys,
> >     implements values, items, update, get, setdefault, len,
> >     iterkeys, iteritems, itervalues, has_key, and __contains__.
> > 
> >     If __delitem__ is also supplied, implements clear, pop,
> >     and popitem.
> > 
> >     Takes advantage of __iter__ if supplied (recommended).

> Does that mean that if you have __iter__, you don't use keys()?  In
> that case it should implement keys() out of __iter__.  Maybe this
> should be required.

Not really.  keys() is always required.  If __iter__ is supplied,
then things like iterkeys(), iteritems(), and itervalues() get computed
from __iter__ rather than keys().  

My thought on using keys() as part of the minimum specification is
that database style interfaces always supply some type of list method.
For instance, shelve can be instantly widened with the mixin, no 
other coding is required.

OTOH, I'm not glued to the idea of using keys() as part of the minimum spec.

> >     Takes advantage of __contains__ or has_key if supplied
> >     (recommended).
> >     """

> Let's standardize on __contains__, not has_key().  I guess you could
> provide __contains__ as follows:

Makes sense.

> > The idea is to make it easier to implement these interfaces.
> > Also, if the interfaces get expanded, the clients automatically
> > updated.  

> A similar thing for sequences would be useful too, right?

Hmm, listing and concatenation beget repetition;
len() and __getitem__() beget slicing.
iteration and __cmp__ beget min(), max()

For mutable sequences, supplying __setitem__ begets
appending, extending, and slice assignment.

Supplying __delitem__ begets pop(), remove() and slice deletion.

For overachivers, the above are all that are needed
for sort(), reverse(), index(), insert(), and count()

Would you like me to create a mixin module
and put it in the sandbox?

Raymond Hettinger