Problems with emulation of numeric types and coercion rules

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • ziga.seilnacht@gmail.com

    #1

    Problems with emulation of numeric types and coercion rules

    """
    I am trying to write some classes representing the quaternion number.
    I wrote a base class, which implements only the numerical interface,
    and a few subclasses, which provide methods for their specific domain.

    Since the operator methods will be the same for all these classes,
    the base class operator methods don't explicitly return an instance
    of this base class, but rather an instance of the class that called
    them (ie 'return self.__class__( *args)' not 'return Quaternion(*arg s)')


    Documentation at http://docs.python.org/ref/coercion-rules.html says:

    Below, __op__() and __rop__() are used to signify the generic method
    names corresponding to an operator; __iop__() is used for the
    corresponding in-place operator. For example, for the operator `+',
    __add__() and __radd__() are used for the left and right variant of
    the binary operator, and __iadd__() for the in-place variant.

    For objects x and y, first x.__op__(y) is tried. If this is not
    implemented or returns NotImplemented, y.__rop__(x) is tried. If this
    is also not implemented or returns NotImplemented, a TypeError
    exception is raised. But see the following exception:

    Exception to the previous item: if the left operand is an instance of a
    built-in type or a new-style class, and the right operand is an
    instance of a proper subclass of that type or class, the right
    operand's __rop__() method is tried before the left operand's __op__()
    method. This is done so that a subclass can completely override binary
    operators. Otherwise, the left operand's __op__ method would always
    accept the right operand: when an instance of a given class is
    expected, an instance of a subclass of that class is always acceptable.


    So I thought my plan would work. But it shows that even if the right
    operand is a subclass of left operand, its __rop__() method is called
    first _only_ when it overwrites the parent's method. If the method is
    inherited or just copied from its parent, the rule is ignored. Here is
    a simplified example:
    """

    # file: number.py

    def convert(obj):
    if isinstance(obj, Number):
    return obj._value
    try:
    f = float(obj)
    except (TypeError, ValueError):
    return NotImplemented
    if f == obj:
    return f
    return NotImplemented


    class Number(object):

    def __init__(self, value=0.):
    value = float(value)
    self._value = value

    def __add__(self, other):
    """
    Return sum of two real numbers.

    Returns an instance of self.__class__ so that subclasses
    would't have to overwrite this method when just extending
    the base class' interface.
    """
    other = convert(other)
    if other is NotImplemented:
    return NotImplemented
    return self.__class__( self._value + other)

    __radd__ = __add__

    # other methods


    class Broken(Number):
    pass


    class StillBroken(Num ber):

    __add__ = __radd__ = Number.__add__


    class Working(Number) :

    def __add__(self, other):
    return Number.__add__( self, other)

    __radd__ = __add__


    __doc__ = \
    """
    If I now open the python interpreter::
    [color=blue]
    >python[/color]
    Python 2.4.2 (#67, Sep 28 2005, 12:41:11) [MSC v.1310 32 bit (Intel)]
    on win32
    Type "help", "copyright" , "credits" or "license" for more information.[color=blue][color=green][color=darkred]
    >>> from number import *
    >>> number = Number()
    >>> broken1 = Broken()
    >>> broken2 = StillBroken()
    >>> working = Working()[/color][/color][/color]

    When the subclass is on the left side of the operator,
    everything works as intended::
    [color=blue][color=green][color=darkred]
    >>> print type(broken1 + number).__name_ _[/color][/color][/color]
    Broken[color=blue][color=green][color=darkred]
    >>> print type(broken2 + number).__name_ _[/color][/color][/color]
    StillBroken[color=blue][color=green][color=darkred]
    >>> print type(working + number).__name_ _[/color][/color][/color]
    Working

    But when the sublass is on the right side of the operator, only the
    subclass that has owerwritten the operator method gets called first::
    [color=blue][color=green][color=darkred]
    >>> print type(number + broken1).__name __[/color][/color][/color]
    Number[color=blue][color=green][color=darkred]
    >>> print type(number + broken2).__name __[/color][/color][/color]
    Number[color=blue][color=green][color=darkred]
    >>> print type(number + working).__name __[/color][/color][/color]
    Working

    According to the coercion rule, the subclass should allways be called
    first. Is this a bug (either in documentation or in python), or should
    I stop trying to 'upcast' the return value? I did find a solution to
    this problem, but it isn't pretty and I'm not the only one using this
    method. Also if this is a bug could this mail be used as a bug report?

    Thanks in advance.

    Ziga

    """

    if __name__ == '__main__':
    import doctest
    doctest.testmod ()

  • ziga.seilnacht@gmail.com

    #2
    Re: Problems with emulation of numeric types and coercion rules

    Never mind, I forgot that class inheritance tree is a tree.
    Resulting type of adding Broken and Working from previous
    example would also depend on the order of operands.

    Ziga

    Comment

    Working...