Addition and multiplication puzzle

Discussion in 'Python' started by Mark Dickinson, Oct 25, 2003.

  1. Can anyone either reproduce or explain the following
    apparently inconsistent behaviours of __add__ and
    __mul__? The class Gaussian provides a sub-bare-bones
    implementation of Gaussian integers (a Gaussian
    integer is a complex number x+yi for which both x and
    y are
    integers):

    class Gaussian(object):
    """class representing Gaussian integers"""

    def __init__(self, x, y = 0):
    self.real, self.imag = x, y

    def __repr__(self):
    return repr(self.real) + "+" + repr(self.imag)
    + "*i"

    def __add__(self, other):
    if type(other) != Gaussian:
    other = Gaussian(other)
    return Gaussian(self.real + other.real,
    self.imag + other.imag)

    def __mul__(self, other):
    if type(other) != Gaussian:
    other = Gaussian(other)
    return Gaussian(self.real * other.real -
    self.imag * other.imag, \
    self.real * other.imag +
    self.imag * other.real)

    Under Python 2.3.2 I get:

    >>> i = Gaussian(0, 1)
    >>> i * 3

    0+3*i
    >>> 3 * i # surprise!

    0+3*i
    >>> i + 3

    3+1*i
    >>> 3 + i

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    TypeError: unsupported operand type(s) for +: 'int'
    and 'Gaussian'

    In other words, I can *multiply* an int by a Gaussian
    in either order, but I can only *add* a Gaussian to an
    int, not the other way around. The surprise is that
    multiplying an int by a Gaussian works---I'd expect it
    to complain since there's no __rmul__ method defined,
    in just the same way that 3+i produced an exception
    above. Why do addition and multiplication behave
    differently?

    Yours hoping for enlightenment,

    Mark


    __________________________________
    Do you Yahoo!?
    The New Yahoo! Shopping - with improved product search
    http://shopping.yahoo.com
     
    Mark Dickinson, Oct 25, 2003
    #1
    1. Advertising

  2. "Mark Dickinson" <> wrote in message
    news:...
    > Can anyone either reproduce or explain the following
    > apparently inconsistent behaviours of __add__ and
    > __mul__? The class Gaussian provides a sub-bare-bones
    > implementation of Gaussian integers (a Gaussian
    > integer is a complex number x+yi for which both x and
    > y are
    > integers):
    >
    > class Gaussian(object):
    > """class representing Gaussian integers"""
    >
    > def __init__(self, x, y = 0):
    > self.real, self.imag = x, y
    >
    > def __repr__(self):
    > return repr(self.real) + "+" + repr(self.imag)
    > + "*i"
    >
    > def __add__(self, other):
    > if type(other) != Gaussian:
    > other = Gaussian(other)
    > return Gaussian(self.real + other.real,
    > self.imag + other.imag)
    >
    > def __mul__(self, other):
    > if type(other) != Gaussian:
    > other = Gaussian(other)
    > return Gaussian(self.real * other.real -
    > self.imag * other.imag, \
    > self.real * other.imag +
    > self.imag * other.real)
    >
    > Under Python 2.3.2 I get:
    >
    > >>> i = Gaussian(0, 1)
    > >>> i * 3

    > 0+3*i
    > >>> 3 * i # surprise!

    > 0+3*i
    > >>> i + 3

    > 3+1*i
    > >>> 3 + i

    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > TypeError: unsupported operand type(s) for +: 'int'
    > and 'Gaussian'
    >
    > In other words, I can *multiply* an int by a Gaussian
    > in either order, but I can only *add* a Gaussian to an
    > int, not the other way around. The surprise is that
    > multiplying an int by a Gaussian works---I'd expect it
    > to complain since there's no __rmul__ method defined,
    > in just the same way that 3+i produced an exception
    > above. Why do addition and multiplication behave
    > differently?


    I think new style classes create this, as old style classes exhibit the same
    behaviour for multiplication and division:

    class Test:
    def __init__(self, val):
    self.val = val
    def __add__(self, other):
    return self.val + other
    def __mul__(self, other):
    return self.val * other

    In typeobject.c, I find:

    SQSLOT("__add__", sq_concat, slot_sq_concat, wrap_binaryfunc,
    "x.__add__(y) <==> x+y"),
    SQSLOT("__mul__", sq_repeat, slot_sq_repeat, wrap_intargfunc,
    "x.__mul__(n) <==> x*n"),
    SQSLOT("__rmul__", sq_repeat, slot_sq_repeat, wrap_intargfunc,
    "x.__rmul__(n) <==> n*x"),

    My guess is that this explains why multiplication works and addition
    doesn't, as there is no corresponding SQSLOT("__radd__"... but adding

    SQSLOT("__radd__", sq_concat, slot_sq_concat, wrap_binaryfunc,
    "x.__radd__(y) <==> y+x"),

    and recompiling didn't fix the problem, so I'm either on the wrong track or
    missed a connection along the way.

    Emile van Sebille
     
    Emile van Sebille, Oct 25, 2003
    #2
    1. Advertising

  3. Mark Dickinson

    Bruce Wolk Guest

    Mark Dickinson wrote:
    > Can anyone either reproduce or explain the following
    > apparently inconsistent behaviours of __add__ and
    > __mul__? The class Gaussian provides a sub-bare-bones
    > implementation of Gaussian integers (a Gaussian
    > integer is a complex number x+yi for which both x and
    > y are
    > integers):
    >
    > class Gaussian(object):
    > """class representing Gaussian integers"""
    >
    > def __init__(self, x, y = 0):
    > self.real, self.imag = x, y
    >
    > def __repr__(self):
    > return repr(self.real) + "+" + repr(self.imag)
    > + "*i"
    >
    > def __add__(self, other):
    > if type(other) != Gaussian:
    > other = Gaussian(other)
    > return Gaussian(self.real + other.real,
    > self.imag + other.imag)
    >
    > def __mul__(self, other):
    > if type(other) != Gaussian:
    > other = Gaussian(other)
    > return Gaussian(self.real * other.real -
    > self.imag * other.imag, \
    > self.real * other.imag +
    > self.imag * other.real)
    >
    > Under Python 2.3.2 I get:
    >
    >
    >>>>i = Gaussian(0, 1)
    >>>>i * 3

    >
    > 0+3*i
    >
    >>>>3 * i # surprise!

    >
    > 0+3*i
    >
    >>>>i + 3

    >
    > 3+1*i
    >
    >>>>3 + i

    >
    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > TypeError: unsupported operand type(s) for +: 'int'
    > and 'Gaussian'
    >
    > In other words, I can *multiply* an int by a Gaussian
    > in either order, but I can only *add* a Gaussian to an
    > int, not the other way around. The surprise is that
    > multiplying an int by a Gaussian works---I'd expect it
    > to complain since there's no __rmul__ method defined,
    > in just the same way that 3+i produced an exception
    > above. Why do addition and multiplication behave
    > differently?
    >
    > Yours hoping for enlightenment,
    >
    > Mark
    >
    >
    > __________________________________
    > Do you Yahoo!?
    > The New Yahoo! Shopping - with improved product search
    > http://shopping.yahoo.com
    >

    3*i gives the expected error under Python 2.2.2. Curious indeed.
     
    Bruce Wolk, Oct 25, 2003
    #3
  4. Mark Dickinson

    John Roth Guest

    "Mark Dickinson" <> wrote in message
    news:...
    > Can anyone either reproduce or explain the following
    > apparently inconsistent behaviours of __add__ and
    > __mul__? The class Gaussian provides a sub-bare-bones
    > implementation of Gaussian integers (a Gaussian
    > integer is a complex number x+yi for which both x and
    > y are
    > integers):
    >
    > class Gaussian(object):
    > """class representing Gaussian integers"""
    >
    > def __init__(self, x, y = 0):
    > self.real, self.imag = x, y
    >
    > def __repr__(self):
    > return repr(self.real) + "+" + repr(self.imag)
    > + "*i"
    >
    > def __add__(self, other):
    > if type(other) != Gaussian:
    > other = Gaussian(other)
    > return Gaussian(self.real + other.real,
    > self.imag + other.imag)
    >
    > def __mul__(self, other):
    > if type(other) != Gaussian:
    > other = Gaussian(other)
    > return Gaussian(self.real * other.real -
    > self.imag * other.imag, \
    > self.real * other.imag +
    > self.imag * other.real)
    >
    > Under Python 2.3.2 I get:
    >
    > >>> i = Gaussian(0, 1)
    > >>> i * 3

    > 0+3*i
    > >>> 3 * i # surprise!

    > 0+3*i
    > >>> i + 3

    > 3+1*i
    > >>> 3 + i

    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > TypeError: unsupported operand type(s) for +: 'int'
    > and 'Gaussian'
    >
    > In other words, I can *multiply* an int by a Gaussian
    > in either order, but I can only *add* a Gaussian to an
    > int, not the other way around. The surprise is that
    > multiplying an int by a Gaussian works---I'd expect it
    > to complain since there's no __rmul__ method defined,
    > in just the same way that 3+i produced an exception
    > above. Why do addition and multiplication behave
    > differently?
    >
    > Yours hoping for enlightenment,
    >
    > Mark


    I vaguely remember a discussion a while back about
    getting rid of the 'r' operators. I don't remember what
    ever came of it, though, and I agree that the inconsistent
    treatment of __mul__ and __add__ is strange. I suspect
    something may have gotten lost in the boolean implementation.

    The Python Reference Manual doesn't say anything
    about it, though.

    John Roth



    >
    >
    > __________________________________
    > Do you Yahoo!?
    > The New Yahoo! Shopping - with improved product search
    > http://shopping.yahoo.com
    >
     
    John Roth, Oct 25, 2003
    #4
  5. John Roth wrote:
    ...
    > "Mark Dickinson" <> wrote in message

    ...
    > I vaguely remember a discussion a while back about
    > getting rid of the 'r' operators. I don't remember what
    > ever came of it, though, and I agree that the inconsistent
    > treatment of __mul__ and __add__ is strange. I suspect
    > something may have gotten lost in the boolean implementation.


    It's not that -- it's a different bug (good catch guys!).

    Objects/typeobject.c takes BOTH __mul__ AND __rmul__ as implying
    a "sq_repeat" (sequence repeat) slot, and then function
    Objects/abstract.c checks both operand for sq_repeat slots --
    thus, it will (erroneously) tread a __mul__ AS IF it was an
    __rmul__... BUT only when multiplying by an integer, because
    that's what sequence-repeat involves.

    Try 3.0 * i rather than 3 * i and you'll see it correctly
    raise a TypeError (although the msg is a good hint that the
    bug is still at work: "can't multiply sequence to non-int"
    rather than "unsupported operand type(s)").


    Unfortunately the fix (for Python 2.3.3) isn't as obvious to
    me as the diagnosis. Anyway, I've reported this as a bug on
    SF, its "Request ID" is 830261, and I hope I'll get advice
    from somebody to whom the fix _is_ obvious:).


    Again, thanks for the bug report!


    Alex
     
    Alex Martelli, Oct 25, 2003
    #5
  6. Emile van Sebille wrote:
    ...
    > In typeobject.c, I find:
    >
    > SQSLOT("__add__", sq_concat, slot_sq_concat, wrap_binaryfunc,
    > "x.__add__(y) <==> x+y"),
    > SQSLOT("__mul__", sq_repeat, slot_sq_repeat, wrap_intargfunc,
    > "x.__mul__(n) <==> x*n"),
    > SQSLOT("__rmul__", sq_repeat, slot_sq_repeat, wrap_intargfunc,
    > "x.__rmul__(n) <==> n*x"),
    >
    > My guess is that this explains why multiplication works and addition
    > doesn't, as there is no corresponding SQSLOT("__radd__"... but adding


    Right (though incomplete).

    > SQSLOT("__radd__", sq_concat, slot_sq_concat, wrap_binaryfunc,
    > "x.__radd__(y) <==> y+x"),
    >
    > and recompiling didn't fix the problem, so I'm either on the wrong track
    > or missed a connection along the way.


    Yep: the fact that PyNumber_Add only tries the concatenate-sequences
    route if the LEFT operand has a sq_concat slot -- it needs not try
    BOTH operands for the purpose, as PyNumber_Multiply, alas, must
    (because seq*int and int*seq are both valid). Both functions are in
    Objects/abstract.c , btw.

    Unfortunately, even with this complete diagnosis, the fix is NOT
    obvious to me. Anyway, I've opened bug #830261 to see if I can
    get advice from somebody to which the fix IS obvious (btw, the fix
    involves NOT having 3*i succeed and 3.0*i fail with a weird msg --
    it's not acceptable to "fix" by making 3+i succeed and 3.0+i fail
    weirdly...:).


    Alex
     
    Alex Martelli, Oct 25, 2003
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael Bader
    Replies:
    11
    Views:
    12,052
    Michael Bader
    Mar 3, 2004
  2. Replies:
    7
    Views:
    1,085
    Steven Bethard
    May 26, 2005
  3. robin

    addition puzzle

    robin, Sep 17, 2007, in forum: C++
    Replies:
    5
    Views:
    389
    Juha Nieminen
    Sep 18, 2007
  4. rockerboy

    Addition and multiplication

    rockerboy, Dec 5, 2007, in forum: VHDL
    Replies:
    0
    Views:
    456
    rockerboy
    Dec 5, 2007
  5. William Hughes
    Replies:
    13
    Views:
    1,220
    Ben Bacarisse
    Mar 15, 2010
Loading...

Share This Page