Point
class¶We will write a class for a point in a two dimensional Euclidian space ($\mathbb{R}^2$).
We start with the class definition (def
) and the constructor (__init__
) which defines the creation of a new class instance.
Note:
self
, a reference to the instance.__init__
have a default values 0.__init__
arguments are numbers.class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
p = Point(1,2)
print("point", p.x, p.y)
origin = Point()
print("origin", origin.x, origin.y)
point 1.0 2.0 origin 0.0 0.0
Notice that when we send a Point
to the console we get:
p
<__main__.Point at 0x8519fd0>
Which is not useful, so we will define how Point
is represented in the console using __repr__
.
class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
Point(1,2)
Point(1.0, 2.0)
Next up we define a method to add two points. Addition is by elements - $(x_1, y_1) + (x_2, y_2) = (x_1+x_2, y_1+y_2)$.
We also allow to add an int
, in which case we add the point to a another point with both coordinates equal to the argument value.
class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
def add(self, other):
assert isinstance(other, (int, Point))
if isinstance(other, Point):
return Point(self.x + other.x , self.y + other.y)
else: # other is int, taken as (int, int)
return Point(self.x + other , self.y + other)
Point(1,1).add(Point(2,2))
Point(3.0, 3.0)
Point(1,1).add(2)
Point(3.0, 3.0)
A nicer way to do it is to overload the addition operator + by defining the addition method name to a name Python reserves for addition - __add__
(those are double underscores):
class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
def __add__(self, other):
assert isinstance(other, (int, Point))
if isinstance(other, Point):
return Point(self.x + other.x , self.y + other.y)
else: # other is int, taken as (int, int)
return Point(self.x + other , self.y + other)
Point(1,1) + Point(2,2)
Point(3.0, 3.0)
Point(1,1) + 2
Point(3.0, 3.0)
We want to be a able to compare Point
s:
Point(1,2) == Point(2,1)
False
Point(1,2) == Point(1,2)
False
p = Point()
p == p
True
Point(1,2) > Point(2,1)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-11-5ba2a5d37eb0> in <module>() ----> 1 Point(1,2) < Point(2,1) TypeError: unorderable types: Point() < Point()
So ==
checks by identity and >
is not defined. Let us overload both these operators:
class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
def __add__(self, other):
assert isinstance(other, (int, Point))
if isinstance(other, Point):
return Point(self.x + other.x , self.y + other.y)
else: # other is int, taken as (int, int)
return Point(self.x + other , self.y + other)
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __gt__(self, other):
return (self.x > other.x and self.y > other.y)
First we check if two points are equal:
Point(1,0) == Point(1,2)
False
Point(1,0) == Point(1,0)
True
Then if one is strictly smaller than the other:
Point(1,0) > Point(1,2)
False
Point(5,6) > Point(1,2)
True
The addition operator + returns a new instance.
Next we will write a method that instead of returning a new instance, changes the current instance:
class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __gt__(self, other):
return (self.x > other.x and self.y > other.y)
def __add__(self, other):
assert isinstance(other, (int, Point))
if isinstance(other, Point):
return Point(self.x + other.x , self.y + other.y)
else: # other is int, taken as (int, int)
return Point(self.x + other , self.y + other)
def increment(self, other):
'''this method changes self (add "inplace")'''
assert isinstance(other,Point)
self.x += other.x
self.y += other.y
p = Point(6.5, 7)
p + Point(1,2)
print(p)
p.increment(Point(1,2))
print(p)
Point(6.5, 7.0) Point(7.5, 9.0)
We now write a method that given many points, checks if the current point is more extreme than the other points.
Note that the argument *points
means that more than one argument may be given.
class Point():
"""Holds on a point (x,y) in the plane"""
def __init__(self, x=0, y=0):
assert isinstance(x, (int, float)) and isinstance(y, (int, float))
self.x = float(x)
self.y = float(y)
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __lt__(self, other):
return (self.x < other.x and self.y < other.y)
def __add__(self, other):
assert isinstance(other, (int, Point))
if isinstance(other, Point):
return Point(self.x + other.x , self.y + other.y)
else: # other is int, taken as (int, int)
return Point(self.x + other , self.y + other)
def increment(self, other):
'''this method changes self (add "inplace")'''
assert isinstance(other,Point)
self.x += other.x
self.y += other.y
def is_extreme(self, *points):
for point in points:
if not self > point:
return False
return True
p = Point(5, 6)
p.is_extreme(Point(1,1))
True
p.is_extreme(Point(1,1), Point(2,5), Point(6,2))
False
We can also use the method via the class instead of the instance, and give the instance of interest (the one that we want to know if it is the extreme) as the first argument self
. Much like this, we can either do 'hi'.upper()
or str.upper('hi')
.
Point.is_extreme(Point(7,8), Point(1,1), Point(4,5), Point(2,3))
True
class Rectangle1():
"""
Holds a parallel-axes rectangle by storing two points
lower left vertex - llv
upper right vertex - urv
"""
def __init__(self, lower_left_vertex, upper_right_vertex):
assert isinstance(lower_left_vertex, Point)
assert isinstance(upper_right_vertex, Point)
assert lower_left_vertex < upper_right_vertex
self.llv = lower_left_vertex
self.urv = upper_right_vertex
def __repr__(self):
representation = "Rectangle with lower left {0} and upper right {1}"
return representation.format(self.llv, self.urv)
def dimensions(self):
height = self.urv.y - self.llv.y
width = self.urv.x - self.llv.x
return height, width
def area(self):
height, width = self.dimensions()
area = height * width
return area
def transpose(self):
"""
Reflection with regard to the line passing through lower left vertex with angle 315 (-45) degrees
"""
height, width = self.dimensions()
self.urv = self.llv
self.llv = Point(self.urv.x - height, self.urv.y - width)
rec = Rectangle1(Point(), Point(2,1))
print(rec)
print("Area:", rec.area())
print("Dimensions:", rec.dimensions())
rec.transpose()
print("Transposed:", rec)
Rectangle with lower left Point(0.0, 0.0) and upper right Point(2.0, 1.0) Area: 2.0 Dimensions: (1.0, 2.0) Transposed: Rectangle with lower left Point(-1.0, -2.0) and upper right Point(0.0, 0.0)
The second implementation defines a rectangle by the lower left point, the height and the width.
We define the exact same methods as in Rectangle1
, with the same input and output, but different inner representation / implementation.
class Rectangle2():
"""
Holds a parallel-axes rectangle by storing lower left point, height and width
"""
def __init__(self, point, height, width):
assert isinstance(point, Point)
assert isinstance(height, (int,float))
assert isinstance(width, (int,float))
assert height > 0
assert width > 0
self.point = point
self.height = float(height)
self.width = float(width)
def __repr__(self):
representation = "Rectangle with lower left {0} and upper right {1}"
return representation.format(self.point, Point(self.point.x + self.width, self.point.y + self.height))
def dimensions(self):
return self.height, self.width
def area(self):
area = self.height * self.width
return area
def transpose(self):
self.point = Point(self.point.x - self.height , self.point.y - self.width)
self.height, self.width = self.width, self.height
rec = Rectangle2(Point(), 1, 2)
print(rec)
print("Area:", rec.area())
print("Dimensions:", rec.dimensions())
rec.transpose()
print("Transposed:", rec)
Rectangle with lower left Point(0.0, 0.0) and upper right Point(2.0, 1.0) Area: 2.0 Dimensions: (1.0, 2.0) Transposed: Rectangle with lower left Point(-1.0, -2.0) and upper right Point(0.0, 0.0)
We will see two recusive functions that calculate the maximum value in a list.
In the first function the recursive step removes the first element in the list.
In the second function, the recursive step breaks the list into two lists:
Try to count the number of function calls each function requires and the amount of memory used.
View here or directly at Python Tutor
The number of function calls is $O(n)$ but the amount of memory used is $n + n - 1 + n - 2 + ... + 1 = \sum_{k=1}^{n}{k} = O(n^2)$.
View here or at Python Tutor
The number of function calls is again $O(n)$ but here the amount of memory used simultaneously is only $n + \frac{n}{2} + \frac{n}{4} + ... + 1 = n\sum_{k=0}^{n}{\frac{1}{2^n}} = 2n = O(n)$.
This notebook is part of the Extended introduction to computer science course at Tel-Aviv University.
The notebook was written using Python 3.2 and IPython 0.13.1.
The code is available at https://raw.github.com/yoavram/CS1001.py/master/recitation5.ipynb.
The notebook can be viewed online at http://nbviewer.ipython.org/urls/raw.github.com/yoavram/CS1001.py/master/recitation5.ipynb.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.