python operator overloading __radd__ and __add__ - python

I'm currently learning python operator overloading (__radd__ and __add__ to be exact) and I have the following code
class Commuter1:
def __init__(self, val):
self.val = val
def __add__(self, other):
print('add', self.val, other)
return self.val + other
def __radd__(self, other):
print('radd', self.val, other)
return other + self.val
x = Commuter1(88)
y = Commuter1(99)
print(x + y)
I have got the following result
When used separately, I understand how __radd__ and __add__ works. But for the line x + y, I'm not sure why both __radd__ and __add__ methods are evoked.

First, Python looks at the types of x and y to decide whether to call x.__add__ or y.__radd__. Since they're both the same type Commuter1, it tries x.__add__ first.
Then, inside your __add__ method, you do this:
return self.val + other
So, Python looks at the types of self.val and other to decide whether to call self.val.__add__ or other.__radd__. Since they're unrelated types int and Commuter1, it tries int.__add__ first.
But int.__add__ returns NotImplemented for a type it doesn't know about, so Python falls back to calling other.__radd__.
Inside your __radd__ method, you do this:
return other + self.val
So, Python looks at the types of other and self.val to decide whether to call other.__add__ or self.val.__radd__. Since they both the same type int, it tries __add__ first.
And of course int.__add__ works on another int, so it returns a value for the inner + inside your __radd__, which you return, which returns a value for the + inside __add__, which you return, which returns a value for the top-level +, which you print.

Related

Determine short operators (+= like) usage in descriptor

I am now trying to create a descriptor class for model fields which saves it's modification history.
I can determine the fact when some method is called on field value by just overriding getattr:
def __getattr__(self, attr):
print(attr)
return super().__getattr__(attr)
And I can see arguments of overrided methods:
def __add__(self, other):
print(self, other)
return super().__add__(other)
The problem is that += operator is just a syntactic sugar for:
foo = foo + other
So I can not handle += as single method call, it triggers __add__ and then __set__. Am I able to determine that value was not totally replaced with new one, but was added/multiplied/divided etc.?
Use __iadd__
For instance, if x is an instance of a class with an __iadd__() method, x += y is equivalent to x = x.__iadd__(y) . Otherwise, x.__add__(y) and y.__radd__(x) are considered, as with the evaluation of x + y.

Overloading operators for operands of different types in Python

Consider the following example of a 'wrapper' class to represent vectors:
class Vector:
def __init__(self, value):
self._vals = value.copy()
def __add__(self, other):
if isinstance(other, list):
result = [x+y for (x, y) in zip(self._vals, other)]
elif isinstance(other, Vector):
result = [x+y for (x, y) in zip(self._vals, other._vals)]
else:
# assume other is scalar
result = [x+other for x in self._vals]
return Vector(result)
def __str__(self):
return str(self._vals)
The __add__ method takes care of adding two vectors as well as adding a vector with a scalar. However, the second case is not complete as the following examples show:
>>> a = Vector([1.2, 3, 4])
>>> print(a)
[1.2, 3, 4]
>>> print(a+a)
[2.4, 6, 8]
>>> print(a+5)
[6.2, 8, 9]
>>> print(5+a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'Vector'
To my understanding the reason is that the overloaded operator only tells Python what to do when it sees a + x where a is an instance of Vector, but there is no indication of what to do for x + a (with a an instance of Vector and x a scalar).
How one should overload the operators in such circumstances to cover all cases (i.e., to support the case that self is not an instance of Vector but other is)?
Ok. I guess I found the answer: one has to overload __radd__ operator as well:
class Vector:
def __init__(self, value):
self._vals = value.copy()
def __add__(self, other):
if isinstance(other, list):
result = [x+y for (x, y) in zip(self._vals, other)]
elif isinstance(other, Vector):
result = [x+y for (x, y) in zip(self._vals, other._vals)]
else:
# assume other is scalar
result = [x+other for x in self._vals]
return Vector(result)
def __radd__(self, other):
return self + other
def __str__(self):
return str(self._vals)
Although to me this looks a bit redundant. (Why Python does not use the commutativity of addition by default, assuming __radd__(self, other) always returns self + other? Of course for special cases the user can override __radd__.)
You could define a Scalar class that has int as its base class.
Then override __add__ to do what you want.
class Scalar(int):
def __add__(self):
# do stuff
You already figured out you need to implement __radd__. This is an answer as to why this is so, and why you need to do this in addition to implementing __add__, as a Both quotes are taken from Python Docs (Data Model - 3.3.8 Emulating numeric types), starting with the obvious:
These methods are called to implement the binary arithmetic operations (+, -, *, #, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |). For instance, to evaluate the expression x + y, where x is an instance of a class that has an __add__() method, x.__add__(y) is called.
So order determines which object's implementation of __add__ is called. When the method doesn't support the operation with the passed argument NotImplemented should be returned. That's where the so-called "reflected methods" come into play:
These functions are only called if the left operand does not support the corresponding operation and the operands are of different types. 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 [sic].
Now, why wouldn't __radd__(self, other) just fall back to __add__(self, other)? While ring addition is always commutative (see this and this math.stackexchange answers), you could have algebraic structures that are do not satisfy this assumption (e.g., near-rings). But my guess as a non-mathematician would be that it's just desirable to have a consistent data model across different numerical methods. While addition might be commonly commutative, multiplication is less so. (Think matrices and vectors! Although, admittedly this is not the best example, given __matmul__). I also prefer to see there being no exceptions, especially if I had to read about rings, etc. in a language documentation.

Overriding Commutative Operations For Same Class Operands in Python

I am trying to understand how operator overriding works for two operands of a custom class.
For instance, suppose I have the following:
class Adder:
def __init__(self, value=1):
self.data = value
def __add__(self,other):
print('using __add__()')
return self.data + other
def __radd__(self,other):
print('using __radd__()')
return other + self.data
I initialize the following variables:
x = Adder(5)
y = Adder(4)
And then proceed to do the following operations:
1 + x
using __radd__()
Out[108]: 6
x + 2
using __add__()
Out[109]: 7
The two operations above seem straigtforward. If a member of my custom class is to the right of the "+" in the addition expression, then __radd__ is used. If it is on the left, then __add__ is used. This works for expressions when one operand is of the Adder type and another one is something else.
When I do this, however, I get the following result:
x + y
using __add__()
using __radd__()
Out[110]: 9
As you can see, if both operands are of the custom class, then both __add__ and __radd__ are called.
My question is how does Python unravel this situation and how is it able to call both the right-hand-addition function, as well as the left-hand-addition function.
It's because inside your methods you add the data to other. This is itself an instance of Adder. So the logic goes:
call __add__ on x;
add x.data (an int) to y (an Adder instance)
ah, right-hand operand is an instance with a __radd__ method, so
call __radd__ on y;
add int to y.data (another int).
Usually you would check to see if other was an instance of your class, and if so add other.data rather than just other.
That's the because the implementation of your __add__ and __radd__ method do not give any special treatment to the instances of the Adder class. Therefore, each __add__ call leads to an integer plus Adder instance operation which further requires __radd__ due to the Adder instance on the right side.
You can resolve this by doing:
def __add__(self, other):
print('using __add__()')
if isinstance(other, Adder):
other = other.data
return self.data + other
def __radd__(self, other):
print('using __radd__()')
return self.__add__(other)

Implicit conversions in Python

For example, if I wanted to apply mathematical operations on objects in the following way:
class A(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return value
assert(A(1) + A(2) == 3)
I am getting the following error: TypeError: unsupported operand type(s) for +: 'A' and 'A'
Is it possible to evaluate objects to primitives so that I can apply simple operations on them? Similarly how you could use implicit conversions in Scala.
You can implement __add__ to define addition on your class.
class A(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return 'A(%r)'%self.value
def __add__(self, other):
return A(self.value+other.value)
>>> A(1)+A(2)
A(3)
This implementation assumes that you are only trying to add instances of A to other instances of A to get a third instance of A. You can write an __add__ adaptable to what type of operand you need it to work for.
See also __radd__ and __iadd__.
That depends on what you're trying to do. You can define the + operator by defining the __add__ method:
class A(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return value
def __add__(self, other):
return A(self.value + other.value)
then of course in your example code you're trying to compare it to an integer which also need to be defined - which is done by implementing the __eq__ method:
def __eq__(self, other):
try:
self.value == other.value
except AttributeError: # other wasn't of class A, try to compare directly instead
return self.value == other
(implicit typecasts on the other hand is not available as far as I know)
The problem is that there isn't enough context in the expression to decide what the objects should be converted to. Python has various methods that can be defined on an object that implement various operators, including the __add__() and __radd__() methods.
There isn't enough context to know that foo should be equivalent to foo.value, so with Python's philosophy explicit is better than implicit. You can certainly subclass int, but then the operators won't produce your new class, and the object itself would remain immutable (as numbers in Python generally are). Notably, ctypes such as c_int32 have a value attribute like your example but do not implement numeric operators.

Override + operator in python for float + obj

I have a class Vec3D (see http://pastebin.com/9Y7YbCZq)
Currently, I allow Vec3D(1,0,0) + 1.2 but I'm wondering how I should proceed to overload the + operator in such a way that I get the following output:
>>> 3.3 + Vec3D(1,0,0)
[4.3, 3.3 , 3.3]
Code is not required, but just a hint in which direction I should look. Something general will be more useful than a specific implementation as I need to implement the same thing for multiplication, subtraction etc.
You're looking for __radd__:
class MyClass(object):
def __init__(self, value):
self.value = value
def __radd__(self, other):
print other, "radd", self.value
return self.value + other
my = MyClass(1)
print 1 + my
# 1 radd 1
# 2
If the object on the left of the addition doesn't support adding the object on the right, the object on the right is checked for the __radd__ magic method.
You want to use the __add__ (and possibly __radd__ and __iadd__) methods. Check out http://docs.python.org/reference/datamodel.html#object.__add__ for more details.
implement __radd__ . When you call 3.3 + Vec3D(1,0,0), as long as float doesn't have method __add__(y) with y being Vec3D, your reflected version __radd__ will be called.

Categories

Resources