Dependent variable in Python - python

I would like to define an Integer class in python, where an Integer (called y) can be related to another Integer (called x) and get updated automatically when this Integer x changes. More concretely I would like to have the following behavior
>>> x = Integer(7)
>>> y = x + 2
>>> print y
9
>>> x.set(9)
>>> print y
11
>>> z = x + y
>>> y.set(10)
>>> print z
19
I realize that one can do this in sympy but I am interested in implementing this myself. I would be grateful if someone can please point out how one would go about this in the simplest manner? Thank you.

I've not used Sympy before but here's my attempt:
class Integer(object):
def __init__(self, value_or_callback):
if isinstance(value_or_callback, int):
self._value_callback = lambda: value_or_callback
else:
self._value_callback = value_or_callback
#property
def value(self):
return self._value_callback()
def set(self, new_value):
self._value_callback = lambda: new_value
def __add__(self, other):
if isinstance(other, int):
return Integer(lambda: self.value + other)
elif isinstance(other, Integer):
return Integer(lambda: self.value + other.value)
else:
raise TypeError(other)
def __radd__(self, other):
return self.__add__(other)
def __repr__(self):
return str(self.value)
if __name__ == '__main__':
x = Integer(7)
y = x + 2
print(y)
x.set(9)
print(y)
z = x + y
y.set(10)
print(z)
Output
9
11
19

Related

How to properly print the output __str__

How to properly print the output str
class Complex(object):
def __init__(self, real, imaginary):
self.real = real
self.imaginary = imaginary
def __add__(self, other):
return Complex(self.real+other.real, self.imaginary+other.imaginary)
def __sub__(self, other):
return Complex(self.real-other.real, self.imaginary-other.imaginary)
def __str__(self):
return '{} & {}i'.format(self.real, self.imaginary)
if __name__ == '__main__':
c = map(float, input().split())
d = map(float, input().split())
x = Complex(*c)
#print (x)
y = Complex(*d)
#print (y)
print(*map(str, [x+y, x-y]), sep='\n')
Input
2 1
5 6
Output
7.0 & 7.0i
-3.0 & -5.0i
Expected out if for addtion it should print + and for substraction it should print -
7.00+7.00i
-3.00-5.00i
class Complex(object):
def __init__(self, real, imaginary):
self.real = real
self.imaginary = imaginary
def __add__(self, other):
return Complex(self.real+other.real, self.imaginary+other.imaginary)
def __sub__(self, other):
return Complex(self.real-other.real, self.imaginary-other.imaginary)
def __str__(self):
return '{:.2f}{}{:.2f}i'.format(self.real, '+' if self.imaginary >= 0 else '', self.imaginary)
if __name__ == '__main__':
c = map(float, input().split())
d = map(float, input().split())
x = Complex(*c)
#print (x)
y = Complex(*d)
#print (y)
print(*map(str, [x+y, x-y]), sep='\n')
You should manually add sign, if var is positive.
Also in your expected result you have 2 decimal points, so you need to add {:.2f}.
Use this in your str implementation.
def __str__(self):
return f"{self.real}{ '+' if self.imaginary >= 0 else ''}{self.imaginary}i"
Instead of this:
def __str__(self):
return '{} & {}i'.format(self.real, self.imaginary)
You could do this:
def __str__(self):
def __map_imaginary(imag):
if imag > 0:
return "+{:2f}i".format(imag)
if imag < 0:
return "{:2f}i".format(imag)
if imag == 0:
return ""
return "{}{}".format(self.real, __map_imaginary(self.imag))
I have assumed that you don't want to print imaginary part if it equals to 0. You can change that at will.

Manipulating Python Magic Methods

I have more of a background with data science libraries or calling methods and attributes from classes. I am experimenting with manipulating magic methods. I am having a difficult time getting bool types and returning their opposites.
I did something with str and datetime objects that worked but can't seem to to the same with __cmp__, __lt__, __eq__ or `gt'. Here is my code:
class Opposite:
def __cmp__(self, other):
if other.__class__.__name__ == 'bool':
return other
def __lt__(self, other):
if other.__class__.__name__ == 'bool':
return other
def __eq__(self, other):
if other.__class__.__name__ == 'bool':
return other
def __gt__(self, other):
if other.__class__.__name__ == 'bool':
return other
if __name__=="__main__":
""" test class Compare """
a = 1
b = 1
c = a < b
d = a > b
e = a == b
print("Results:\na\t{}\nb\t{}\nc\t{}\nd\t{}\ne\t{}\n".format(a,b,c,d,e))
print("\nType:\na-type\t{}\nb-type\t{}\nc-type\t{}\nd-type\t{}\ne-type\t{}\n"
.format(type(a),type(b),type(c),type(d),type(e)))
This prints the following:
Results:
a 1
b 1
c False
d False
e True
Type:
a-type <class 'int'>
b-type <class 'int'>
c-type <class 'bool'>
d-type <class 'bool'>
e-type <class 'bool'>
As you can see, the results are the same as not using the class at all. I added an __init__ method to print using Opposite and it only prints that if I instantiate the object with something like a = Opposite().
I would like to enter something like a > b, a < b, or a == b, and return the opposite boolean value, True, or False, as an exercise.
I tried several things such as placing the methods under the __init__ method I created, which didn't work either. I read on this and still don't quite understand how to do this with booleans, integers and floats for that matter. The way the methods are above is how I was able to turn datetime objects into strings with __add__, __radd__ and __rsub__ methods.
Thank you for your help.
EDIT
Thanks to your help, I have a better understanding and have completed my small experiment with this code:
class Opposite:
def __init__(self, x):
self._x = x
def __lt__(self, other):
return not self._x < other._x
def __eq__(self, other):
return not self._x == other._x
def __gt__(self, other):
return not self._x > other._x
def __le__(self, other):
return not self._x <= other._x
def __ge__(self, other):
return not self._x >= other._x
def tester(w, x, y, z):
try:
# Original values
a = w < x
b = w > x
c = w == x
d = w <= x
e = w >= x
# Opposite values
f = y < z
g = y > z
h = y == z
i = y <= z
j = y >= z
# Results
k = 'Fail' if a == f else 'Success'
l = 'Fail' if b == g else 'Success'
m = 'Fail' if c == h else 'Success'
n = 'Fail' if d == i else 'Success'
o = 'Fail' if e == j else 'Success'
print('\nComparing {} and {}:\t<\t>\t==\t<=\t>='.format(w, x))
print('Original Values:', end='\t')
print('{0}\t{1}\t{2}\t{3}\t{4}'.format(a, b, c, d, e))
print('Opposite Values:', end='\t')
print('{0}\t{1}\t{2}\t{3}\t{4}'.format(f, g, h, i, j))
print('Comparisons:', end='\t')
print('\t{0}\t{1}\t{2}\t{3}\t{4}'.format(k, l, m, n, o))
except(Exception) as err:
print(err)
if __name__=="__main__":
""" test class Compare """
a = 1
b = 2
c = Opposite(a)
d = Opposite(b)
tester(a, b, c, d)
This prints the following:
Comparing 1 and 2: < > == <= >=
Original Values: True False False True False
Opposite Values: False True True False True
Comparisons: Success Success Success Success Success
If you mean that you want to return the negation of the boolean resulting from the comparison you could do something like
class T:
def __init__(self, x):
self._x = x
def __lt__(self, other):
return not self._x < other._x
t1 = T(1)
t2 = T(2)
print(t1 < t2) #False
Note that in the comparison self._x < other._x you are using the __lt__ method of the int class.

How to check if one list is equal to another list created using a class?

from math import pi
class Circle(object):
'Circle(x,y,r)'
def __init__(self, x=0, y=0, r=1):
self._r = r
self._x = x
self._y = y
def __repr__(self):
return 'Circle({},{},{})'.\
format(self.getx(), self.gety(),\
self.getr())
#silly, but has a point: str can be different from repr
def __str__(self):
return 'hello world'
def __contains__(self, item):
'point in circle'
px, py = item
return (self.getx() - px)**2 + \
(self.gety() - py)**2 < self.getr()**2
def getr(self):
'radius'
return self._r
def getx(self):
'x'
self._lst.append(self._x)
return self._x
def gety(self):
'y'
self._lst.append(self._y)
return self._y
def setr(self,r):
'set r'
self._r = r
def setx(self,x):
'set x'
self._x = x
def sety(self,y):
'set y'
self._y = y
def move(self,x,y):
self._x += x
self._y += y
def concentric(self, d):
d = self._list
def area(self):
'area of circle'
return (self.getr())**2*pi
def circumference(self):
'circumference of circle'
return 2*self.getr()*pi
My question is worded kinda awkwardly but what I am trying to do is check if 2 different circles have the same center (x,y). I think the easiest way to solve this would be to input the 2 points into a list but I am not sure how to compare the 2 lists as every time i try my code it adds everything to the same list
Add the following method to your Circle class.
def equal_center(self, other):
'check if another circle has same center'
return (self._x == other._x) & (self._y == other._y)
Usage
C1 = Circle(3, 5, 8)
C2 = Circle(3, 5, 10)
C3 = Circle(3, 2, 1)
C1.equal_center(C2) # True
C1.equal_center(C3) # False
I would recommend creating a function which takes two circle objects and returns if the coordinates are the same or not by comparing the x and y values of each object:
def same_center(circle_1, circle_2):
if circle_1.getx() == circle_2.getx() and circle_1.gety() == circle_2.gety():
return True
else:
return False
This solution is much easier than using lists and should be easy to implement.
If you have two instances of the class...
a = Circle(0,0,1)
b = Circle(0,0,1)
You could add them to a list of circles...
circles = [a,b]
And loop through the list, checking their values...
for i in circles:
for j in filter(lambda x : x != i, circles):
if i._x == j._x and i._y == j._y:
return True #two circles have same center
This should work for n instances of the class, though if its only two you want to check
if a._x == b._x and a._y == a._y:
return True

Updating object values in class

I have a question about how to return the update value of an object in a class and then use that in another function in the same class. Here is my old code.
class Vector:
def __init__(self, a):
self.a = a
assert type(self.a) == list
for i in self.a:
assert type(i) == int or type(i) == float
def dim(self):
return len(self.a)
def __getitem__(self, i):
assert i >= 1 and i <= self.dim()
return self.a[i-1]
def __setitem__(self, i, x):
assert i >= 1 and i <= self.dim()
self.a[i-1] = x
return self.a[i-1]
def __str__(self):
return 'Vector: ' + str(self.a)
def __add__(self, other):
assert type(other.a) == list and other.dim() == self.dim()
n = []
for j in range(self.dim()):
n.append(self.a[j]+other.a[j])
self.a = n
return self.a
so when i'm running this test case:
v1 = Vector([2, 3, 4])
v2 = Vector([1, 2, 3])
str(v1 + v2)
my output is '[3, 5, 7]' which means it is only following return self.a and not the __str__ function however i want my output to be 'Vector: [3, 5, 7]' as it should be following the __str__ function. I fixed this by returning Vector(self.a) in the __add__ function but i dont know why this works. Can anyone explain why that works, and why return self.a does not simply update the object value and run the __str__ function instead?
Note: Python uses following equivalent notations:
v[i] == v.__getitem__(i)
v[i] = x == v.__setitem__(i, x)
str(v) == v.__str__()
v + other == v.__add__(other)

Python multiple intersection

I reimplemented the set in python but i have some problem with multiple intersection.... I followed the book Learning Python but i have problem with my code
class Set:
def __init__(self,value=[]):
self.data = []
self.remDupli(value)
def remDupli(self,val):
for i in val:
if i not in self.data:
self.data.append(i)
def intersect(self,other):
val=[]
for i in self.data:
for k in other:
if i == k:
val.append(i)
return Set(val)
def union(self,other):
val=self.data
for i in other:
if i not in self.data:
val.append(i)
return Set(val)
def __or__(self,a): return self.union(a)
def __and__(self,a): return self.intersect(a)
def __len__(self): return len(self.data)
def __getitem__(self,key): return self.data[key]
def __repr__(self): return 'Set: ' +repr(self.data)
class Extend(Set):
def intersect(self, *others):
val = []
for i in self:
for x in others:
if i in x:
val.append(i)
return Set(val)
but when I run this:
x = Extend([1,2,3,4])
y = Extend([3,4,5])
z = Extend([0,1,2])
print(x & y & z)
print(x.intersect(y, z))
I have two different behavior
Set: []
Set: [1, 2, 3, 4]
I don't understand because the second is different, in my opinion they should have the same behavior, anyone can help me?
Extend.intersect does not calculate intersection between many sets. It calculates intersection between self and union of others.
The results are different because x & y & z calls Extend.intersect(Extend.intersect(x,y), z), while x.intersect(y,z) calls Extend.intersect(x, *[y,z]) and given what Extend.intersect actually does, those happen to be different operations.

Categories