Operator overloading with python outside a class - python-3.x

class A:
def __init__(self,m1,m2):
self.m1 = m1
self.m2 = m2
def __add__(self, other):
''' add takes 2 objects basically objects on RHS and LHS of + say a + b '''
print("Inside add")
s3 = A(self.m1+other.m1,self.m2+other.m2)
return s3
def disp(self):
print('{} {}'.format(self.m1,self.m2))
def __str__(self):
return '{} {}'.format(self.m1,self.m2)
def __add__(self,other):
return self*other
a = 2+5
print(a)
Output seen is: 7
Whereas expected is 10 since I am trying to overwrite the implicit add function with multiply operation.
What happens here?
Does operator overloading works only with pytclass?

class A:
def __init__(self,m):
self.m=m
def __str__(self):
return str(self.m)
#multiply two objects
def __mul__(self,other):
t=self.m*other.m
return A(t)
#add two objects
def __add__(self,other):
t=self.m+other.m
return A(t)
obj1=A(2)
obj2=A(5)
#calling operator overloading of '*'
print(obj1*obj2)
#calling operator overloading of '+'
print(obj1+obj2)

Related

How to work with a python collections.deque of custom class instances?

I am trying to utilize python deque from collections module where the element inside the deque is a custom class instances. I want to know how I can erase/delete an object? Can I use the builtin methods like deque.remove(element) if yes, how? How would it find my custom object?
class Buffer(object):
""" """
def __init__(self, name, size_total, size_in_cache):
self.name = name
self.size_total = size_total
class Storage(object):
"""
"""
def __init__(self, capacity):
self.CAPACITY = capacity
self.contents = collections.deque()
self.capacity_used = 0
def push_to_contents(self, buffer_name, buffer_size):
buf = Buffer(buffer_name, buffer_size)
self.contents.appendleft(buf)
def delete_from_contents(self, buffer_name)
""" HOW??
How can I use self.contents.remove() here>
"""
The way collections.deque.remove operates is by comparing the argument to each item in the deque. If it finds something which is equal to the argument, it removes it. Otherwise, it raises a ValueError.
Since a Buffer object, as you've implemented it, doesn't know how to compare itself with other objects, Python defaults (using the object parent class) to comparing the id values.
However, if you were to implement the __eq__ method for your class, you could accomplish what you're looking for.
E.g.,
def __eq__(self, other):
if isinstance(other, Buffer):
return self.name == other.name and self.size_total == other.size_total
elif isinstance(other, str):
return self.name == other
else:
return NotImplemented
EDIT:
This is all fine and good if you're using Python 3. In Python 2, you have to implement __ne__ ("not equal") as well. It can be as simple as
def __ne__(self, other):
return not self == other
Python 3 takes care of this for you automatically.

how can i iterate class objects while using generator in python?

i want to compare the 2000 pairs of a and b, and print the matching pairs and count of them.can anyone help me to get rid of it.it shows this error TypeError: 'seq_generator' object is not iterable at line 34
class generator(object):
def __init__(self,s,start_A,fact_A):
self.s=s
self.start_A=start_A
self.fact_A = fact_A
def seq_gen_A(self):
while self.s>0:
self.gen_A=(self.start_A*self.fact_A)%2147483647
self.g=self.gen_A
self.start_A=self.gen_A
self.bin_A=(bin(self.gen_A)[-16:])
yield self.bin_A
self.s=self.s-1
def next(self):
for i in self.seq_gen_A():
return i
def main():
s=200
count=0
a=generator(s,65,16807)
b=generator(s,8921,48271)
print("Matching pair")
print("*************")
for i in range(0,s):
if next(a)==next(b):
count+=1
print(a,b)
print('Matching pair count',count)
You are defining your generator not correctly. You need methods __iter__ and __next__:
class generator(object):
def __init__(self, s, start_A, fact_A):
self.s = s
self.start_A = start_A
self.fact_A = fact_A
def __iter__(self):
return self
def __next__(self):
if self.s <= 0:
raise StopIteration()
gen_A = (self.start_A*self.fact_A)%2147483647
self.start_A = gen_A
yield bin(gen_A)[-16:]
self.s -= 1

Cannot refer to other within method utilizing self - python 3.6

I have been going at this question and i cant seem to wrap my head around the add() portion of it. if any one could help me out and explain it to me that would be much appreciated. especially with the other parameter. The actual question is within the Docstring of the methods. I beleive the str(self): method is correct and the init(self,m,b) method is correct also but if not please correct me.
class LinearPolynomial():
def __init__(self, m, b):
self.m = m
self.b = b
def __str__(self):
"""
Returns a string representation of the LinearPolynomial instance
referenced by self.
Returns
-------
A string formatted like:
mx + b
Where m is self.m and b is self.b
"""
string= '{}x + {}'
return string.format(self.m,self.b)
def __add__(self, other):
"""
This function adds the other instance of LinearPolynomial
to the instance referenced by self.
Returns
-------
The sum of this instance of LinearPolynomial with another
instance of LinearPolynomial. This sum will not change either
of the instances reference by self or other. It returns the
sum as a new instance of LinearPolynomial, instantiated with
the newly calculated sum.
"""
Cm= other.m + self.m
Cb = other.b + self.b
string= '{}x + {}'
return string.format(Cm,Cb)
The expected results are within the docstring of the methods.
You need to create and return a new LinearPolynomial object, not a string. Your method should also check the type of the other operand and return the NotImplemented value if it is not a LinearPolynomial
def __add__(self, other):
if not isinstance(other, LinearPolynomial):
return NotImplemented
Cm = other.m + self.m
Cb = other.b + self.b
return LinearPolynomial(Cm, Cb)

How can I use an operator to compose functions?

It's fairly straightforward to write a function that composes two other functions. (For simplicity, assume they are one parameter each.)
def compose(f, g):
fg = lambda x: f(g(x))
return fg
def add1(x):
return x + 1
def add2(x):
return x + 2
print(compose(add1, add2)(5)) # => 8
I would like to do composition using an operator, e.g., (add1 . add2)(5).
Is there a way to do that?
I tried various decorator formulations, but I couldn't get any of them to work.
def composable(f):
"""
Nothing I tried worked. I won't clutter up the question
with my failed attempts.
"""
#composable
def add1(x):
return x + 1
First only a certain amount of operator symbols are allowed in Python syntax. Dot "." is not a valid operator.
This page (the page is actually about the Python operator module, but the naming convention are the same to datamodel and the content is more organized) listed all available operators and the corresponding instance methods. For example, if you want to use "#" as the operator, you can write a decorator like this:
import functools
class Composable:
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
def __matmul__(self, other):
return lambda *args, **kw: self.func(other.func(*args, **kw))
def __call__(self, *args, **kw):
return self.func(*args, **kw)
To test:
#Composable
def add1(x):
return x + 1
#Composable
def add2(x):
return x + 2
print((add1 # add2)(5))
# 8

addition between classes using radd method

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
__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.
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)
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'

Resources