Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

addition between classes using radd method

Tags:

python-3.x

I have two different classes, and I want to define the addition of them only in one class, and define both __add__ and __radd__ for that class (in my example below, that's ExampleClass2. I DO NOT want to create an __add__ method that works for ExampleClass1 to add ExampleClass2.

As it is right now it just ignores it. I also tried with raising error, but that didn't work either.

class ExampleClass1:
    def __init__(self, data):
        self.data = data
        
    def __add__(self, other):
        if isinstance(other, int):
            print('other was an int')
        
        
class ExampleClass2:
    def __init__(self, data):
        self.data = data
        
    def __add__(self, other):
        if isinstance(other, ExampleClass1):
            print("it's working")
            
    __radd__ = __add__
    
a = ExampleClass1('q')

b = ExampleClass2('w')

a+b
like image 543
user1187139 Avatar asked Feb 03 '12 09:02

user1187139


3 Answers

__radd__ is only called if the left object does not have an __add__ method, or that method does not know how to add the two objects (which it flags by returning NotImplemented). Both classes have an __add__ method, which do not return NotImplemented. Therefore the __radd__ method would never be called.

like image 156
Lennart Regebro Avatar answered Sep 30 '22 21:09

Lennart Regebro


Suppose you are implementing a class that you want to act like a number via operator overloading. So you implement add in your class, and now expressions like myobj + 4 can work as you want and yield some result. This is because myobj + 4 is interpreted as myobj.__add__(4), and your custom method can do whatever it means to add 4 to your custom class.

However, what about an expression like 4 + myobj which is really (4).__add__(myobj)? The 4 is an instance of a Python built-in type and its add method doesn't know anything about your new type, so it will return a special value NotImplemented. (The interpreter recognizes this special value coming from add and raises a TypeError exception which kills your program, which is the behavior you'd actually see, rather than the special value being returned.)

It would suck for operator overloading if myobj + 4 was valid but 4 + myobj was invalid. That's arbitrary and restrictive — addition is supposed to be commutative. Enter __radd__. Python will first try (4).__add__(myobj), and if that returns NotImplemented Python will check if the right-hand operand implements radd, and if it does, it will call myobj.__radd__(4) rather than raising a TypeError. And now everything can proceed as usual, as your class can handle the case and implement your behavior, rather than the built-in type's add which is fixed and doesn't know about your class.

Example:

class X:
    
    def __init__(self, num):
        self.num = num

    def __str__(self):
        return str(self.num)

    def __add__(self, other):
        return self.num + other.num
    
    __radd__ = __add__


class Y:

    def __init__(self, num):
        self.num = num

    def __str__(self):
        return str(self.num)


x = X(5)
y = Y(10)

print(x+y)

print(y+x)
like image 30
mubix Avatar answered Sep 30 '22 21:09

mubix


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

class X:
  def __init__(self, num):
    self.num = num

class Y:
  def __init__(self, num):
    self.num = num

  def __radd__(self, other_obj):
    return Y(self.num+other_obj.num)

  def __str__(self):
    return str(self.num)

>>> x = X(2)
>>> y = Y(3)
>>> print(x+y)
5
>>>
>>> print(y+x)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-60-9d7469decd6e> in <module>()
----> 1 print(y+x)

TypeError: unsupported operand type(s) for +: 'Y' and 'X'
like image 41
Adil Abbasi Avatar answered Sep 30 '22 21:09

Adil Abbasi