[Python-Dev] Advice on numbers.py implementation of binary mixins.

Raymond Hettinger python at rcn.com
Fri Jun 13 22:06:52 CEST 2008

PEP-3141 outlines an approach to writing binary operators to allow the
right operand to override the operation if the left operand inherits
the operation from the ABC.

Here is my first approximation at how to write them for the Integral mixins:

class Integral(Rational):

    def __and__(self, other):
        if isinstance(other, (type(self), int, long)):         # XXX
            return int(self) & int(other)
        return NotImplemented

    def __rand__(self, other):
        if isinstance(other, Integral):
            return int(other) & int(self)
        return NotImplemented

The question for the group is what to put on the XXX line.

1. Limit it to type(self).  This approach claims the least knowledge about other types and uses their __rand__ methods().

2. Use type(self), int, and long.  This approach says that we know that ints and longs can be reasonably converted to an int; 
however, it means that any int/long subtype cannot use __rand__ to override the mixin's __and__.

3. Emulate the PEP as closely as possible and accomodate subclassing without changing behaviors.  This approach is a bit complex:

        knowntypes = (set(type(self).__mro__)
                               & set(type(other.__mro__))
                               - set(type(Integral.__mro__))
                               | set([int, long]))
        if isinstance(other, tuple(knowntypes)):

I got this by analyzing the possible paths in an inheritance tree:

   class P(Integral): pass
   class Q(P): pass
   class R(Q): pass
   r = R(3)
   class S(Q): def __rand__(s,o) ...
   s = S(6)
   class T(Integral): def __rand__(s,o) ...
   t = T(5)

With r&t, there is no common ancestor below Integral, so we want
Integral.__and__() to return NotImplemented and allow T.__rand__()
to do its thing.

With r&s, both r and s share Q as a common ancenstor.
By using the mixin and not overriding __and__(), Q specifies that __and__()
should mean int(r)&int(s) whenever isinstance(r,Q) and isinstance(s,Q).

Approaches 1 & 2 don't search the mro for shared ancestors below
the level of Integral.  So, when evaluating r&s, Integral.__and__()
only checks that r is not an instance of S, int or long, and it
erroneously returns NotImplemented , leaving s.__rand__() to take over.

What do you guys think?


More information about the Python-Dev mailing list