So I have a point class and a line class that both have a scale method.
class Point:
def __init__(self, x, y):
if not isinstance(x, float):
raise Error("Parameter \"x\" illegal.")
self.x = x
if not isinstance(y, float):
raise Error ("Parameter \"y\" illegal.")
self.y = y
def scale(self, f):
if not isinstance(f, float):
raise Error("Parameter \"f\" illegal.")
self.x = f * self.x
self.y = f * self.y
def __str__(self):
return '%d %d' % (int(round(self.x)), int(round(self.y)))
class Line:
def __init__(self, point0, point1):
self.point0 = point0
self.point1 = point1
def scale(self, factor):
if not isinstance(factor, float):
raise Error("Parameter \"factor\" illegal.")
self.point0.scale(factor)
self.point1.scale(factor)
def __str__(self):
return "%s %s" % (self.point0, self.point1)
So one of the tests I do on this code is to check for a shallow copy which I do in this test code.
p0.scale(2.0)
p1.scale(2.0)
print line
The problem is the print line gives me 0 2 4 6 and it should give me 0 1 2 3. So why is it printing multiples of 2 instead? The scale method is supposed to return the scaled values and for all the other test cases it prints the expected values however just with this test code it prints values I didn't expect. Here's how the values of p0 and p1 are set up:
print '********** Line'
print '*** constructor'
p0 = Point(0.0, 1.0)
p1 = Point(2.0, 3.0)
line = Line(p0,p1)
print line
In your __init__ method for Line, you are assigning the names self.point0 and self.point1 to the two points that are passed in. This does not make a new copy, only gives the objects in memory another name. If you change this method to
def __init__(self, point0, point1):
self.point0 = Point(point0.x, point0.y)
self.point1 = Point(point1.x, point1.y)
then everything should work as intended. Or, you can use the copy module:
from copy import copy
class Line:
def __init__(self, point0, point1):
self.point0 = copy(point0)
self.point1 = copy(point1)
You could also define your own __copy__ and __deepcopy__ methods on your Point class.
def __copy__(self):
return type(self)(self.x, self.y)
def __deepcopy__(self, memo):
return type(self)(self.x, self.y)
You can look at this question for more information.
Printing line after scaling p0,p1 by 2 multiply x,y pairs for p0, p1. Line instance values of point0 and point1 pointing to instances of Point which is p0 and p1 appropriately, as result of print line you can see updated value of x,y of each point.
p0 = Point(0,1)
p1 = Point(2,3)
line = Line(p0, p1)
print line # 0 1 2 3
p0.scale(2.0)
p1.scale(2.0)
print line # 0 2 4 6
Related
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__'
I am trying to pass many arguments to a constructor but when i try to call a method; i have an error. I did instantiate my class; but i get an error. What i have is for example in my main function is:
Points = Line(1,1,2,3)
a= Line.slope()
print("slope",a)
in my class i have
class Line(object):
def __init__(self,X1,Y1,X2,Y2):
self.k1=X1
self.k2=Y1
self.k3=X2
self.k4=Y2
''' Compute the slope of the line'''
def slope(self):
x1, y1, x2, y2 = self.k1, self.k2, self.k3, self.k4
try:
return (float(y2)-y1)/(float(x2)-x1)
except ZeroDivisionError:
# line is vertical
return None
'''Get the y intercept of a line segment'''
def yintercept(self, slope):
if slope != None:
x, y = self.k1, self.k2
return y - self.slope * x
else:
return None
'''Find Y cord using line equation'''
def solve_for_y(self, x, slope, yintercept):
if slope != None and yintercept != None:
return float(slope) * x + float(yintercept)
else:
raise Exception("impossible to get it")
'''Find X cord using line equation'''
def solve_for_x(self, y, slope, yintercept):
if slope != 0 and slope:
return float((y - float(yintercept))) / float(slope)
else:
raise Exception("Imposssible to get it ")
The error is have is: TypeError: Compute missing 1 required positional argument: 'self'.
I am not what the problem is.
That is my complete code
You've got a few issues with your existing class.
class TestClass():
def __init__(self,x1,x2,x3):
self.k1 = x1
self.k2 = x2
self.k3 = x3
def Compute(self):
return self.k1 * self.k2 + self.k3
>>> test = TestClass(2,2,3)
>>> test.Compute()
7
The method _init_ should be __init__ (note the double underscores)
Your Compute method should use the member variables k instead of the input variables x because the x versions don't exist in that scope
Your capitalization of Compute was incorrect when you called the method.
You are missing a : after your class declaration and the __init__ function definition
Totally rewritten code:
HOR = [0, 1, 3, 6]
VER = [0,10,20,100,1000]
class Pos:
def __init__(self, x, y):
if (x in HOR) and (y in VER):
self.x = x
self.y = y
else:
print "Invalid position: ", x, y
def __str__(self):
return self.x + self.y
def get_x(self):
return self.x
def get_y(self):
return self.y
class list_A1:
def __init__(self): # create a A1 object
self.list_A1 = []
for i in HOR:
for j in VER:
self.list_A1.append(Pos(i,j))
def __str__(self):
d = "list_A1 contains: " + repr(self)
return d # return a string representing the A1
a1 = list_A1()
print a1
Now I get:
list_A1 contains: <__main__.list_A1 object>
but I want to get list of [x,y], for example:
[[1,1],[1,2]...]
I am new to object programming and I don't understand why I can't see these values in the list.
The parentheses after self.A1 mean that you are trying to call it as if it were a function. As the error message tells you, you cannot call a list. The correct syntax would be:
d = "A1 contains: " + str(self.A1)
or, better
d = "A1 contains: {}".format(self.A1)
Also, you seem to be using A1 (the class name) and A (the instance attribute) interchangeably; you should give your classes, attributes and methods more sensible and meaningful names to help avoid this issue.
Do
self.A.append(Pos(x,y))
instead of
self.A1.append(Pos(x,y))
and
d = "A1 contains: " + str(self.A)
Edit:
Implement str(self) and repr(self) of Pos if you want to print them.
A1.A1 is not defined in your code example. If it is a list, just change the line to:
d = "A1 contains: " + str(self.A1)
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)
One of my exercises says to write an add method for Points that works with either a Point object or a tuple:
If the second operand is a Point, the method should return a new Point whose x coordinate is the sum of the x coordinates of the operands, and likewise for the y coordinates.
If the second operand is a tuple, the method should add the first element of the tuple to the x coordinate and the second element to the y coordinate, and return a new Point with the result.
This how far I got and I'm not sure if the tuple portion of my code is accurate. Can someone shed some light how I would call this program for the tuple portion. I think I nailed the first part.
Here is my code:
Class Point():
def__add__(self,other):
if isinstance(other,Point):
return self.add_point(other)
else:
return self.print_point(other)
def add_point(self,other):
totalx = self.x + other.x
totaly = self.y + other.y
total = ('%d, %d') % (totalx, totaly)
return total
def print_point(self):
print ('%d, %d) % (self.x, self.y)
blank = Point()
blank.x = 3
blank.y = 5
blank1 = Point()
blank1.x = 5
blank1.y = 6
That's what I've built so far and I'm not sure how to actually run this with the tuple part. I know if it did blank + blank1 the if portion would run and call the add_point function but how do I initiate the tuple. I'm not sure if I wrote this correctly... please assist.
You can simply derive your class from the tuple (or just implement __getitem__).
class Point(tuple):
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
def __add__(self, other):
return Point(self[0] + other[0], self[1] + other[1])
def __repr__(self):
return 'Point({0}, {1})'.format(self[0], self[1])
p = Point(1, 1)
print p + Point(5, 5) # Point(6, 6)
print p + (5, 5) # Point(6, 6)
Alternatively, if you want to be able to use point.x and point.y syntax, you could implement the following:
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
elif isinstance(other, tuple):
return Point(self.x + other[0], self.y + other[1])
else:
raise TypeError("unsupported operand type(s) for +: 'Point' and '{0}'".format(type(other)))
def __repr__(self):
return u'Point ({0}, {1})'.format(self.x, self.y) #Remove the u if you're using Python 3