[Tutor] Operator overloading surprise

Danny Yoo dyoo@decrem.com
Mon, 18 Mar 2002 02:53:58 -0800 (PST)


On Sun, 17 Mar 2002, Arthur Siegel wrote:

> I posted the below on the python-list, and other than
> a response seeming to share my surprise (and verifying that
> the behavior can be duplicated), not much interest.  Thought
> I'd try here.
> 
> 
> Given:
> 
> class Complex(complex):
>     def __mul__(self,other):
>        other=Complex(other)
>        t = complex.__mul__(self,other)
>        return Complex(t.real,t.imag)
>     __rmul__ = __mul__
> 
>     def __add__(self,other):
>        other=Complex(other)
>        return Complex(self.real.__add__
> (other.real),self.imag.__add__(other.imag))
>     __radd__ = __add__
> 
> Then:
> 
> print type(Complex(5,4) * 7)
> >><class '__main__.Complex'>
> print type(7 * Complex(5,4))
> >><class '__main__.Complex'>
> print type(Complex(5,4) + 7)
> >><class '__main__.Complex'>
> 
> But:
> 
> print type(7 + Complex(5,4))
> >><type 'complex'>


Hmmmm...  This looks really subtle.  My guess at the moment is that
there's some type coersion going on that turns your Complex() class back
into the normal base 'complex' type.

I'm closely reading:

    http://www.python.org/doc/current/ref/numeric-types.html

at the moment... Hmmm...  I believe that what's happening is that Python
is coercing both into complex numbers, and then trying again, according to
rule 2a of the __coerce__() function.  What's probably happening is that
the built-in __coerce__() of the 'complex' type just blindly calls
'complex()' on both sides, treating them numerically as in rule 3c.

There's some ambiguous meanings with the new style classes that makes me
feel a little uncomfortable; someone may want to look at this later.

But to fix this problem, we can add one more method to tell Python to do
something special on coersion:

###
class Complex(complex):
    def __mul__(self,other):
        other = Complex(other)
        t = complex.__mul__(self,other)
        return Complex(t.real,t.imag)
    __rmul__ = __mul__
    
    def __coerce__(self, other):
        return (self, Complex(other))

    def __add__(self, other):
        return Complex(self.real.__add__(other.real),
                       self.imag.__add__(other.imag))

    __radd__ = __add__
###


And this fixes the problem you were running into:


###
>>> print type(7 + Complex(3, 4))
<class '__main__.Complex'>
>>> print 7+Complex(3, 4)
(10+4j)
###

That was subtle!  *grin*  Hope this helps!


(The server I use for my mail is down, so that's why I'm using an
emergency email account.  Apologies for those who can't access the IDLE
introduction on my web page!)