Interval class - Python - python

I am supposed to write a class for Intervals, then I need to define addition (how to add two intervals together).
I've done this and it works:
def __add__ (self, other):
return Interval (self.a + other.a, self.b + other.b)
where a and b are ending points of one interval.
Now I need to modify the code so that addition between an interval and number c (float or int) is defined.
[a,b] + c = [a+c,b+c] and
c + [a,b] = [a+c,b+c].
I've tried a lot of things that don't work, something like:
def __add__ (self, other, *args):
if args:
return Interval (self.a + other.a, self.b + other.b)
else:
return Interval (self.a + int(number), self.b + int(number))
Whatever I try it doesn't work. If you have time, please take a look and give me a hint. I'd really appreciate that!

If you want to define both Interval(a, b) + Interval(c, d) and Interval(a, b) + c (for some non-Interval type of c), you need to examine the argument other in the definition.
def __add__(self, other):
if instanceof(other, Interval):
return Interval(self.a + other.a, self.b + other.b)
elif instanceof(other, (int, float)):
return Interval(self.a + other, self.b + other)
else:
return NotImplemented
To support c + Interval(a, b) as well, you need to define __radd__:
def __radd__(self, other):
return self + other
If you right 3 + Interval(a, b), 3.__add__(Interval(a, b)) doesn't know how to deal with an Interval, so it returns NotImplemented, which is Python's cue to try Interval(a, b).__radd__(3) instead. The definition of __radd__ usually isn't too complicated, unless your operation isn't commutative (that is, 3 + Interval(a, b) and Interval(a, b) + 3 are not equal).

You could assume that other is already an Interval and try the addition, but catch an exception when not:
def __add__ (self, other):
try:
return Interval (self.a + other.a, self.b + other.b)
except AttributeError:
pass
return Interval (self.a + int(other), self.b + int(other))
If you then want to calculate 42 + x you need the radd method:
def __radd__(self, other):
return self + other

Related

python: overload operator > with different types

tell me how to overload the operator > (lt) or < (gt) etc so that it can work with different types (especially with 0)
for example:
class CRational(object):
def __init__(self, a = 0, b = 1):
self.a = a
self.b = b
def __neg__(self):
return CRational(-self.a, self.b)
def __add__(self, other):
return CRational(self.a * other.b + other.a * self.b, self.b * other.b)
def __sub__(self, other):
return CRational(self.a * other.b - other.a * self.b, self.b * other.b)
def __mul__(self, other):
return CRational(self.a * other.a, self.b * other.b)
def __truediv__(self, other):
return CRational(self.a * other.b, self.b * other.a)
def __eq__(self, other):
return self.a * other.b == other.a * self.b
def __ne__(self, other):
return not self.__eq__(other)
def __lt__(self, other):
return self.a * other.b < other.a * self.b
def __le__(self, other):
return self.a * other.b <= other.a * self.b
def __gt__(self, other):
return self.a * other.b > other.a * self.b
def __ge__(self, other):
return self.a * other.b >= other.a * self.b
error output:
File "lessons/python/square_eq.py", line 66, in __gt __
return self.a * other.b > other.a * self.b AttributeError: 'int' object has no attribute 'b'
in code:
s_a = "" if a > 0 else "-"
how do you fix it?
s_a = "" if a > CRational(0) else "-"
the method described above helps, but it is not beautiful :)
If you want to compare a CRational object with an int then your __gt__ method should works with integers too. I.e., if other is an integer, you clearly can't do something like other.b. Here is a possible solution:
class CRational:
def __init__(self, a = 0, b = 1):
self.a = a
self.b = b
def __gt__(self, other):
if isinstance(other, CRational):
return self.a * other.b > other.a * self.b
elif isinstance(other, int):
# Compare self (a CRational object) with other (an int)
# TODO
else:
raise NotImplemented()
Now you can do something like this:
a = CRational()
if a > 3:
...
Be careful though! Even if you implement correctly all the methods, you still can't do 3 > a. Order is important!! 3 > a would call the __gt__ method of the int class. You can only do a > 3, a < 3, a >= 3 etc.
With regards to your error output: you are getting the error because your "other" is an int and CRational.__gt__ is trying to access an attribute that it does not have a b:
def __gt__(self, other): # passing in an int here
return self.a * other.b > other.a * self.b
With regards to your code example, assuming a is another CRational object, then a > 0 will cause the AttributeError you are seeing above and the only way to fix it is by comparing it with either another CRational object or some other object that has attribues a and b.
Sounds like you're wanting to test for what kind of object you are working with. Maybe something like:
import numbers
def __gt__(self, other):
if isinstance(other, type(self)):
return self.a * other.b > other.a * self.b
elif isinstance(other, numbers.Number):
return self.a > other
else:
err_str = "Unknown type in comparison with %s object: %s" % (type(self).__name__, type(other).__name__)
raise(TypeError(err_str))
Here, type(self) is a generic way of getting the CRational class. That way you don't have to modify the class's code if you simply change the name later.
isinstance checks to see if a given object is of the given type, or a child type. Python documentation.
I made up the numeric case, because I don't know how you'd want to define that.

Adding two distinct subclasses as their common superclass

I have a class with a lot of subclasses in my code. Consider the following code:
class Dataset:
def __init__(self, samples):
self.data = samples
def __add__(self, other):
if isinstance(other, type(self)):
return type(self)(self.data + other.data)
return NotImplemented
def __radd__(self, other):
if isinstance(other, type(self)):
return type(self)(other.data + self.data)
return NotImplemented
def __str__(self):
return str(self.data)
class DatasetA(Dataset):
def __init__(self, samples):
super().__init__(samples)
self.a = len(self.data)
def __str__(self):
return f"{self.a}: {self.data}"
class DatasetB(Dataset):
def __init__(self, samples):
super().__init__(samples)
self.b = sum(self.data)
def __str__(self):
return f"{self.data} (sum: {self.b})"
d = Dataset([1, 2, 3])
a = DatasetA([4, 5])
b = DatasetB([6, 7])
print(d + d)
print(d + a)
print(a + d)
print(d + b)
print(b + d)
print(a + a)
print(b + b)
print(a + b)
print(b + a)
The idea is to define an __add__ method in the superclass that won't require being overridden in each of the subclasses and will still add them correctly (for instance, two DatasetBs should add up to another DatasetB). This works correctly (the first 7 prints are fine), however there is one additional functionality I'd like to implement, represented in the last 2 prints.
I would like any two distinct subclasses to add up to the first common superclass. For example, a + b should result in a Dataset instance. Also if we add up instances of two subclasses of DatasetB, the result should be a DatasetB instance.
I've tried changing return NotImplemented to return super().__add__(other) (and similar for __radd__), but that resulted in an AttributeError on the 3rd print statement already.
Is there a way to implement this desired functionality without breaking the existing one (i.e. still have the first 7 prints execute properly), and without explicitly having to override __add__ in each of the subclasses?
You could change:
return type(self)(self.data + other.data)
to:
klass = next(c for c in type(self).mro() if c in type(other).mro())
return klass(self.data + other.data)
This will pick the most specific superclass the two have in common, leveraging the method resolution order.

I want to give an input of adding two numbers but it should return multiplication of those two numbers using operator overloading

I am a newbie. I want to use operator overloading which gives 3+4 but returns answer of 3*4
I have made a class and passed two functions add and mul
class A:
def __init__(self, a,b):
self.a = a
self.b = b
# adding two objects
def __add__(self, other):
return self.a + other.a , self.b + other.b
# multiply two objects
def __mul__(self, other):
return self.a * other.a , self.b +other.b
ob1 = A(1)
ob2 = A(2)
ob3 = ob1+ob2
ob4 = ob1*ob2
print(ob3)
print(ob4)
Expected: input 3 and 4 , it should show 3+4 but return 3*4
In your __mul__ and __add__ methods, you need to return an instance of A, not just some values (unless you are doing in place operations). It seems like you only want to add 2 numbers together, so maybe you should try having only 1 parameter __init__:
class A:
def __init__(self, a):
self.a = a
def __add__(self, other):
return A(self.a * other.a)
Now when you do:
A(3) + A(2)
You are getting back 2*3 as the __add__ method returns a new instance of A whose .a attribute is the product, not sum, of the given two.
You should also consider type checking or error handling as your next step. What if I typed:
A(2) + 10 # not A(10)
Would an error be raised? That’s up to you. The easiest way to cause an error to be raised if you return NotImplemented from the function. This method also allows polymorphism to take place where any object with a .a attribute will work (as long as it is something that can be multiplied).
...
def __add__(self, other):
try:
return A(self.a * other.a)
except Exception:
return NotImplemented

create a range limited int or float object or function

What would be the best (loaded term...) to limit the range of an int or a float.
In python they are basically infinite but say I want to "clamp" an int at +-10 and equally cause a wraparound (like in digital) between two arbitrary limit
eg
edit this works
def wrapped(x, L, H):
return ((x-L) % (H-L))+L
If I am understanding this correctly, you could use a mod operator to limit the range between (a,b):
def clamped(x, a, b):
return ((x - a) % b) + a
First, normalize x to be around 0, then mod by b, then restore the modded value back to the start.
Then, wrap-around would also work!
I would make a simple class and have it wrap around using % when you tried to assign a value higher than your range.
class NewInt:
def __init__(self, value=0, range=10):
self.value = value
self.range = range
def set(self, number):
self.value = number % self.range
def get(self): # just in case you don't want to call to obj.value
return self.value
A more natural class implementation.
class IntOverflow(Exception):
pass
class LimitedInt(object):
def __init__(self, value, range):
self._value = value
self._range = range
def __add__(self, some_int):
return abs(self._value + some_int) <= self._range and (self._value + some_int) or self._error()
#staticmethod
def _error():
raise IntOverflow
def __repr__(self):
return repr(self._value)
def __str__(self):
return str(self._value)
a = LimitedInt(3, 10)
print(a + 3)
print(a + 10)
Output
6
...IntOverflow...

python math expressions to latex, verbatim (no reordering, factoring, etc.)

I have input of a whole lot of math expressions and equations, and I'd like to print out latex representation for each on them. So far I have tried Sage and sympy, but the tricky part is to not-reorder terms in expressions.
So, if my input is this, something that can be eval-ed in python:
(C - A*x) / B
I want output that will be something like this:
\frac{C - A x}{B}
What I don't want is something like this:
\frac{-(A x - C)}{B}
\frac{1}{B}(C - A x)
etc...
Can this be achieved? I'm slowly losing hope...
EDIT:
The input expressions are diverse, some containing square roots, nested parentheses, exponents etc. Looking for a generic solution.
Here is what doesn't work so far:
1) Sage:
sage: var('A B C x y')
(A, B, C, x, y)
sage: latex(y == (C - A*x) / B)
y = -\frac{A x - C}{B}
2) sympy:
>>> from sympy import *
>>> x = Symbol('x')
>>> A = Symbol('A')
>>> B = Symbol('B')
>>> C = Symbol('C')
>>> latex((C - A*x) / B)
'\\frac{1}{B} \\left(- A x + C\\right)'
Short of writing your own parser, I believe the only real way to do this is to use python's built-in compile() function and process the returned abstract syntax tree.
\You can do this by creating Symbol and Operator classes that implement the standard python data model (http://docs.python.org/2/reference/datamodel.html). This will keep things in the same order of python operator precedence, although you can rearrange via parens:
class Symbol(object):
def __init__(self, name):
self._name = name
def __str__(self):
return str(self._name)
def __div__(self, other):
return Div(self, other)
def __mul__(self, other):
return Mult(self, other)
def __add__(self, other):
return Add(self, other)
def __sub__(self, other):
return Sub(self, other)
def __rdiv__(self, other):
return Div(other, self)
def __rmul__(self, other):
return Mult(other, self)
def __radd__(self, other):
return Add(other, self)
def __rsub__(self, other):
return Sub(other, self)
class Operation(Symbol):
def __init__(self, a, b, op):
self._a = a
self._b = b
self._op = op
def __str__(self):
return self._op.format(self._a, self._b)
class Add(Operation):
precedence = 0
def __init__(self, a, b):
super(Add, self).__init__(a, b, "{0} + {1}")
class Sub(Operation):
precedence = 0
def __init__(self, a, b):
super(Sub, self).__init__(a, b, "{0} - {1}")
class Mult(Operation):
precedence = 1
def __init__(self, a, b):
if isinstance(a, Operation) and a.precedence < Mult.precedence:
a_form = "({0})"
else:
a_form = "{0}"
if isinstance(b, Operation) and b.precedence < Mult.precedence:
b_form = "({1})"
else:
b_form = "{1}"
super(Mult, self).__init__(a, b, a_form + " " + b_form)
class Div(Operation):
precedence = 1
def __init__(self, a, b):
super(Div, self).__init__(a, b, "\\frac{{{0}}}{{{1}}}")
A = Symbol('A')
B = Symbol('B')
C = Symbol('C')
x = Symbol('x')
Then:
>>> print (C - A * x) / (B)
\frac{C - A x}{B}
>>> print (C * (A + B))
C (A + B)
>>> print (C * (A + B + A + B + C + x))
C (A + B + A + B + C + x)

Categories