By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,846 Members | 1,872 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,846 IT Pros & Developers. It's quick & easy.

Problems with emulation of numeric types and coercion rules

P: n/a
"""
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(*args)')
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(Number):

__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::
python

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.
from number import *
number = Number()
broken1 = Broken()
broken2 = StillBroken()
working = Working()
When the subclass is on the left side of the operator,
everything works as intended::
print type(broken1 + number).__name__ Broken print type(broken2 + number).__name__ StillBroken print type(working + number).__name__ 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::
print type(number + broken1).__name__ Number print type(number + broken2).__name__ Number print type(number + working).__name__

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()

Nov 1 '05 #1
Share this Question
Share on Google+
1 Reply


P: n/a
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

Nov 2 '05 #2

This discussion thread is closed

Replies have been disabled for this discussion.