Override + operator in python for float + obj - python

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.

Related

python operator overloading __radd__ and __add__

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.

adding class elements to list elements [duplicate]

I am trying to understand how __add__ works:
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __str__(self):
return str(self.num)
If I put them in a list
d=[MyNum(i) for i in range(10)]
this works
t=MyNum(0)
for n in d:
t=t+n
print t
But this does not:
print sum(d)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
What am I doing wrong? How can I get the sum() to work?
My problem is how to use the sum on a list of objects that support the __add__, need to keep it as generic as possible.
You need to define __radd__ as well to get this to work.
__radd__ is reverse add. When Python tries to evaluate x + y it first attempts to call x.__add__(y). If this fails then it falls back to y.__radd__(x).
This allows you to override addition by only touching one class. Consider for example how Python would have to evaluate 0 + x. A call to 0.__add__(x) is attempted but int knows nothing about your class. You can't very well change the __add__ method in int, hence the need for __radd__. I suppose it is a form of dependency inversion.
As Steven pointed out, sum operates in place, but starts from 0. So the very first addition is the only one that would need to use __radd__. As a nice exercise you could check that this was the case!
>>> help(sum)
Help on built-in function sum in module __builtin__:
sum(...)
sum(sequence[, start]) -> value
Returns the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the sequence is
empty, returns start.
In other words, provide a start value:
sum(d, MyNum(0))
Edit pasted from my below comment:
sum works with a default start value of the integer zero. Your MyNum class as written does not know how to add itself to integers. To solve this you have two options. Either you can provide a start value to sum that has the same type as you class, or you can implement __radd__, which Python calls when adding values of differing types (such as when the first value in d is added to the default start value of zero).
I oppose relaying on sum() with a start point, the loop hole exposed below,
In [51]: x = sum(d, MyNum(2))
In [52]: x.num
Out[52]: 47
Wondering why you got 47 while you are expecting like
…start from 2nd of MyNum() while leaving first and add them till end, so the expected result = 44 (sum(range(2,10))
The truth here is that 2 is not kept as start object/position but instead treated as an addition to the result
sum(range(10)) + 2
oops, link broken !!!!!!
Use radd
Here below the correct code. Also note the below
Python calls __radd__ only when the object on the right side of the + is your class instance
eg: 2 + obj1
#!/usr/bin/env python
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __radd__(self,other):
return MyNum(self.num+other)
def __str__(self):
return str(self.num)
d=[MyNum(i) for i in range(10)]
print sum(d) ## Prints 45
d=[MyNum(i) for i in range(2, 10)]
print sum(d) ## Prints 44
print sum(d,MyNum(2)) ## Prints 46 - adding 2 to the last value (44+2)
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return self.num += other.num
def __str__(self):
return str(self.num)
one = MyNum(1)
two = MyNum(2)
one + two
print(two.num)
Another option is reduce (functools.reduce in Python 3.x).
from functools import reduce
from operators import add
d=[MyNum(i) for i in range(10)]
my_sum = reduce(add,d)

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.

TypeError after overriding the __add__ method

I am trying to understand how __add__ works:
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __str__(self):
return str(self.num)
If I put them in a list
d=[MyNum(i) for i in range(10)]
this works
t=MyNum(0)
for n in d:
t=t+n
print t
But this does not:
print sum(d)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
What am I doing wrong? How can I get the sum() to work?
My problem is how to use the sum on a list of objects that support the __add__, need to keep it as generic as possible.
You need to define __radd__ as well to get this to work.
__radd__ is reverse add. When Python tries to evaluate x + y it first attempts to call x.__add__(y). If this fails then it falls back to y.__radd__(x).
This allows you to override addition by only touching one class. Consider for example how Python would have to evaluate 0 + x. A call to 0.__add__(x) is attempted but int knows nothing about your class. You can't very well change the __add__ method in int, hence the need for __radd__. I suppose it is a form of dependency inversion.
As Steven pointed out, sum operates in place, but starts from 0. So the very first addition is the only one that would need to use __radd__. As a nice exercise you could check that this was the case!
>>> help(sum)
Help on built-in function sum in module __builtin__:
sum(...)
sum(sequence[, start]) -> value
Returns the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the sequence is
empty, returns start.
In other words, provide a start value:
sum(d, MyNum(0))
Edit pasted from my below comment:
sum works with a default start value of the integer zero. Your MyNum class as written does not know how to add itself to integers. To solve this you have two options. Either you can provide a start value to sum that has the same type as you class, or you can implement __radd__, which Python calls when adding values of differing types (such as when the first value in d is added to the default start value of zero).
I oppose relaying on sum() with a start point, the loop hole exposed below,
In [51]: x = sum(d, MyNum(2))
In [52]: x.num
Out[52]: 47
Wondering why you got 47 while you are expecting like
…start from 2nd of MyNum() while leaving first and add them till end, so the expected result = 44 (sum(range(2,10))
The truth here is that 2 is not kept as start object/position but instead treated as an addition to the result
sum(range(10)) + 2
oops, link broken !!!!!!
Use radd
Here below the correct code. Also note the below
Python calls __radd__ only when the object on the right side of the + is your class instance
eg: 2 + obj1
#!/usr/bin/env python
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __radd__(self,other):
return MyNum(self.num+other)
def __str__(self):
return str(self.num)
d=[MyNum(i) for i in range(10)]
print sum(d) ## Prints 45
d=[MyNum(i) for i in range(2, 10)]
print sum(d) ## Prints 44
print sum(d,MyNum(2)) ## Prints 46 - adding 2 to the last value (44+2)
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return self.num += other.num
def __str__(self):
return str(self.num)
one = MyNum(1)
two = MyNum(2)
one + two
print(two.num)
Another option is reduce (functools.reduce in Python 3.x).
from functools import reduce
from operators import add
d=[MyNum(i) for i in range(10)]
my_sum = reduce(add,d)

Categories

Resources