views:

121

answers:

4

I have never handled reverse operators before, so please no flaming! Just finished learning about them so wanted to try them out. But for some reason, it is not working. Here is the code:

>>> class Subtract(object):
    def __init__(self, number):
        self.number = number
    def __rsub__(self, other):
        return self.number - other.number


>>> x = Subtract(5)
>>> y = Subtract(10)
>>> x - y         # FAILS!

Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    x - y
TypeError: unsupported operand type(s) for -: 'Subtract' and 'Subtract'
>>> x.__rsub__(y) # WORKS!
-5

If I change __rsub__ to __sub__, it works.

What am I doing wrong? Also what is the purpose of these reverse operators?

+2  A: 

__rsub__() will only be called if the operands are of different types; when they're of the same type it's assumed that if __sub__ isn't present they can't be subtracted.

Also note that your logic is reversed in any case; you're returning self - other instead of other - self

Wooble
But how come explicitly calling `__rsub__` works?
Randy
Because `__rsub__` is a function you defined and you can call it like any other function; it just won't be automatically called to implement the `-` operator.
Wooble
@Randy: Because when you call it explicitly, it's just a regular method. Why would you expect it not to work? The only thing special about `__rsub__` is that under certain circumstances, Python will translate a subtraction operation `<something> - <something else>` into a call to `__rsub__` - but as explained in the answers, those certain circumstances are not present in your example.
David Zaslavsky
+1  A: 

The methods with reflected operands are provided so that your class can implement an operator when the left operand is a primitive or something else that isn't under your control:

These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.

Since they're both of the same type, it's cowardly refusing and you should implement the __sub__ method.

jleedev
+2  A: 

From Python's Data model at http://docs.python.org/reference/datamodel.html :

These methods are called to implement the binary arithmetic operations (+, -, , /, %, divmod(), pow(), *, <<, >>, &, ^, |) with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation and the operands are of different types. [2] For instance, to evaluate the expression x - y, where y is an instance of a class that has an __rsub__() method, y.__rsub__(x) is called if x.__sub__(y) returns NotImplemented.

However - both objects must not be of the same class - that means, that even if you put a __sub__ method returning NotImplemented on your example above, you will still get the same error: Python just assumes your Subtract class can't subtract from "Subtract" iobjects, no matter the order.

However, this works:

class Sub1(object):
    number = 5
    def __sub__(self, other):
        return NotImplemented

class Sub2(object):
    number = 2
    def __rsub__(self, other):
        return other.number - self.number


a = Sub1()
b = Sub2()

print a - b
jsbueno
+2  A: 

The point of these methods is to allow this:

class MyNumber(object):
    def __init__(self, x):
        self.x = x

print 10 - MyNumber(9) # fails because 10.__sub__(MyNumber(9)) is unknown

class MyFixedNumber(MyNumber):
    def __rsub__(self, other):
        return MyNumber( other - self.x )

print 10 - MyFixedNumber(9) # MyFixedNumber(9).__rsub__(10) is defined

Very rarely useful though, usually you just use things of the same type and the direct __sub__

THC4k