Need to create class Route and Point - python

I'm struggling with this easy assignment. I need to create 2 classes, Point and Route. I need to add/remove Points to/from Route and then calculate the sum distance between the Points in the Route.
So far my code is this:
import math
class Point:
"Two-dimensional points"
def __init__(self, x=0.0, y=0.0):
self._x = x
self._y = y
def __str__(self):
result = "\n".join(["x: %f" % self.x(),
"y: %f" % self.y(),
"rho: %f" % self.rho(),
"theta: %f" % self.theta()])
return result
# Queries
def x(self):
"Abscissa"
return self._x
def y(self):
"Ordinate"
return self._y
def rho(self):
"Distance to origin (0, 0)"
return math.sqrt(self.x()**2 + self.y()**2)
def theta(self):
"Angle to horizontal axis"
return math.atan2(self.y(), self.x())
def distance(self, other):
"Distance to other"
return self.vectorTo(other).rho()
def vectorTo(self, other):
"Returns the Point representing the vector from self to other Point"
return Point(other.x() - self.x(), other.y() - self.y())
# Commands
def translate(self, dx, dy):
"Move by dx horizontally, dy vertically"
self._x += dx
self._y += dy
def scale(self, factor):
"Scale by factor"
self._x *= factor
self._y *= factor
def centre_rotate(self, angle):
"Rotate around origin (0, 0) by angle"
temp_x = self.rho() * math.cos(self.theta() + angle)
temp_y = self.rho() * math.sin(self.theta() + angle)
self._x, self._y = temp_x, temp_y
def rotate(self, p, angle):
"Rotate around p by angle"
self.translate(-p.x(), -p.y())
self.centre_rotate(angle)
self.translate(p.x(), p.y())
class Route:
def __init__(self):
self.Point = []
def __add__ (x,y,index):
self.points.insert(Point(x,y), index)
The Point works fine but I can't figure out how to get Route to work.
The error I get is:
>>>
>>> route = Route()
>>> route.add(32, 12, 2)
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
route.add(32, 12, 2)
File "C:\Users\John Wayne\Dropbox\kool\geom.py", line 73, in add
self.points.insert(Point(x, y), index)
TypeError: 'Point' object cannot be interpreted as an integer
>>>
OK I have managed to fix the Route class as following:
class Route:
def __init__(self):
self.points = []
def add_point(self, x, y, index):
self.points.insert(index, Point(x,y))
But now I have problem with my method get_lenght:
def get_lenght(self, Point):
for Point in self.points:
What is wrong with this get_lenght method?
Thank you very much.

It looks like you are trying to insert in to the list self.points using the insert method. Insert takes two arguments:
my_list = []
my_list.insert(index, object)
Which will insert the object before the index supplied. The argument 'index' must be a number.
In your example, you have passed the object first and the index second. Switch the arguments around and it should be working fine. e.g.:
self.points.insert(index, Point(x,y))
Alternatively, if you just want to add the object on to the end of the list, the 'append' method may work better for you:
self.points.append(Point(x,y))
In addition, I have assumed that your class 'Route' is written as follows as the code copied above appears to be incorrect (your method 'add' shouldn't be defined with the underscored before and after, you didn't pass 'self' as the first argument to your add method, and you had inconsistency with self.points):
class Route:
def __init__(self):
self.points = []
def add (self, x, y, index):
self.points.insert(Point(x,y), index)

Related

How do I set the default value Point(0,0) for center and radius = 1?

Please help me modify this class Circle, (1) setting a default instance.
The circle class uses a Point object as the center. Use center and radius keywords, with the center as the first parameter. Use repr and str . When assigning point object and radius value to a circle,
the output should look like this for assigned values
this is what I am getting
Defaults for a circle with no inputs are: center at a default instance of a
Point object, and radius of 1.
This is how the output should look like for default values
This is what I am getting for defaults
If I am to change the following
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.center.x,self.center.y,self.radius)
to this:
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius {2})"
.format(self.center[0],self.center[1],self.radius)
This will create an error, because point object for assigned center Point(2, 3) does not support indexing.
import math
class Point:
def __init__(self,x=0,y=0):
self.x = x
self.y = y
def __iter__(self):
yield self.x
yield self.y
def __add__(self,other):
return Point(self.x+other.x,self.y+other.y)
def __mul__(self,n):
return Point(self.x*n,self.y*n)
def __rmul__(self,n):
return Point(n*self.x,n*self.y)
#classmethod
def from_tuple (cls, self=(0,0)):
return cls(*self)
def loc_from_tuple(self,t=(0,0)):
self.x=t[0]
self.y=t[1]
def __str__(self):
return "Point at ({0}, {1})".format(self.x,self.y)
def __repr__(self):
return"Point(x={0}, y={1})".format(self.x,self.y)
#property
def magnitude(self):
return math.sqrt(self.x**2+self.y**2)
def distance (self, other):
return math.sqrt((self.x-other.x)**2+(self.y-other.y)**2)
def shift(p1, p2):
mx = p1.x+p2.x
my = p1.y+p2.y
return Point(mx,my)
class Circle(Point):
def __init__(self, center=(0,0), radius=1):
Point.__init__(self,center)
self.center=center
self.radius=radius
def __str__(self):
return "Circle with center at ({0}, {1}) and radius {2}"
.format(self.center.x, self.center.y, self.radius)
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.center.x,self.center.y,self.radius)
def __add__(self,other):
return Circle(
Point(self.center.x+other.center.x,
self.center.y+other.center.y),
self.radius+other.radius)
#classmethod
def from_tuple(cls, center,radius):
return cls(center, radius)
#property
def radius(self):
return self._radius
#radius.setter
def radius(self, radius):
if radius<0:
raise ValueError('The radius cannot be negative')
self._radius=radius
#property
def diameter(self):
return self._radius*2
#diameter.setter
def diameter(self, diameter):
if diameter<0:
raise ValueError('The radius cannot be negative')
self._radius = diameter/2
#property
def area(self):
return math.pi*self.radius**2
Center is a tuple, so it does not have an attribute x. Here you define center:
class Circle(Point):
def __init__(self, center=(0,0), radius=1):
Point.__init__(self,center)
self.center=center
And here you call center.x
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.center.x,self.center.y,self.radius)
A quick workaround would be to change __repr__ to something like:
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.center[0],self.center[1],self.radius)
That way you will be indexing the tuple. Another approach will be to make center a dictionary like center = {'x':0, 'y'0}. Then __repr__ will work as is although you will have to go through the rest of your program to ensure consistency.
The error message tells you everything you need to know:
AttributeError: 'tuple' object has no attribute 'x'
The offending line is this one:
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.center.x,self.center.y,self.radius)
The object center is a tuple, meaning its variables are accessed with indices, not attribute names. Try instead:
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.center[0],self.center[1],self.radius)
What would be even better is to actually make use of the inheritance Circle has from Point and access x and y directly - there is no reason to keep the center as an instance variable if you have all you need already there:
return "Circle(center=Point({0}, {1}), radius={2})"
.format(self.x,self.y,self.radius)

Add from_tuple() classmethod that allows objects to be created with a tuple?

Circle class uses a Point object as the center. A #classmethod called from_tuple is needed to allow Circle objects to be created with tuple instead of a Point as the center. This is how the output should look like for from_tuple()
I apologize for the messy codes, having two getters and one setter for center. I got really confused toward the end, knowing that center must be a tuple. Please help me with resolving the AttributeError in the getter when accessing from_tuple().
Error message is shown below
>>> center_point=3,4
>>> circle=Circle.from_tuple(center=center_point)
>>> circle
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\hole1\Desktop\ProgramIntro\shapes.py", line 54, in __repr__
return "Circle(center=Point({0}, {1}), radius={2})".format(self.center[0],self.center[1],self.radius)
File "C:\Users\hole1\Desktop\ProgramIntro\shapes.py", line 65, in center
return(self._center)
AttributeError: 'Circle' object has no attribute '_center'
The below is class Point:
import math
class Point:
def __init__(self,x=0,y=0):
self.x = x
self.y = y
self.data=[x,y]
def __getitem__(self, index):
return self.data[index]
def __iter__(self):
yield self.x
yield self.y
def __add__(self,other):
return Point(self.x+other.x,self.y+other.y)
def __mul__(self,n):
return Point(self.x*n,self.y*n)
def __rmul__(self,n):
return Point(n*self.x,n*self.y)
#classmethod
def from_tuple (cls, self=(0,0)):
return cls(*self)
def loc_from_tuple(self,t=(0,0)):
self.x=t[0]
self.y=t[1]
def __str__(self):
return "Point at ({0}, {1})".format(self.x,self.y)
def __repr__(self):
return"Point(x={0}, y={1})".format(self.x,self.y)
The below is class Circle.
class Circle(Point):
def __init__(self, center=(0,0), radius=1):
try:
x, y = center
super(Circle, self).__init__(x,y)
Point.__init__(self,center)
self.radius=radius
self.center=center
except (UnboundLocalError, TypeError, AttributeError) as e:
pass
if not center == (0,0):
if not isinstance(center,Point):
if not isinstance(center, tuple):
raise TypeError("The center must be a oint!")
def __str__(self):
return "Circle with center at ({0}, {1}) and radius {2}".format(self.center.x, self.center.y, self.radius)
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})".format(self.center[0],self.center[1],self.radius)
#property
def center(self):
return self.x, self.y
#property
def center(self):
return(self._center)
#center.setter
def center(self, center):
if not center == (0,0):
if not isinstance(center,Point):
raise TypeError("The center must be a Point!")
self._center=center
#property
def radius(self):
return self._radius
#radius.setter
def radius(self, radius):
if radius<0:
raise ValueError('The radius cannot be negative')
self._radius=radius
#classmethod
def from_tuple(cls, center=(0,0),radius=1):
return cls(center,radius)
There are a lot of things wrong with your code - from logical (i.e. why does a Circle inherit from a Point?) to structural (using undeclared instance properties) to downright weird (why are you defining your Circle.center getter twice?) - but the main culprit for the error in question is the very bad idea of capturing all exceptions just to ignore them because if you haven't done so you'd notice the error yourself.
Namely, when you call your Circle.from_tuple() class method, it in turn creates a new Circle instance and its __init__() method gets called with the passed passed center and radius (btw. why do you need Circle.from_tuple() when you're just forwarding your arguments - why not just call Circle() directly?). Given that you're passing a (3, 4) as center to it, it's as if you've effectively called: Circle.__init__(center=(3, 4)).
And there you get a slew of problems. First, you call the super.__init__() method to pass the center's x and y - fair enough, it will initiate self.x, self.y and self.data (redundant, but lets not get into it). Then for some unknown reason you attempt to call it again, using a different syntax and passing your tuple only as the first parameter, which in turn now turns self.x to have the value of center ((3, 4)). But the real problem lies when you call your Circle.center() setter passing it the same (3, 4) tuple. If you look at your method:
#center.setter
def center(self, center):
if not center == (0, 0):
if not isinstance(center, Point):
raise TypeError("The center must be a Point!")
self._center = center
What do you think is going to happen? Given that the passed center ((3, 4)) is not (0, 0), and most definitely not an instance of Point (it's a tuple) it will raise a TypeError and self._center will never be set.
But wait, since you've decided to capture all UnboundLocalError, TypeError and AttributeError exceptions in your Circle.__init__() and just pass them through you never get informed that your center wasn't set, so when you attempt to print it out in REPL Circle.__repr__() gets called, which in turn attempts to access the undefined Circle._center and you get your nice error.
A quick & dirty solution, since you're already inheriting from Point, is to just forget about the center altogether - you already have self.x and self.y (and self.data being a list based on those) so remove any references to center and you're golden:
class Circle(Point):
def __init__(self, center=(0, 0), radius=1):
super(Circle, self).__init__(*center)
self.radius = radius
def __str__(self):
return "Circle with center at ({0}, {1}) and radius {2}".format(self.x, self.y, self.radius)
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})".format(self.x, self.y, self.radius)
#property
def center(self):
return Point(self.x, self.y)
#property
def radius(self):
return self._radius
#radius.setter
def radius(self, radius):
if radius < 0:
raise ValueError('The radius cannot be negative')
self._radius = radius
And now you can instantiate it both with a Point or a tuple for its center.
But even better solution would be to rethink what are you trying to achieve here and, when you get stuck, to attempt good old Rubber duck debugging to capture errors such as the one in your code.

Python: Unexpected identifier when calling method of class

I would like to write a class Calculator including:
a function called add that takes two parameters containing double values and returns their sum
a function called subtract that takes two parameters containing double values and returns their difference (subtract the second from the first)
a function called multiply that takes two parameters containing double values and returns their product
a function called divide that takes two parameters containing double values and returns the value of the first divided by the second. If the second number is a zero, do not divide, and return "You can't divide by zero!"
This is my attempt, but apparently it's not correct.
class Calculator:
def add(x,y):
return x+ y
def subtract(x,y):
return x - y
def multiply(x,y):
return x * y
def divide(x,y):
if y==0:
return "You can t divide by zero!"
else:
return x/y
From the results, I get unexpected identifier x and y.
Object methods in python need to explicitly define the 'this' parameter you know from 'C', or the argument referring to the object itself. In Python it is usually called 'self'. For example:
class Calc:
def add(self,x,y): return x+y
Since all your methods do not really need self, and the calculator is more of a container of methods, you can define them as class methods, so Calc.add(3,4) works without creating an object:
class Calc:
#staticmethod
def add(x,y): return x+y
If you're new to python please note indentation is very important as well.
This answer will be accepted by the programming lab system:
class Calculator:
def add(self, x, y):
self.x = x
self.y = y
a = self.x + self.y
return a
def subtract(self, x, y):
self.x = x
self.y = y
a = self.x - self.y
return a
def multiply(self, x, y):
self.x = x
self.y = y
a = self.x * self.y
return a
def divide(self, x, y):
self.x = x
self.y = y
if (y == 0):
a = "You can't divide by zero!"
else:
a = self.x / self.y
return a
There are more simple soutions but this will be accepted by the programming lab editor. It can be a bit picky at times.
class Calculator:
def add(self,num1,num2):
print(num1+num2)
def subtract(self,num1,num2):
print(num1-num2)
def multiply(self,num1,num2):
print(num1*num2)
def divide(self,num1,num2):
print(num1 / num2)
object1 = Calculator()
object2 = Calculator()
object3 = Calculator()
object4 = Calculator()
object1.add(100,200)
object2.subtract(50,30)
object3.multiply(10,3)
object4.divide(250,5)

Setting the property of a property

I am trying to figure out a way of having the setter of a property of a property trigger some action on the top level class.
As a dummy example, lets say my top level class is a Segment. All is good if I store the coordinates of its endpoints directly as properties of this object x0, y0, x1 and y1, and have the setters of each one trigger the selected action.
But if I want to group them into two Point members as properties p0 and p1, each with properties x and y, whenever one of these coordinates is modified, there is no obvious way of telling the Segment to do something. This is what I would like to be able to do:
>>> segment = Segment(Point(0, 0), Point(3, 3))
>>> segment.p0
Point(0, 0)
>>> segment.p0.x
0
>>> segment.p1.y = 4
Length of segment changed to 5.0! # This can only be printed by segment, not p1!
The problem is that the line segment.p1.y = 4 first calls the getter of p1 on the segment instance, and then the setter of y on the return of the previous call, at which point there is no simple way of letting the segment instance know that a change has been made.
The best I can think of right now is something along the lines of the following:
class Point(object):
def __init__(self, x, y, parent=None, name=None):
self.parent, self.name = parent, name
self._x, self._y = x, y
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
if parent is not None:
setattr(self.parent, self.name, self)
# Similar code for y goes here...
class Segment(object):
def __init__(self, p0, p1):
self.p0, self.p1 = p0, p1
#property
def p0(self):
return self._p0
#p0.setter
def p0(self, point):
self._p0 = point
self.p0.parent = self
self.p0.name = 'p0'
if not self._silent:
self.do_something() # This would print the length in the above example
# Similar code for p1 goes here...
While this does what I want it to, I don't quite like having to manually add that link back to the parent, nor how I would either have to make lots of redundant copies of the Point objects, or risk interesting bugs if doing something like:
p0, p1, p2 = Point(0, 0), Point(1, 1), Point(2, 2)
seg0 = Segment(p0, p1)
seg1 = Segment(p0, p2)
# The following line changes the value on both seg0 and seg1, but triggers
# the do_something call on seg1 only!
seg0.p0.x = 6
Is there some ready-made recipe for this? Anyone can come up with a better way of doing it?
Perhaps you are looking for the Observer design pattern:
import math
class Point(object):
def __init__(self, x, y, name=None):
self.name = name
self._x, self._y = x, y
self.observers = []
def observe(self, observer):
self.observers.append(observer)
def __repr__(self):
return 'Point({}, {})'.format(self.x, self.y)
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
for o in self.observers:
o.notify()
#property
def y(self):
return self._y
#y.setter
def y(self, value):
self._y = value
for o in self.observers:
o.notify()
class Segment(object):
def __init__(self, p0, p1):
self._p0, self._p1 = p0, p1
p0.observe(self)
p1.observe(self)
def __repr__(self):
return 'Segment({}, {})'.format(self.p0, self.p1)
def notify(self):
print('Length of {} changed to {}'.format(self, self.length()))
def length(self):
return math.sqrt((self.p0.x - self.p1.x)**2
+ (self.p0.y - self.p1.y)**2)
#property
def p0(self):
return self._p0
#p0.setter
def p0(self, point):
self._p0 = point
#property
def p1(self):
return self._p1
#p1.setter
def p1(self, point):
self._p1 = point
segment = Segment(Point(0, 0), Point(3, 3))
print(segment.p0)
# Point(0, 0)
print(segment.p0.x)
# 0
segment.p1.y = 4
yields
Length of Segment(Point(0, 0), Point(3, 4)) changed to 5.0
and
p0, p1, p2 = Point(0, 0), Point(1, 1), Point(2, 2)
seg0 = Segment(p0, p1)
seg1 = Segment(p0, p2)
seg0.p0.x = 6
yields
Length of Segment(Point(6, 0), Point(1, 1)) changed to 5.09901951359
Length of Segment(Point(6, 0), Point(2, 2)) changed to 4.472135955

Oop for class circle

Im writing a class in python called Circle.
Now as part of the class I want to define methods so I did but when I run the program it crashes and says they are not defined. I cant find the problem.
class Circle():
""" Holds data on a circle in the plane """
def __init__(self,*args):
if isinstance(args[0],Point) and isinstance(args[1],(float,int)):
assert args[1]>0
self.center= args[0]
self.r= args[1]
else:
assert args[2]>0
self.a=args[0]
self.b=args[1]
self.center= Point(self.a,self.b)
self.r= args[2]
def __mul__(self,other):
assert isinstance(other,(float,int))
assert other>0
return Circle(self.center,self.r*other)
__rmul__= __mul__
def area(self):
return math.pi*self.r**2
def circumference(self):
return 2*self.r*math.pi
def move(self,p):
assert isinstance(p,Point)
self.center= p
return None
I wrote a class for Point aswell, so thats not the problem.
This is what happens when I run the porgram:
>>> a=Circle(-3,-3,1)
>>> area(a)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
area(a)
NameError: name 'area' is not defined
Edit: as #jsbueno points out, this was not the error causing your error message:
Your indentation is off (def __mul__ should be 1 space to the left), therefore Python thinks you have ended your class definition and are simply defining some more functions (not class methods).
Also, you should call area as a method - a.area(), not area(a).
I've done a bit of reworking - added some comments, renamed some variables, generally cleaned up - this now works properly:
from math import pi
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Circle:
"""
Holds data on a circle in the plane
"""
def __init__(self, a, b, c=None):
if c is None:
# Circle(Point, scalar)
self.center = a
self.r = b
else:
# Circle(scalar, scalar, scalar)
self.center = Point(a, b)
self.r = c
#property
def r(self):
return self._r
#r.setter
def r(self, new_r):
assert new_r > 0
self._r = new_r
def __mul__(self, scale_by):
return Circle(self.center, self.r * scale_by)
__rmul__ = __mul__
def area(self):
return pi * self.r**2
def circumference(self):
return 2 * pi * self.r
def move(self, new_center):
self.center = new_center
then
a = Circle(-3,-3,1)
print(a.area())
gives
3.141592653589793
which is correct.
The methods in a class are available to the instance, but they have to be called as components of the instance with the "." operator.
So, in your example, you should use
a = Circle()
a.area()
and not
area(a)

Categories