Cannot refer to other within method utilizing self - python 3.6 - python-3.x

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)

Related

Operator overloading with python outside a class

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)

how to remove objects from set irrespective of order

I'm having trouble removing objects from a set. What i did was, create a test class and store two variables in it. Its a string variable. I need to store the objects i create into a set and also, any object where (t.a, t.b) is same as (t.b, t.a). Hence, whenever i add tuples into my set, i'm having trouble removing the reverse condition. Is there a way to do this in python?
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
self.variables = [a, b]
def __hash__(self):
return hash((self.a, self.b))
def __eq__(self, other: Test):
return type(self) is type(other) and self.endpoint() == other.endpoint() or
self.endpoint() == other.endpoint()[::-1]
def endpoint(self):
return (self.a, self.b)
T = Test('A','B')
T2 = Test("B",'A")
result = set()
result.add(T)
result.add(T2)
However, result is showing me both the objects in it as opposed to one. Is there a way to fix this? Thanks

Why is __setattr__ called on an attribute which is an instance of another class

I very puzzled by the fact that __setattr__ is being called in an attempt to set a class attribute, which has already been set to an instance of another class. Consider the following code:
class A:
def __init__(self):
self.a = 42
def __add__(self, value):
print("Incrementing a of A by {}".format(value))
self.a += value
class B:
def __init__(self):
self.a = A()
self.b = 10
def __setattr__(self, attr, value):
print("Setting {} of B to {}".format(attr, value))
super(B, self).__setattr__(attr, value)
b = B()
print(b.b)
print(b.a)
b.b = 11
b.a += 1
print(b.b)
print(b.a)
When run, the code above produces the following output:
Setting a of B to <__main__.A object at 0x7f5e25438410>
Setting b of B to 10
10
<__main__.A object at 0x7f5e25438410>
Setting b of B to 11
Incrementing a of A by 1
Setting a of B to None
11
None
Obviously, b.a is correctly looked up and incremented. However, after the successful lookup, Python is attempting to create a new attribute of b called a. Why is that happending?
Answering my own question based on Jan Willems' comment:
Having __add__(self, value) return self fixes the described issue despite not being obvious. The documentation for the method (__add__ Documentation ) does not mention that __add__ should return anything when it is successful, which is where my confusion stemmed from.

Caching attributes in superclass

I have a class which caches some values to avoid computing them many times, for instance
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
self._value = None
#property
def value(self):
if self._value is None:
self._value = # <complex code that produces value>
return self._value
In this way, self._value is computed only once and all the other times the precomputed value is returned. So far so good.
Now, let's suppose I want to subclass A with class B. In our case class B will have its own method of computing self._value but it sometimes will need A's value, like in this example:
class B(A):
def __init__(self, a, b):
super().__init__(a, b)
#property
def value(self):
if self._value is not None:
self._value = # <complex code that produces B's version of value>
return self._value
def get_old_value(self):
return super().value # here comes the trouble
Now, clearly the trouble is that if get_old_value() is called before value() it will cache A's value forever. If value() is called before get_old_value() in the same way, get_old_value() will actually always return value().
Of course, one could simply use A's <complex code that produces value>, in the implementation of get_old_value() but that would duplicate code (which would pretty much make subclassing useless) or even wrap <complex code that produces value> inside another method in A and call that method in get_old_value() but this would not use caching at all.
Another way could be the following:
def get_old_value(self):
result = super().value
self._c = None
return result
but that would anyway remove caching for A's version of value and does not look clean at all. Is there any better way to accomplish this?
One thing I want to add is that in my code A and B make really sense as superclass and subclass, otherwise I would consider composition.
What you need to do is use name-mangling -- this will allow each class/subclass to maintain a private version of the variable so they don't clobber each other:
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.__value = None
#property
def value(self):
if self.__value is None:
self.__value = 7
return self.__value
class B(A):
def __init__(self, a, b):
super().__init__(a, b)
self.__value = None
#property
def value(self):
if self.__value is None:
self.__value = 17
return self.__value
def get_old_value(self):
return super().value # no more trouble here
And in use:
>>> b = B(1, 2)
>>> print(b.value)
17
>>> print(b.get_old_value())
7
Please note you now need to set __value in B's __init__ as well.
See also this answer for a couple more tidbits about name-mangling.

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