Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

surprise overriding __radd__ in subclass of complex #36275

Closed
ajsiegel mannequin opened this issue Mar 18, 2002 · 9 comments
Closed

surprise overriding __radd__ in subclass of complex #36275

ajsiegel mannequin opened this issue Mar 18, 2002 · 9 comments
Assignees
Labels
docs Documentation in the Doc dir

Comments

@ajsiegel
Copy link
Mannequin

ajsiegel mannequin commented Mar 18, 2002

BPO 531355
Nosy @gvanrossum, @freddrake

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = 'https://github.com/freddrake'
closed_at = <Date 2002-09-06.23:35:16.000>
created_at = <Date 2002-03-18.14:32:27.000>
labels = ['docs']
title = 'surprise overriding __radd__ in subclass of complex'
updated_at = <Date 2002-09-06.23:35:16.000>
user = 'https://bugs.python.org/ajsiegel'

bugs.python.org fields:

activity = <Date 2002-09-06.23:35:16.000>
actor = 'gvanrossum'
assignee = 'fdrake'
closed = True
closed_date = None
closer = None
components = ['Documentation']
creation = <Date 2002-03-18.14:32:27.000>
creator = 'aj_siegel'
dependencies = []
files = []
hgrepos = []
issue_num = 531355
keywords = []
message_count = 9.0
messages = ['9762', '9763', '9764', '9765', '9766', '9767', '9768', '9769', '9770']
nosy_count = 4.0
nosy_names = ['gvanrossum', 'fdrake', 'nnorwitz', 'aj_siegel']
pr_nums = []
priority = 'low'
resolution = 'fixed'
stage = None
status = 'closed'
superseder = None
type = None
url = 'https://bugs.python.org/issue531355'
versions = ['Python 2.3']

@ajsiegel
Copy link
Mannequin Author

ajsiegel mannequin commented Mar 18, 2002

I had presented this on tutor and python-list:

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'>

Danny Yoo, after looking into it pretty deeply -
and going to the docs and source gace me this
advice.

"In any case, this is definitely a bug in the
documentation. Arthur, bring
it up on comp.lang.python and Sourceforge again.
Someone should really
look at your example, since it does seem serious... if
not a little
obscure. *grin* "

@ajsiegel ajsiegel mannequin closed this as completed Mar 18, 2002
@ajsiegel ajsiegel mannequin assigned freddrake Mar 18, 2002
@ajsiegel ajsiegel mannequin added the docs Documentation in the Doc dir label Mar 18, 2002
@ajsiegel ajsiegel mannequin closed this as completed Mar 18, 2002
@ajsiegel ajsiegel mannequin assigned freddrake Mar 18, 2002
@ajsiegel ajsiegel mannequin added the docs Documentation in the Doc dir label Mar 18, 2002
@gvanrossum
Copy link
Member

Logged In: YES
user_id=6380

You're right, something's weird. I'll add this to my list.

@gvanrossum
Copy link
Member

Logged In: YES
user_id=6380

It seems that * is the exception -- all other operators (+,
-, /, **) return the type of the left operand. It gets even
weirder if we change the left operand from 7 to 7.0 -- then

  • is no longer an exception and returns a complex like all
    other operators!

The weirdness is in int_mul. If the right argument is not an
int but a sequence that implements the sq_repeat slot, it
invokes that. But if you create a class that defines
__mul__, that implements the sequence repeat slot! (Python
doesn't know if you intend to implement a seqence or a
number, when you define a __mul__ method, so it maps both
the nb_multiply and the sq_repeat slots to the __mul__
method).

So 7 + Complex() takes the path through int_mul that calls
the Complex.__mul__ , and that's why you get a Complex value
back. Floats can't be used in the sequence repeat operator,
so its multiply is "normal".

But wait...! There's another mystery. Ints don't know how to
add themselves to complex numbers! So why wouldn't
7+Complex() use the normal pattern of (a) try the left
operand, (b) if that returns NotImplemented, try the right
operand? The answer is that complex numbers implement
coercion! And in that case, step (b) is modified to (b) ask
either operand to perform a coercion, and if it succeeds,
ask the left operand of the resulting coerced pair to
perform the operation. So complex coerces the int to a plain
complex, and then invokes the plain complex's add/mul/etc.
operation.

Solution: add the following to your Complex class:

    def __coerce__(self, other):
        x = complex.__coerce__(self, other)
        if x is NotImplemented:
            return x
        a, b = x
        return Complex(a), Complex(b)

I don't think I want to do anything to fix this, although I
think I'll pass it on to Fred for documentation (not that I
expect him to be in a hurry to fix it either, given the
subtlety of the issues).

In the long run, I think complex should stop using coercion
and start behaving like a "new-style" number like all the
other numeric types. That should fix the second mystery.

Also, in the long run, the sq_repeat and sq_concat slots
should be deprecated and instead sequences should implement
multiply and add operations. Then the funny business in
int_mul could be taken out and the first mystery would
disappear.

@ajsiegel
Copy link
Mannequin Author

ajsiegel mannequin commented Mar 25, 2002

Logged In: YES
user_id=248775

Guido -

Thanks for your attention to my report.

Another bit of suprise with operator overriding
in a subclass of complex:

from __future__ import division
class Complex(complex):
def __div__(self,other):
t=complex.__div__(self,other)
return Complex(t.real,t.imag)
a=Complex(5,4)
b=Complex(1,7)

> <type 'complex'>

But:

everything the same *without* calling
from __future__ import division

print type(a/b)

><class '__main__.Complex'>

Based your analysis of the previous
issue(which I follow only
in broad outline), I have a sense of
what's happening.

My general sense, though, is that this issue
is in some sense more of a problem than
the other.

Don't think anyone will expect a change in
behavior here based on the from __future__
call. Not that subbing complex is a common pursuit -
but couldn't this actually break working code
in a very unanticpated way, eventually.

Obviously not a big issue if the other changes
you see happening with complex coercian kick in
first, and in fact those changes - as I suspect -
would prevent a different return type based
on which way int/int is toggled.

Art

@ajsiegel
Copy link
Mannequin Author

ajsiegel mannequin commented Mar 25, 2002

Logged In: YES
user_id=248775

Have just re-read my submission - and
in addition to my usual quota of typos
I see I left out something that could
confuse what I am trying to bring to
your attention.

The gist, I am sure you will see, is that
type(a/b), where a and b are of a class derived
from complex, will change based on whether
from __future__ import division is or is not
called.

And I am assuming that behavior is unanticipated.

And I assuming there is no reason to submit this a
a bug report separate from this thread.

Art

@gvanrossum
Copy link
Member

Logged In: YES
user_id=6380

If you want to override division in a complex subclass, you
should define both __truediv__ and __div__, as well as
__rtruediv__ and __rdiv__. You can do this by saying
__truediv__ = __div__, of course.

@ajsiegel
Copy link
Mannequin Author

ajsiegel mannequin commented Mar 25, 2002

Logged In: YES
user_id=248775

Knew I'd embarass myself soon enough in this
company.

Found the __truediv__ answer on the train into
work. Came here hoping to catch it, before you caught
me.

Too late.

Thanks again - oops for the false alarm.

Art

@nnorwitz
Copy link
Mannequin

nnorwitz mannequin commented Sep 6, 2002

Logged In: YES
user_id=33168

Guido, Fred, is there anything to be done or should this be
closed?

@gvanrossum
Copy link
Member

Logged In: YES
user_id=6380

This is fixed in 2.3. Can't remember if it was fixed in 2.2
branch, don't care.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir
Projects
None yet
Development

No branches or pull requests

2 participants