How to call variables from the __init__ method of classes (Python)? - 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()

Related

Best Practice for passing arguments in a class 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())}")

Method That Calculates Slope In Python

I'm trying to learn Object Oriented Programming in Python. To do this I need to create a method that calculates the slope of a line, which joins the origin to a point. (I think) we're assuming that the origin is (0,0). For example:
Point(4, 10).slopeFromOrigin()
2.5
Point(12, -3).slopeFromOrigin()
-0.25
Point(-6, 0).slopeFromOrigin()
0
And we're using the equation slope = (Y2 - Y1) / (X2 - X1) to calculate the slope. Also, since dividing by 0 isn't allowed, we need to return None when the method fails. Here's what I tried:
class Point:
#Point class for representing and manipulating x,y coordinates
def __init__(self, initX, initY):
#Create a new point at the given coordinates
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
#define a method called slopeFromOrigin here
def slopeFromOrigin(self):
#set origin values for x and y (0,0)
self.x = 0
self.y = 0
#slope = (Y2 - Y1) / (X2 - X1)
if (Point(x) - self.x) == 0:
return None
else:
return (Point(y) - self.y) / (Point(x) - self.x)
#some tests to check our code
from test import testEqual
testEqual( Point(4, 10).slopeFromOrigin(), 2.5 )
testEqual( Point(5, 10).slopeFromOrigin(), 2 )
testEqual( Point(0, 10).slopeFromOrigin(), None )
testEqual( Point(20, 10).slopeFromOrigin(), 0.5 )
testEqual( Point(20, 20).slopeFromOrigin(), 1 )
testEqual( Point(4, -10).slopeFromOrigin(), -2.5 )
testEqual( Point(-4, -10).slopeFromOrigin(), 2.5 )
testEqual( Point(-6, 0).slopeFromOrigin(), 0 )
As you can see, I'm trying to say that we need the first parameter of Point to be x2, and the second parameter of Point to be y2. I tried it this way and got
NameError: name 'y' is not defined on line 32.
I also tried to get the index values of Point like this:
return (Point[0] - self.y / (Point[1] - self.x)
But that also gave me an error message:
TypeError: 'Point' does not support indexing on line 32
I'm not sure how to get the value of the x and y parameters from Point so that the method works when it's tested. Please share your suggestions if you have any. Thank you.
First problem
self.x = 0
self.y = 0
You just set the current point to the origin. Don't do that. The distance from the origin would then be 0...
Second problem Point(x) and Point(y) are not how you get the values for self.x and self.y.
Then, slope is simply "rise over run". Plus you want to return None when self.x == 0.
So, simply
def slopeFromOrigin(self):
if self.x == 0:
return None
return self.y / self.x
Or even
def slopeFromOrigin(self):
return None if self.x == 0 else self.y / self.x
Or let Python return None on its own
def slopeFromOrigin(self):
if self.x != 0:
return self.y / self.x
I think your confusion lies in that you think you need to somehow define "the origin". If you needed to do that, you would instead have this
origin = Point(0,0)
Point(-6, 0).slopeFromPoint(origin)
if (Point(x) - self.x) == 0:
return None
else:
return (Point(y) - self.y) / (Point(x) - self.x)
As you can see, I'm trying to say that we need the first parameter of Point to be x2, and the second parameter of Point to be y2. I tried it this way and got
NameError: name 'y' is not defined on line 32.
You're trying to access the value of y, which is a global variable that you haven't assigned yet.
I also tried to get the index values of Point like this:
return (Point[0] - self.y / (Point[1] - self.x)
Two problems:
"Point" is a class, not an object (which is an instance of an object).
Even if you've put an object instead, Point is not an list-like object. In order to access an item using index like variableName[index], the class of the variableName must have an implementation for __getitem__(self, key). For example:
>>> class GoodListClass:
... def __init__(self, list):
... self.myList = list
... def __getitem__(self, key):
... return self.myList[key]
...
>>> class BadListClass:
... def __init__(self, list):
... self.myList = list
...
>>> someList = range(10)
>>> goodListObject = GoodListClass(someList)
>>> badListObject = BadListClass(someList)
>>> print(goodListObject[2])
2
>>> print(badListObject[2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: BadListClass instance has no attribute '__getitem__'

Python - NameError: name '' is not defined

I am currently expanding by python skills by programming a procedurally generated dungeon level in text format. I am confused as to why my "intersects" define is not working. Here is the class containing the def:
class Room:
global x1
global x2
global y1
global y2
global w
global h
global centre
def __init__(self,x,y,w,h):
x1 = x
x2 = x + w
y1 = y
y2 = y + h
self.x = x
self.y = y
self.w = w
self.h = h
centre = math.floor((x1 + x2) / 2),math.floor((y1 + y2) / 2)
#function that checks if the rooms intersect by comparing corner pins relative to the x,y tile map
def intersects(self,room):
if x1 <= room.x2 and x2 >= room.x1 and y1 <= room.y2 and room.y2 >= room.y1:
return True
return False
Here is where it's called:
def placeRooms(r):
rooms = []
#Where the room data is stored
for r in range(0,r):
w = minRoomSize + randint(minRoomSize,maxRoomSize)
h = minRoomSize + randint(minRoomSize,maxRoomSize)
x = randint(1,map_width - w - 1) + 1
y = randint(1,map_height - h - 1) + 1
newRoom = Room(x,y,w,h)
failed = False
#for every room generated, this function checks if new room intersects with the last one
for otherRoom in rooms:
if newRoom.intersects(otherRoom):
failed = True
break
if failed == False:
createRoom(newRoom)
rooms.append(newRoom)
Full traceback:
Traceback (most recent call last):
File "C:\Users\Max\Desktop\LiClipse Workspace\testing\RandomDungeon.py", line 78, in <module>
placeRooms(2)
File "C:\Users\Max\Desktop\LiClipse Workspace\testing\RandomDungeon.py", line 65, in placeRooms
if newRoom.intersects(otherRoom):
File "C:\Users\Max\Desktop\LiClipse Workspace\testing\RandomDungeon.py", line 41, in intersects
if x1 <= room.x2 and x2 >= room.x1 and y1 <= room.y2 and room.y2 >= room.y1:
NameError: name 'x1' is not defined
I hope someone can help me understand why this code won't work, thank you.
I have managed to fix the problem. I'm sorry if my question was not defined very well. I have only been learning Python for around 4 weeks and i am used to Java which has a very different syntax. Here is my solution:
def __init__(self,x,y,w,h):
self.x1 = x
self.x2 = x + w
self.y1 = y
self.y2 = y + h
self.x = x
self.y = y
self.w = w
self.h = h
As most previous comments have said, you use global variables that shouldn't be global at all.
The way I understand your code, you meant for x1, x2, y1 and y2 to be attributes of your Room instance, meaning that each room has its own values for x1, x2, y1 and y2. In Python you don't have to declare attributes at the beginning of the class (where you declare all the global variables), you simply need to initialize the attributes in the __init__ method.
This means that you can safely delete all the global lines, and change your __init__ to
def __init__(self,x,y,w,h):
self.x1 = x
self.x2 = x + w
self.y1 = y
self.y2 = y + h
self.w = w
self.h = h
centre = (self.x1 + self.x2) // 2,(self.y1 + self.y2) // 2
(note that you don't need math.floor since you're already dealing with integers, simply use the integer division operator //)
That way you define x1, y1, x2, y2, w, h and center as attributes of your class meaning that each instance has its own values for these variables. In Python, you need to add self. before all calls to attributes of the object itself, so you should also modify intersects to add self. before each access to an attribute of your current object (all the x1, x2, etc. that are not already prefixed by room. in your code).
Also, while we're at it I don't think your intersect function works as intended, but that's another problem :)

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))

How can I rewrite this Python code using iteration?

The code shown below works. I think using recursion is not effective in Python. How can I convert it to for loop version?
def fun(x1, y1, x2, y2, n, r=[]):
if n<1 :
return
r.append( [[x1,y1],[x2,y2]])
x3=(x1+x2)/2.0
y3=(y1+y2)/2.0
fun(x1,y1,x3,y3, n-1)
fun(x3,y3,x2,y2,n-1)
x4=(x2+y2-y3-x3)*0.7+x3;
y4 = (y2 - x2 + x3 - y3)*0.7 + y3;
fun(x3, y3, x4, y4, n - 1);
x3 = (3* x1 + x2)/4;
y3 = (3* y1 + y2)/4;
x2 = (3*x2 + x1)/4;
y2 = (3*y2 + y1)/4;
x4 = (x2*1.7 - y2 + 2*x3 - x3*1.7 + y3)/2;
y4 = (x2 + y2*1.7 - x3 + 2*y3 - 1.7*y3)/2;
fun(x3, y3, x4, y4, n - 1);
return r
print fun(200, 400, 200, 0, 9).__len__()
You may want to consider something like memoize to speed up recursive functions by using more memory. Essentially it stores the results of any call in a cache.
Add the following code
import collections
import functools
class memoized(object):
'''Decorator. Caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned
(not reevaluated).
'''
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if not isinstance(args, collections.Hashable):
# uncacheable. a list, for instance.
# better to not cache than blow up.
return self.func(*args)
if args in self.cache:
return self.cache[args]
else:
value = self.func(*args)
self.cache[args] = value
return value
def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__
def __get__(self, obj, objtype):
'''Support instance methods.'''
return functools.partial(self.__call__, obj)
Then decorate your function like this
#memoized
def fun(x1, y1, x2, y2, n, r=[]):
...
Also be careful with your optional parameter. The list created by r = [] will actually be shared across all calls of f with an r. It is better to do something like this so a new list is created every time.
def fun(x1, y1, x2, y2, n, r=None):
r = [] if r is None else r
A more Pythonic way of getting the length is like this
print len(fun(200, 400, 200, 0, 9))

Categories

Resources