Best Practice for passing arguments in a class Python - python

I'm new to OOP and pretty sure the way I'm about to solve something is not the smartest.
I'm building a custom K-means Algorithm and want to give options for different distance functions.
The way I am about to solve it, is building if statements and calculate the distances respectively.
For Example:
class ExampleDist():
def __init__(self, measure="euklid"):
self.measure = measure
def euklid_distance(x_1,x_2):
dist = np.linalg.norm(x_1-x_2)
return dist
def abs_distance(x_1,x_2):
dist = np.absolute(x_1-x_2)
return dist
def dist(self, x_1, x_2):
if(self.measure == "euklid"):
self.dist = euklid_distance(x_1,x_2)
elif(self.measure == "abs"):
self.dist = abs_distance(x_1,x_2)
dist1 = ExampleDist(measure = "euklid")
dist1.dist(np.array([1,1]),np.array([0,2]))
However in the K-Mean Algorithm I would have to copy-paste the whole loop where the distances between the datapoints and centroid is calculated and only change the distances.
Copy pasting is never a good solution, so I would hope to have a solution that automatically passes on which distance measure I want to use.
Like so (pseudo code):
class ExampleDist():
def __init__(self, measure="euklid"):
self.measure = measure
def euklid_distance(x_1,x_2):
dist = np.linalg.norm(x_1-x_2)
return dist
def abs_distance(x_1,x_2):
dist = np.absolute(x_1-x_2)
return dist
def dist(self, x_1, x_2):
self.dist = [self.meassure]_distance(x_1,x_2)
dist1 = ExampleDist(measure = "euklid")
dist1.dist(np.array([1,1]),np.array([0,2]))

Why not just create a single dist function, like:
class ExampleDist():
def __init__(self, measure="euklid"):
self.measure = measure
def dist(self, x_1, x_2):
if self.measure == 'euklid':
return np.linalg.norm(x_1-x_2)
elif self.measure == 'absolute':
return np.absolute(x_1-x_2)
else:
return None

Assuming that all the distance functions you are going to have will accept the same arguments (x1 and x2), you can use a dict to map between the distance type and the distance function.
This is one of the most expandable and flexible ways to achieve this.
class ExampleDist():
_distance_funcs = {'euclid': np.linalg.norm,
'abs': np.absolute}
# or implement your own wrappers as in your example
def dist(self, x1, x2, measure):
try:
return self._distance_funcs[measure](x1, x2)
except KeyError:
raise ValueError(f"`measure` should be one of {', '.join(self._distance_funcs.keys())}")

Related

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

How to call variables from the __init__ method of classes (Python)?

I'm new to programming and I'm confused as to how you call a method/parameter that is defined within a class in Python 2. For example (with obstacle being a previous class made),
class Block(Obstacle):
def __init__(self, origin, end, detection=9.):
self.type = 'block'
self.origin = origin
self.end = end
x1 = self.origin[0]
y1 = self.origin[1]
x2 = self.end[0]
y2 = self.end[1]
def __str__(self):
return "block obstacle"
When I generate an environment, I define different x1, y1, x2 and y2 values (essentially signifying the coordinate points of the corners of the block). I have another later method where I needs the values of x1, y1, x2 and y2 in calculating something, but I'm confused as to how I actually call them into this new function? What parameters would I put in this new function?
import math
I would make x1 --> self.x1 so you can have it as an object variable.
Inside the class object you can define these functions for calculation as an example.
def calculate_centre(self):
self.centre_x = self.x2 - self.x1
self.centre_y = self.y2 - self.y1
self.centre = (centre_x, centre_y)
def distance_between_block_centres(self, other):
block_x, block_y = other.centre
distance = math.sqrt((self.centre_x - block_x)**2 + (self.centre_y - block_y)**2)
return distance
block = Block(stuff)
block_2 = Block(other_stuff)
if you want to call these function using the objects youve created:
block.calculate_centre()
block_2.calculate_centre()
distance_between = block.distance_between_block_centres(block_2)
And even external to your object call the variables:
print block.centre
#>>> (3, 5)
Lastly you can run the calculations of the centre without having to call it every time you create your object if your put it in def __init__():
self.calculate_centre()

Storing and checking a boolean in a python class

So I was trying to create an object which basically has a constructor that takes two coordinates, xcoord and ycoord. I further created methods which shift the coordinates and I have to check if the point is valid or not(criteria for validity is if the coordinates are out of a specified range it should return False else True).
Problem:
My class only returns the validity for the initial points, not the shifted points.
What do I need to correct my code?
Code:
class Point:
MaxScreenSize=10
def __init__(self,x,y):
self.xcoord=x
self.ycoord=y
if 0>self.xcoord or self.xcoord>Point.MaxScreenSize or 0>self.ycoord or self.ycoord>Point.MaxScreenSize:
Point.isValidPt=False
else:
Point.isValidPt=True
def translateX(self,shiftX):
self.xcoord=self.xcoord+shiftX
def translateY(self,shiftY):
self.ycoord=self.ycoord+shiftY
Test Code:
I tried my code and it only returns the isValidFunction variable for my initial points (gives me True instead of False for following code)
p=Point(9,2)
p.translateX(20)
p.translateY(10)
p.isValidPt
Your isValidPt is only calculated when the class is instantiated. Instead try something like:
Code:
class Point:
MaxScreenSize = 10
def __init__(self, x, y):
self.xcoord = x
self.ycoord = y
def translateX(self, shiftX):
self.xcoord = self.xcoord + shiftX
def translateY(self, shiftY):
self.ycoord = self.ycoord + shiftY
#property
def isValidPt(self):
return (
0 <= self.xcoord <= Point.MaxScreenSize and
0 <= self.ycoord <= Point.MaxScreenSize
)
Test Code:
p = Point(9, 2)
p.translateX(20)
p.translateY(10)
print(p.isValidPt)
Result:
False
Constructor is used basically for initiate the values. In your case the constructor checks the initial value and set the validate flag. ie, isValidPt.
It will be True for the scope of p object you created. So you have to create a validate function and call the validate function on both init and shift functions.
Check the following
class Point:
MaxScreenSize=10
def __init__(self,x,y):
self.xcoord=x
self.ycoord=y
self.validate()
def validate(self):
if 0>self.xcoord or self.xcoord>Point.MaxScreenSize or 0>self.ycoord or self.ycoord>Point.MaxScreenSize:
Point.isValidPt=False
else:
Point.isValidPt=True
def translateX(self,shiftX):
self.xcoord=self.xcoord+shiftX
self.validate()
def translateY(self,shiftY):
self.ycoord=self.ycoord+shiftY
self.validate()
in the above code each time the validation will perform and update the values
of isValidPt.

How would I get this value? (Python)

I have
class Point:
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
def __init__(self, initP, initW, initH):
self.__location = initP
self.__width = initW
self.__height = initH
def getWidth(self):
return self.__width
def getHeight(self):
return self.__height
def getLocation(self):
return self.__location
#---------------------------------------------------------------
#string
def __str__(self):
return "x=" + str(self.__location.x) + ", y=" + str(self.__location.y) +", Width=" + str(self.getWidth()) + ", Height=" +str(self.getHeight())
def area(self):
return self.getWidth() * self.getHeight()
def calculatePerimeter(self):
return self.getWidth()*2 +self.getHeight()*2
def transpose(self):
temp = self.__width
self.__width = self.__height
self.__height = temp
def encloses(self, otherP):
return ((self.getWidth() + self.getLocation().getX()) > otherP.getX()\
and (self.getLocation().getX()) <otherP.getX() \
and (self.getHeight() + self.getLocation().getY()) >otherP.getY()\
and self.getLocation().getY() < otherP.getY())
def computeDiagonal(self):
d = (self.getWidth()**2 + self.getHeight()**2) ** 0.5
return d
def detectCollision(firstRectangle, secondRectangle):
print(firstRectangle.getWidth())
print(secondRectangle)
first = Rectangle(Point(1,0), 4, 3)
second = Rectangle(Point(4,0), 4, 3)
Rectangle.detectCollision(first, second)
I am trying to detect a collision. I'm a bit stuck. (detectCollision)
I am having trouble getting the value from the point class to the rectangle class.
Does anybody have any idea?
The function detectCollision is wrong. I was testing and I could get the width, and the height with getHeight() but I could not get the values inside Point.
I am having trouble getting the value from the point class to the rectangle class.
I think you need to read through a good tutorial on classes. Maybe the chapter in the official tutorial, or maybe a third-party tutorial. StackOverflow is not a good place to learn basic concepts.
You don't actually want to get a value from the point class, you want to get the value from a particular point instance. After all, there are lots of points in the world, and each one has different x and y values, and you're trying to check if some particular point has collided with the rectangle.
How do you know which instance? You take one as a parameter. And then you can access that object's members, methods, etc., just like you do with a string or any other object.
class Rectangle(object):
# existing stuff
def collision_check(self, point):
return (self.left <= point.getX() <= self.right and
self.top <= point.getY() <= self.bottom)
That's it.
Except that you probably don't want getX and getY methods in the first place; better to just do point.x and point.y.
Also, I've obviously had to make some assumptions about how you defined Rectangle (left/top/bottom/right? left/right/width/top? topleftpoint/bottomrightpoint?) and about what you mean by "collision" (hitting the edge of the rectangle, or the interior of the rectangle?), etc., since you didn't explain any of that. But hopefully you can adapt this to whatever your actual design is.
So, how do you use this? You just pass a point as an argument to the method, same as you do with, say, len:
>>> rect = Rectangle(10, 10, 20, 20)
>>> point1 = Point(5, 5)
>>> rect.collision_check(point1)
False
>>> point2 = Point(15, 15)
>>> rect.collision_check(point2)
True
Now that you've shown us more of your code, it looks like you're trying to collision-check two rectangles—and, more importantly, your problem is that your rectangle uses a Point as its top-left origin, and you don't know how to access the coordinations of that.
From your description, "I was testing and I could get the width, and the height with getHeight() but I could not get the values inside Point", you seem to still be missing the key issue here. You don't want to get the values inside Point. Point is a class—a factory for creating actual point objects. You want to get the values inside one of those actual point objects, the one you've stored in a rectangle object's __location and made available through a getLocation method. (As I already explained, you should get rid of those getter methods and just have a location attribute, but let's forget that for now.)
So, the way you get the particular point object you're interested in is to call getLocation() on the rectangle, and then the way you get the x and y values for that particular point object is to call its getX and getY methods. So, here's an example of using all those methods:
firstLocation = firstRectangle.getLocation()
firstLeft = firstLocation.getX()
Or you can combine those calls into one expression:
firstLeft = firstRectangle.getLocation().getX()
So, you can do something like this:
def detectCollision(firstRectangle, secondRectangle):
firstLeft = firstRectangle.getLocation().getX()
firstRight = firstLeft + firstRectangle.getWidth()
# similar code for top and bottom, and for second rectangle
return ((firstLeft <= secondLeft <= firstRight or
firstLeft <= secondRight <= firstRight) and
(firstTop <= secondTop <= firstBottom or
firstTop <= secondBottom <= firstBottom))

line 60, in make_tuple return tuple(l) TypeError: iter() returned non-iterator of type 'Vector'

I am new to Vectors and making classes. I am trying to construct my own vector class but when i pass it through my code which is:
position += heading*distance_moved
where position and heading are both vectors. heading is normalized. my goal is to repeat my code until position = destination.
What is wrong with this class?
import math
class Vector(object):
#defaults are set at 0.0 for x and y
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
#allows us to return a string for print
def __str__(self):
return "(%s, %s)"%(self.x, self.y)
# from_points generates a vector between 2 pairs of (x,y) coordinates
#classmethod
def from_points(cls, P1, P2):
return cls(P2[0] - P1[0], P2[1] - P1[1])
#calculate magnitude(distance of the line from points a to points b
def get_magnitude(self):
return math.sqrt(self.x**2+self.y**2)
#normalizes the vector (divides it by a magnitude and finds the direction)
def normalize(self):
magnitude = self.get_magnitude()
self.x/= magnitude
self.y/= magnitude
#adds two vectors and returns the results(a new line from start of line ab to end of line bc)
def __add__(self, rhs):
return Vector(self.x +rhs.x, self.y+rhs.y)
#subtracts two vectors
def __sub__(self, rhs):
return Vector(self.x - rhs.x, self.y-rhs.y)
#negates or returns a vector back in the opposite direction
def __neg__(self):
return Vector(-self.x, -self.y)
#multiply the vector (scales its size) multiplying by negative reverses the direction
def __mul__(self, scalar):
return Vector(self.x*scalar, self.y*scalar)
#divides the vector (scales its size down)
def __div__(self, scalar):
return Vector(self.x/scalar, self.y/scalar)
#iterator
def __iter__(self):
return self
#next
def next(self):
self.current += 1
return self.current - 1
#turns a list into a tuple
def make_tuple(l):
return tuple(l)
I guess you are using python 3.x, because I've got a similar error.
I'm also new on making class, but it would be nice to share what I learned :)
In 3.x, use __next__() instead of next() in the definition of classes.
The error haven't occurred after I renamed it in your code, but I got another problem, "'Vector' object has no attribute 'current'" :)
I think it might be better for you to understand iterators (and class?) more.
A simplest example is:
class Count:
def __init__(self, n):
self.max = n
def __iter__(self):
self.count = 0
return self
def __next__(self):
if self.count == self.max:
raise StopIteration
self.count += 1
return self.count - 1
if __name__ == '__main__':
c = Count(4)
for i in c:
print(i, end = ',')
and the outputs are 0,1,2,3,.
With a vector class, I want to iterate the components of the vector. So:
def __iter__(self):
self.count = 0
self.list = [self.x, self.y, self.z] # for three dimension
return self
def __next__(self):
if self.count == len(self.list):
raise StopIteration
self.count += 1
return self.list[self.count - 1]
and the iterator outputs the sequence x, y, z.
Note that the most important feature of iterators is to give the sequence step by step without creating whole list. So it is not very good idea to make self.list if the sequence will be very long.
More details here: python tutorial
The first argument that's being passed into make_tuple is your Vector instance (it's the same self argument that you put everywhere).
You have to pass in what you want to turn into a tuple, which is probably your x and y coordinates:
def make_tuple(self):
return (self.x, self.y)

Categories

Resources