How do I tell python class method to reference its own fields? - python

This is a simple problem that is hard for me to put into words because I'm not too familiar with Python's syntax. I have a class called "Quadrilateral" that takes 4 points, and I'm trying to make a method called "side_length" that I want to use to compute the length of the line between two of the vertices on the quadrilateral:
import math
class Point:
x = 0.0
y = 0.0
def __init__(self, x=0, y=0):
self.x = x
self.y = y
print("Point Constructor")
def to_string(self):
return "{X: " + str(self.x) + ", Y: " + str(self.y) + "}"
class Quadrilateral:
p1 = 0
p2 = 0
p3 = 0
p4 = 0
def __init__(self, p1=Point(), p2=Point(), p3=Point(), p4=Point()):
self.p1 = p1
self.p2 = p2
self.p3 = p3
self.p4 = p4
print("Quadrilateral Constructor")
def to_string(self):
return "{P1: " + self.p1.ToString() + "}, " + "{P2: " + self.p2.ToString() + "}, " + \
"{P3: " + self.p3.ToString() + "}," + "{P4: " + self.p4.ToString() + "}"
def side_length(self, p1, p2):
vertex1 = p1
vertex2 = p2
return math.sqrt((vertex2.x - vertex1.x)**2 + (vertex2.y - vertex1.y)**2)
def perimeter(self):
side1 = self.side_length(self.p1, self.p2)
side2 = self.side_length(self.p2, self.p3)
side3 = self.side_length(self.p3, self.p4)
side4 = self.side_length(self.p4, self.p1)
return side1 + side2 + side3 + side4
Right now I'm calling the side_length method by explicitly telling it to use the quadrilateral's points, but is there a way to implicitly use just "p1" and "p2" without the need to tell it to use the quadrilateral's points (I'm using q.p1 and q.p2 when I just want to use p1 and p2 and imply python to use the quadrilateral's points)? I've realized it's basically a static method, and I want it to use the class fields rather than take in any point.
q = Quadrilateral(p1, p2, p3, p4)
print(q.to_string())
print("Side length between " + q.p1.to_string() + " and " + q.p2.to_string() + ": " + str(q.side_length(q.p1, q.p2)))
print("Perimeter is: " + str(q.perimeter()))
I also have other redundancy issues- like are there better ways to go about initially defining the points p1, p2, p3, p4 in the quadrilateral class, and is there a simpler way to compute the perimeter of the quadrilateral?

This looks like a nearly perfect question for https://codereview.stackexchange.com, but I'll put my edits anyway.
To note at start:
avoid classes until you can
try writing less junk/support/boilerplate code and more fun code
recylce whatever python has to offer before making something new
write at least a tiny test or an assert
skip the rules above while learning
Here is my edit of your code, hope you find some useful ideas/clues:
# not needed
#import math
class Point:
#FIXME: delete this and do some reading on class attributes
# x = 0.0
# y = 0.0
def __init__(self, x, y):
self.x = x
self.y = y
#FIXME: use __str__ method for this
# def to_string(self):
# return "{X: " + str(self.x) + ", Y: " + str(self.y) + "}"
def distance(self, p):
return ((self.x - p.x) ** 2 + (self.y - p.y) ** 2) ** .5
class Quadrilateral:
# FIXME: same as in class Point
# p1 = 0
# p2 = 0
# p3 = 0
# p4 = 0
# FIXED: you do not want anything undefined for constructor
# def __init__(self, p1=Point(), p2=Point(), p3=Point(), p4=Point()):
def __init__(self, p1, p2, p3, p4):
# saving some typing
self.points = [p1, p2, p3, p4]
# self.p2 = p2
# self.p3 = p3
# self.p4 = p4
# print("Quadrilateral Constructor")
# FIXME: please make some life easier
# def to_string(self):
# return "{P1: " + self.p1.ToString() + "}, " + "{P2: " + self.p2.ToString() + "}, " + \
# "{P3: " + self.p3.ToString() + "}," + "{P4: " + self.p4.ToString() + "}"
def side(self, vertex_n):
# TODO: elaborate some protection against wrong *vertex_n*
a, b = self.points[vertex_n], self.points[vertex_n-1]
return a.distance(b)
#property
def perimeter(self):
return sum([self.side(i) for i in range(4)])
# any duplicated code is a sign of danger: somehtign is going wrong!
#side1 = self.side_length(self.p1, self.p2)
#side2 = self.side_length(self.p2, self.p3)
#side3 = self.side_length(self.p3, self.p4)
#side4 = self.side_length(self.p4, self.p1)
#return side1 + side2 + side3 + side4
pts = [Point(x, y) for x, y in [(0,0), (1,0), (1,1), (0,1)]]
assert Quadrilateral(*pts).perimeter == 4
As an excercise you can make a parent class Polygon and inherit Quadrilateral from it.
Update: in the original answer I wrote:
# COMMENT: class Point better be a namedtuple, please google it and use it
That sounded too categorical to a respected reader, who noted:
Whether Point is mutable depends on what you want to do with points in your app. Usually go with immutable by default if you don't have any reason not to.
In other words, it is prudent not to rush into namedtuple unless you are sure the the data structure should not change. To me that sounds overly protective, but you cannot deny the logic your choice of data structure depends on how you plan to use it.
For converting something to namedtuple please see a nice example of refactoring at Raymond Hettinger - Beyond PEP 8 -- Best practices for beautiful intelligible code.
Other suggested direction for thought is using dataclass, available in standard library starting Python 3.7.

Is this what you mean?
def side_length(self, side):
if side == 1:
vertex1 = self.p1
vertex2 = self.p2
elif side == 2:
vertex1 = self.p2
vertex2 = self.p3
elif side == 3:
vertex1 = self.p3
vertex2 = self.p4
elif side == 4:
vertex1 = self.p4
vertex2 = self.p1
else:
print("Error! No side {}".format(side))
return None
return math.sqrt((vertex2.x - vertex1.x)**2 + (vertex2.y - vertex1.y)**2)

I'd suggest changing how you keep track of your points. Rather than having four separate attributes with numbers in the names, use a list. Then you can pass an index to your side_lengths method:
def __init__(self, p1, p2, p3, p4):
self.points = [p1, p2, p3, p4]
def side_length(self, n):
vertex1 = self.points[n]
vertex2 = self.points[n-1] # if the index becomes -1, that's ok, it will wrap around
return math.sqrt((vertex2.x - vertex1.x)**2 + (vertex2.y - vertex1.y)**2)
def perimeter(self):
return sum(side_length(i) for i in range(4))
I'd also suggest that you rename your to_string functions to __str__, as Python will call that method automatically for you in some cases (such as when you're printing an object).

Related

I am studying Python Class and method. But I don't know whats wrong with my code. Need help. Result for P3, P4 do not work

I am studying Python Class and method. But I don't know whats wrong with my code. Need help. Result for P3, P4 do not work.
I made Point class and Rectangle class. The code works out for r1 = Rectangle(p1, p2). But, does not work out for r2 = Rectangle(p3, p4). Can someone please explain me what is wrong and how to fix the code. Thank you
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
class Rectangle:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def get_area(self):
w = max(p1.x, p2.x)
h = max(p1.y, p2.y)
return w*h
def get_perimeter(self):
w = max(p1.x, p2.x)
h = max(p1.y, p2.y)
if w == h:
return w*4
else:
return abs(p2.x - p1.x)*2 + abs(p2.y - p1.y)*2
def is_square(self):
w = max(p1.x, p2.x)
h = max(p1.y, p2.y)
if w == h:
return True
else:
return False
p1 = Point(1,3)
p2 = Point(3,1)
r1 = Rectangle(p1, p2)
print(r1.get_area())
print(r1.get_perimeter())
print(r1.is_square())
p3 = Point(3,7)
p4 = Point(6,4)
r2 = Rectangle(p3, p4)
print(r2.get_area())
print(r2.get_perimeter())
print(r2.is_square())
Inside of the Rectangle class you are accessing the global scope p1 and p2 variables. All the class functions should use self.p1 and self.p2

check whether two circle touch each other externally and only at one point

i need to find out whether two circles which are c1 and c2 touch each other externally and at only one point by using Circle class.
I created method which is touches. The method should return a boolean value and it needs to be called like this c1.touches(c2)
this formula to check circles touch or intersect with each other formula
this is my code
import math
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
self.r = r
def touches(self):
dist_between_centers = math.sqrt((self.c1.x - self.c2.x)^(2 + (self.c1.y - self.c2.y)^2))
if dist_between_centers == (self.c1.r + self.c2.r):
print("True")
elif dist_between_centers > (self.c1.r + self.c2.r):
print("False")
else:
print("False")
c1 = Circle(2,3,12)
c2 = Circle(15, 28, 10)
c1.touches(c2)
However i am getting error like this, TypeError: touches() takes 1 positional argument but 2 were given
Seems like you mix up with usage self, c1 and c2 in def touches
You should pass c2 as parameter to def touches(circle). Inside method you should refer the first circle as self rather self.c1 and the second as circle rather than self.c2
Final code like this
import math
class Circle:
def __init__(self, x, y, r):
self.x = x
self.y = y
self.r = r
def touches(self, circle):
dist_between_centers = math.sqrt((self.x - circle.x)^2 + (self.y - circle.y)^2)
if dist_between_centers == (self.r + circle.r):
print("True")
elif dist_between_centers > (self.r + circle.r):
print("False")
else:
print("False")
c1 = Circle(2, 3, 12)
c2 = Circle(15, 28, 10)
c1.touches(c2)

adding two values from different classes

class PolygonInteriorAngle(object):
def __init__(self, x):
self.x = self
def FindInteriorAngle(self):
degrees = int((x - 2) * 180)
interior = int(degrees / x)
return interior
def PrintInterior(self):
print("interior angle: " + str(self.FindInteriorAngle()))
class PolygonExteriorAngle(object):
def __init__(self, x):
self.x = self
def FindExteriorAngle(self):
exterior = int(360 / x)
return exterior
def PrintExterior(self):
print("exterior angle: " + str(self.FindExteriorAngle()))
class AngleAddition(object):
def __init__(self, x):
self.x = self
def Add(self):
sum = int(interior + exterior)
return sum
def PrintAdd(self):
print("sum of interior and exterior: " + str(self.Add()))
if __name__ == "__main__":
x = int(input("enter: "))
intObj = PolygonInteriorAngle(x)
intObj.FindInteriorAngle()
intObj.PrintInterior()
extObj = PolygonExteriorAngle(x)
extObj.FindExteriorAngle()
extObj.PrintExterior()
addObj = AngleAddition(x)
addObj.Add()
addObj.PrintAdd()
both classes (PolygonInteriorAngle and PolygonExteriorAngle) work fine, they print what they're expected to. what i want to do in the AngleAddition class is to add both of the final values (interior and exterior) that you get from the other two classes. i think it's pretty inefficient putting them in different classes, but that's what my computer science teacher asked me to and i'm not sure how to use a value from another class in a new class. if you do x = 6, you'll get 120 and 60. with AngleAddition i want to print 180.
General comments:
You need to check very carefully the variables in your instance methods. example:
def Add(self):
sum = int(interior + exterior)
return sum
Both interior and exterior are not specified in the instance method arguments.
I agree with User: Tim Roberts's comment. Either you make a base class "angle" and let the "interior/exterior angle" class inherit from the "angle" class, or just use angle class for both interior/exterior angles. It depends on how you want to write the __init__ method for interior/exterior angle classes.
Note that I overrode the magic method to perform the sum of two angles. There are other ways of doing that cause in my way the + operator is being redefined.
Anyways:
class AngleBase:
def __init__(self,angle):
self.angle=angle
def __add__(self,angleobj):
return int(self.angle+angleobj.angle)
class PolygonInteriorAngle(AngleBase):
def __init__(self, side):
degrees = int((side - 2) * 180)
interior = int(degrees / side)
AngleBase.__init__(self,interior)
def FindInteriorAngle(self):
return self.angle
def PrintInterior(self):
print("interior angle: " + str(self.angle))
class PolygonExteriorAngle(AngleBase):
def __init__(self, side):
exterior = int(360 / side)
AngleBase.__init__(self,exterior)
def FindExteriorAngle(self):
return self.angle
def PrintExterior(self):
print("exterior angle: " + str(self.angle))
class AngleAddition:
def __init__(self, x):
pass
def Add(self,interior, exterior):
sum = int(interior + exterior)
return sum
def PrintAdd(self,interior, exterior):
print("sum of interior and exterior: " + str(self.Add(interior, exterior)))
if __name__ == "__main__":
x = int(input("enter: "))
intObj = PolygonInteriorAngle(x)
print(intObj.angle)
intObj.FindInteriorAngle()
intObj.PrintInterior()
extObj = PolygonExteriorAngle(x)
extObj.FindExteriorAngle()
extObj.PrintExterior()
addObj = AngleAddition(x)
addObj.Add(extObj,intObj)
addObj.PrintAdd(extObj,intObj)

Im trying to calculate cartesian distance between two points using classes

import random
class point:
def __init__(self,p):
self.p = p
def fill_point(self):
x = random.uniform(0,100)
y = random.uniform(0,100)
z = random.uniform(0,100)
self.p = [x,y,z]
return self.p
def distance_between_points(self,p1,p2):
D = ((self.p1[0]-self.p2[0])**2 + (self.p1[1]-self.p2[1])**2 + (self.p1[2]-self.p2[2])**2)**(1/2)
return D
def main():
point1 = point(fill_point())
point2 = point(fill_point())
Distance = distance_between_points(point1,point2)
print(Distance)
main()
im quite new to classes and am having a hard time understanding what im doing wrong.
import random
from math import sqrt
class Point:
def __init__(self, name='anonym_point',x=0,y=0,z=0):
self.name = name
self.x = x
self.y = y
self.z = z
#property
def coord(self):
return (self.x, self.y, self.z)
def __repr__(self):
return ("{} has coordinate {}  {}  {}".format(self.name, self.x, self.y, self.z))
def makepoint(namepoint):
return Point(namepoint, random.uniform(0,100), random.uniform(0,100), random.uniform(0,100))
def distance_between_points(p1,p2):
dist = sqrt((p2.x-p1.x)**2 + (p2.y-p1.y)**2 + (p2.z-p1.z)**2)
print("distance between point ",p1.name," and the point ",p2.name," : ",dist)
point1 = makepoint("p1")
point2 = makepoint("p2")
print(point1)
print(point2)
Distance = distance_between_points(point1,point2)
The issue is that you are accessing the class method just as a normal method, you need to initialize class object first then call the method by class object you created, again use only the variables you are sure you need,. keeping code easy for you because I think you already know what you needed I did this
import random
class Point:
def fill_point(self):
x = random.uniform(0,100)
y = random.uniform(0,100)
z = random.uniform(0,100)
p = [x,y,z]
return p
def distance_between_points(self,p1,p2):
D = ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2 + (p1[2]-p2[2])**2)**(1/2)
return D
def main():
obj = Point()
point1 = obj.fill_point()
point2 = obj.fill_point()
distance = obj.distance_between_points(point1,point2)
print(distance)
main()
it would not kill if you try to understand python classes better, python best naming, etc...
i think that what you are trying to do is something like this.
import math
class Point():
def __init__(self,x,y,z):
self.coordinates = (x,y,z)
def distance(self,point):
return math.sqrt((point.coordinates[0] - self.coordinates[0])**2 + (point.coordinates[1] - self.coordinates[1])**2 + (point.coordinates[1] - self.coordinates[1])**2)
a = Point(4,2,8)
b = Point(2,7,3)
print(a.distance(b))
what you are doing by executing this python code is simply creating a "Point" class, this point class has an attribute (coordinates) that contains its coordinates into a tuple.
so to create a point object you just have to use this code point = Point(x_coordinates,y_coordinates,z_coordinates).
In the last line the code calculates the distance between two points, in that case you are calculating the distance of "b" respect to "a", but you could also do viceversa by doing something like this: print(b.distance(a))
to calculate the distance between random point all you have to do is this:
import math, random
class Point():
def __init__(self,x,y,z):
self.coordinates = (x,y,z)
def distance(self,point):
return math.sqrt((point.coordinates[0] - self.coordinates[0])**2 + (point.coordinates[1] - self.coordinates[1])**2 + (point.coordinates[1] - self.coordinates[1])**2)
r = lambda: random.uniform(0,100)
a = Point(r(),r(),r())
b = Point(r(),r(),r())
print(a.distance(b))
this is how to do that, but i really don't understand why you should calculate the distance between two random numbers

Having some trouble with assignment

I need to make some functions that perform basic algebraic operations and a couple of other things on quaternions(these are basically complex numbers of the form a + xi + yj + z*k). I first created a class which contain some attributes, and whenever i create an instance with it I get a quaternion. However, when I tried implementing the functions I mentioned before I keep getting error messages. Anyway, here is my code in its full:
from math import *
class Quaternion(object):
def __init__(self, re, xc, yc, zc):
self.a = re
self.x = xc
self.y = yc
self.z = zc
def __str__(self):
return str(self.a) + "+" + str(self.x) + "i" + "+" + str(self.y) + "j" + "+" + str(self.z) + "k"
def add(self, q):
self.a = self.a + q.a
self.x = self.x + q.x
self.y = self.y + q.y
self.z = self.z + q.z
def mul(self, q):
self.a = self.a*q.a - self.x*q.x - self.y*q.y - self.z*q.z
self.x = self.a*q.x + self.x*q.a + self.y*q.z - self.z*q.y
self.y = self.a*q.y + self.y*q.a + self.z*q.x - self.x*q.z
self.z = self.a*q.z + self.z*q.a + self.x*q.y - self.y*q.x
def conjugate(self):
self.a = self.a
self.x = -1 * self.x
self.y = -1 * self.y
self.z = -1 * self.z
def norm(self):
return sqrt((self.a)**2+(self.x)**2+(self.y)**2+(self.z)**2)
def reciprocal(self):
p1 = self.conjugate()
self.a = p1.a * (1/(self.norm())**2)
self.x = p1.x * (1/(self.norm())**2)
self.y = p1.y * (1/(self.norm())**2)
self.z = p1.z * (1/(self.norm())**2)
def main():
p = Quaternion(2, 0, -3, 0)
q = Quaternion(0, 1, 1, -2)
print "p =", p
print "q =", q
print "p + q =", p.add(q)
print "p * q =", p.mul(q)
print "conjugate of p is", p.conjugate()
print "norm of p is", p.norm()
print "reciprocal of p is", p.reciprocal()
print "p x reciprocal(p) =", p.mul(p.reciprocal)
if __name__ == '__main__':
main()
Now, whenever I run the module(so it then executes the commands under the main function), I get this:
p = 2+0i+-3j+0k
q = 0+1i+1j+-2k
p + q = None
p * q = None
conjugate of p is None
norm of p is 9.11043357914
reciprocal of p is
The only thing it does right is printing out the two quaternions p and q, but none of the other functions/methods seem to be working properly(the norm does give a value, but it isnt the right one for some reason).
Before I forget, let me quickly say what each functions needs to do:
add(self, q) needs to add 2 quaternions together.
mul(self, q) needs to multiply 2 quaternions.
conjugate(self) needs to transform a given quaternion a + xi + yj + zk into this form: a - xi - yj - zk.
norm(self) and reciprocal(self) need to respectively return the norm and reciprocal of the quaternion
You are performing the math correctly in principle, but you are not returning a new object where you should be.
For example, let's look at add(). When you sum two objects, you are expecting the return value to be a third object of the same type, which you are printing. Your add() function does not return anything (in Python this is equivalent to returning None), and instead unexpectedly modifies the object it is called on. Instead, do this:
def add(self, q):
return Quaternion(self.a + q.a,
self.x + q.x,
self.y + q.y,
self.z + q.z)
Do the same for the other methods. If you want to use + and * operators in your code, change the method names to __add__ and __mul__. To do in-place addition and multiplication using += and *= operators, sort of like your current methods are doing, rename the current methods to __iadd__ and __imul__, but don't forget to return self at the end.
You get the None values because you did not specify a return value. Add a
return self
at the end of add, mul, conjugate and reciprocal. (If it is intended that these methods change the value of p, and do not just compute a new Quaternion while leaving p untouched.)

Categories

Resources