I recently learned about operator overloading in python and I would like to know if the following is possible.
Consider the following hypothetical/contrived class.
class My_Num(object):
def __init__(self, val):
self.val = val
def __add__(self, other_num):
if isinstance(other_num, My_Num):
return self.val + other_num.val
else:
return self.val + other_num
I know that the way that's written above, I can do things like this
n1 = My_Num(1)
n2 = My_Num(2)
n3 = 3
print n1 + n2
print n1 + n3
and those will work as expected. I also know that the way it's currently written I can't do this
n1 = My_Num(1)
n2 = 2
print n2 + n1
Is there any way around this? I know this example is contrived but I have an application in which it would be very useful if when I did operator overloading, the class for which I define the operator can appear on the right hand side of operator. Is this possible in python?
Yes. For example, there is __radd__. Also, there are none for __le__(), __ge__(), etc., but as Joel Cornett rightly observes, if you define only __lt__, a > b calls the __lt__ function of b, which provides a workaround.
>>> class My_Num(object):
... def __init__(self, val):
... self.val = val
... def __radd__(self, other_num):
... if isinstance(other_num, My_Num):
... return self.val + other_num.val
... else:
... return self.val + other_num
...
>>> n1 = My_Num(1)
>>> n2 = 3
>>>
>>> print n2 + n1
4
>>> print n1 + n2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'My_Num' and 'int'
Note that in at least some cases it's reasonable to do something like this:
>>> class My_Num(object):
... def __init__(self, val):
... self.val = val
... def __add__(self, other_num):
... if isinstance(other_num, My_Num):
... return self.val + other_num.val
... else:
... return self.val + other_num
... __radd__ = __add__
You have to overload the __radd__ method (right-side addition). Your function should look pretty much the same as your __add__ method, e.g.:
def __radd__(self, other):
return self.val + other.val
Related
I have a class it can successfully add two variables of object of class a
class a():
def __add__(self, other):
return self.val+other.val
def __init__(self,a):
self.val=a
aa=a(22)
bb=a(11)
aa+bb
33
But when I try to give it third object to add, it through error
cc=a(11)
aa+bb+cc
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
aa+bb+cc
TypeError: unsupported operand type(s) for +: 'int' and 'a'
It is because first two aa+bb return int and its add function is design to add object addition
Any one can suggest how I can add three objects
I find this link Using __add__ operator with multiple arguments in Python but it is working on one object and 2 integers. But I want to add three objects. and all these three combine and return integer
__add__ must return an instance of a class, not int
class a():
def __add__(self, other):
return a(self.val + other.val)
def __init__(self, a):
self.val = a
Here's an example of how __add__ and __radd__ should be implemented.
We have a class A that has an attribute n which is an integer. We want to be able to add classes of the same type and we also want to be able to add integer values. Therefore:
class A:
def __init__(self, n):
self._n = n
#property
def n(self):
return self._n
def __add__ (self, other):
if isinstance(other, int):
return A(self.n + other)
assert isinstance(other, type(self))
return A(self.n + other.n)
def __radd__(self, other):
assert isinstance(other, int)
return A(self.n + other)
def __str__(self):
return f'{self.n}'
def __repr__(self):
return f'{self.n=}'
a = A(1)
b = A(2)
c = A(3)
print(10+a+10+b+10+c+10)
c += 5
print(c)
print(c.n)
Output:
46
8
8
This is what I looking for
class a():
def __add__(self, other):
return a(self.val+other.val)
def __init__(self,a):
self.val=a
aa=a(22)
bb=a(11)
cc=a(11)
d=aa+bb+cc
print(d.val)
Out put
44
I'm curious as to how to support mixed arithmetic using a user-defined class in python. The docs state
Python fully supports mixed arithmetic: when a binary arithmetic
operator has operands of different numeric types, the operand with the
“narrower” type is widened to that of the other, where integer is
narrower than floating point, which is narrower than complex.
The behavior I am trying to recreate can be seen with numpy
import numpy as np
a = np.array([1,2,3])
a + 5
Out[3]: array([6, 7, 8])
5 + a
Out[4]: array([6, 7, 8])
If I try to do this with a user defined class, I get something like this
from decimal import Decimal
class MyType:
def __init__(self, value):
self.value = Decimal(str(value))
def __repr__(self):
return f'<MyType {self.value}>'
def __add__(self, other):
if not isinstance(other, MyType):
other = MyType(other)
return MyType(self.value + other.value)
Then attempting to do something similar gives an error when the first addition argument is a float instead of my class.
a = MyType(.1)
a + 5
Out[14]: <MyType 5.1>
5 + a
Traceback (most recent call last):
File "<ipython-input-15-35e25b55bb62>", line 1, in <module>
5 + a
TypeError: unsupported operand type(s) for +: 'int' and 'MyType'
In python you can define both __add__ and __radd__, see https://docs.python.org/3/reference/datamodel.html#object.radd
So the easiest thing in your case to do is to add __radd__ = __add__
from decimal import Decimal
class MyType:
def __init__(self, value):
self.value = Decimal(str(value))
def __repr__(self):
return f'<MyType {self.value}>'
def __add__(self, other):
if not isinstance(other, MyType):
other = MyType(other)
return MyType(self.value + other.value)
__radd__ = __add__
Seems something like link.
from decimal import Decimal
class MyType:
def __init__(self, value):
self.value = Decimal(str(value))
def __repr__(self):
return f'<MyType {self.value}>'
def __add__(self, other):
if not isinstance(other, MyType):
other = MyType(other)
return MyType(self.value + other.value)
def __radd__(self, other):
if not isinstance(other, MyType):
other = MyType(other)
return MyType(self.value + other.value)
I've been subclassing tuple or using namedtuple blissfully for a few years, but now I have a use case where I need a class that can be used as a weak referent. And today I learned tuples don't support weak references.
Is there another way to create an immutable object in Python with a fixed set of attributes? I don't need the numeric indexing or variable width of a tuple.
class SimpleThingWithMethods(object):
def __init__(self, n, x):
# I just need to store n and x as read-only attributes
... ??? ...
I guess this raises the obvious question of why immutable; "Pythonic" code usually just assumes we're all adults here and no one in their right mind would reach into a class and muck with its values if it risks ruining the class invariants. In my case I have a class in a library and I am worried about accidental modification of objects by end-users. The people I work with sometimes make incorrect assumptions about my code and start doing things I did not expect, so it's much cleaner if I can raise an error if they accidentally modify my code.
I'm not so worried about bulletproof immutability; if someone really nefarious wants to go and modify things, ok, fine, they're on their own. I just want to make it hard to accidentally modify my objects.
well, this isn't a great answer but it looks like I can modify the answer in https://stackoverflow.com/a/4828492/44330 --- essentially overriding __setattr__ and __delattr__ to meet my needs at least against accidental modification. (but not as nice as subclassing tuple)
class Point(object):
__slots__ = ('x','y','__weakref__')
def __init__(self, x, y):
object.__setattr__(self, "x", x)
object.__setattr__(self, "y", y)
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return self.x.__hash__() * 31 + self.y.__hash__()
Implementing #Elazar's idea:
class Point(object):
__slots__ = ('x','y','__weakref__')
def __new__(cls, x, y):
thing = object.__new__(cls)
object.__setattr__(thing, "x", x)
object.__setattr__(thing, "y", y)
return thing
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return self.x.__hash__() * 31 + self.y.__hash__()
If you don't worry about isinstance checks, you can strengthen you answer:
def Point(x, y):
class Point(object):
__slots__ = ('x','y','__weakref__')
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return x == other.x and y == other.y
def __hash__(self):
return x.__hash__() * 31 + y.__hash__()
p = Point()
object.__setattr__(p, "x", x)
object.__setattr__(p, "y", y)
return p
I don't really recommend it (every invocation creates a class!), just wanted to note the possibility.
It is also possible to go javascript all the way, and supply __getattr__ that will access the local variables. But that will also slow down access, in addition to creation. Now we don't need these slots at all:
class MetaImmutable:
def __setattr__(self, name, val):
raise TypeError
def Point(x, y):
class Point(object):
__metaclass__ = MetaImmutable
__slots__ = ('__weakref__',)
def __getattr__(self, name):
if name == 'x': return x
if name == 'y': return y
raise TypeError
#property
def x(self): return x
#property
def y(self): return y
def __eq__(self, other):
return x == other.x and y == other.y
def __hash__(self):
return x.__hash__() * 31 + y.__hash__()
return Point()
Test it:
>>> p = Point(1, 2)
>>> p.y
2
>>> p.z
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __getattr__
TypeError
>>> p.z = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> object.__setattr__(p, 'z', 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> from weakref import ref
>>> ref(p)().x
1
>>> type(p).x = property(lambda self: 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __setattr__
TypeError
And finally, you can still break it:
>>> type.__setattr__(type(p), 'x', property(lambda self: 5))
>>> p.x
5
Again, nothing here is recommended. Use #Jasons implementation.
What about using encapsulation and abstraction on the parameter (getter?):
class SimpleThingWithMethods(object):
def __init__(self, n, x):
self._n = n
self._x = x
def x(self):
return self._x
def n(self):
return self._n
SimpleThingWithMethods(2,3).x()
=> 3
I have a problem with return self
class Fib:
def __init__(self, max):
self.max = max
def __iter__(self):
self.a = 0
self.b = 1
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
I have already seen this question return self problem but I can't understand what the benefit is of return self?
Returning self from a method simply means that your method returns a reference to the instance object on which it was called. This can sometimes be seen in use with object oriented APIs that are designed as a fluent interface that encourages method cascading. So, for example,
>>> class Counter(object):
... def __init__(self, start=1):
... self.val = start
... def increment(self):
... self.val += 1
... return self
... def decrement(self):
... self.val -= 1
... return self
...
>>> c = Counter()
Now we can use method cascading:
>>> c.increment().increment().decrement()
<__main__.Counter object at 0x1020c1390>
Notice, the last call to decrement() returned <__main__.Counter object at 0x1020c1390>, which is self.
Now:
>>> c.val
2
>>>
Notice, you cannot do this if you did not return self:
>>> class Counter(object):
... def __init__(self, start=1):
... self.val = start
... def increment(self):
... self.val += 1
... # implicitely return `None`
... def decrement(self):
... self.val -= 1
... # implicitely return `None`
...
>>> c = Counter()
>>> c.increment().increment()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'increment'
>>> c
<__main__.Counter object at 0x1020c15f8>
>>> c.val
2
>>>
Notice, not everyone is a fan of "method cascading" design. Python built-ins do not tend do this, so, list for example:
>>> x = list()
>>> x.append(1).append(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'append'
>>>
The one place you do often see this is when your class implements the iterator protocol, where iter on an iterator returns self by convention, although this is suggested by the docs:
Having seen the mechanics behind the iterator protocol, it is easy to
add iterator behavior to your classes. Define an __iter__() method
which returns an object with a __next__() method. If the class
defines __next__(), then __iter__() can just return self:
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
Notice, this in effect makes your iterator only useful for a single pass (as it should be to properly follow the iterator protocol):
>>> x = [1, 2, 3, 4]
>>> it = iter(x)
>>> list(it)
[1, 2, 3, 4]
>>> list(it)
[]
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
This is needlessly complex code. Pay little attention to it. There's no reason on earth to implement it this way.
That being said, what it does is this:
class Fib:
"""Implements the Fibonacci sequence."""
def __init__(self, max_):
self.max = max_
def __iter__(self):
"""Initializes and returns itself as an iterable."""
self.a = 0
self.b = 1
return self
def __next__(self):
"""What gets run on each execution of that iterable."""
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b # increment
return fib
This is all much easier to express as:
def fib(max_):
a, b = 0, 1
while b <= max_:
out = a
a, b = b, a+b
yield out
Examples:
>>> fib_obj = Fib(20)
>>> for n in fib_obj:
... print(n)
>>> for n in Fib(20):
... print(n)
>>> for n in fib(20):
... print(n)
# all give....
0
1
1
2
3
5
8
13
I've run into some confusing behaviour of the magic comparison methods.
Suppose we have the following class:
class MutNum(object):
def __init__ (self, val):
self.val = val
def setVal(self, newval):
self.val = newval
def __str__(self):
return str(self.val)
def __repr__(self):
return str(self.val)
# methods for comparison with a regular int or float:
def __eq__(self, other):
return self.val == other
def __gt__(self, other):
return self.val > other
def __lt__(self, other):
return self.val < other
def __ge__(self, other):
return self.__gt__(other) or self.__eq__(other)
def __le__(self, other):
return self.__lt__(other) or self.__eq__(other)
The class does what it is supposed to do, comparing a MutNum object to a regular int or float is no problem. However, and this is what I don't understand, it even compares fine when the magic methods are given two MutNum objects.
a = MutNum(42)
b = MutNum(3)
print(a > b) # True
print(a >= b) # True
print(a < b) # False
print(a <= b) # False
print(a == b) # False
Why does this work? Thanks.
It evaluates as follows (using a repr-like notation instead of referring to variables):
MutNum(42) > MutNum(3)
=> MutNum(42).__gt__(MutNum(3))
=> MutNum(42).val > MutNum(3)
=> 42 > MutNum(3)
And from there, it's just the int-MutNum comparision you already know works.
If you throw in some print's and/or sys.stderr.write's, I think you'll see what's happening. EG:
def __gt__(self, other):
sys.stderr.write('__gt__\n')
sys.stderr.write('{}\n'.format(type(other)))
sys.stderr.write('{} {}\n'.format(self.val, other))
result = self.val > other
sys.stderr.write('result {}\n'.format(result))
return result
def __lt__(self, other):
sys.stderr.write('__lt__\n')
sys.stderr.write('{}\n'.format(type(other)))
sys.stderr.write('{} {}\n'.format(self.val, other))
result = self.val < other
sys.stderr.write('result {}\n'.format(result))
return result
When you try to compare self.val (an int) to other (a MutNum), python realizes it has nothing for comparing an int to a MutNum, and reverses the order of the comparison, and compares a MutNum to an int - which is something you've defined. That is, a single > comparison is doing the > as you'd expect, but it's also doing a <.