A class is a programmer-defined data type. Classes can contain variables, which are called attributes.
In the next cell, we will create a class called Rectangle. It will contain two attributes, corner_bl and corner_ur, which which contain the coordinates of the bottom-left and upper-right coordinates of a rectangle.
# Version 1
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
The cell above does not actually create an object of the type Rectangle. It just defines the structure for later objects that we might create with this type. An object that is created with a certain class type is said to be an instance of that class. We may have several instances of the same class.
In the cell below, we will create a single instance of the Rectangle class. We will then explore some aspects of this class.
rect1 = Rectangle()
print(type(rect1))
print(rect1.corner_bl)
print(rect1.corner_ur)
We can change the value of class attributes (although this is often not advisable to do so).
rect1.corner_ur = [3,8]
print(rect1.corner_bl)
print(rect1.corner_ur)
As mentioned previously, we can have multiple instances of a single class.
rect2 = Rectangle()
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
print('rect2 corners:', rect2.corner_bl, 'and', rect2.corner_ur)
When we set a variable to be equal to an existing instance of a class, it does not create a new version of that class. Instance, the new variable simply points to the already existing instance. Recall that lists exhibit a similar behavior.
rect3 = rect1
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
print('rect2 corners:', rect2.corner_bl, 'and', rect2.corner_ur)
print('rect3 corners:', rect3.corner_bl, 'and', rect3.corner_ur)
rect1.corner_bl = [2,4]
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
print('rect2 corners:', rect2.corner_bl, 'and', rect2.corner_ur)
print('rect3 corners:', rect3.corner_bl, 'and', rect3.corner_ur)
A method is a function that is associated with a certain class. A method is always called by using a particular instance of the class, and that instance is always provided to the method as an argument.
# Version 2
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
# Methods
def get_width(self):
return self.corner_ur[0] - self.corner_bl[0]
def get_height(self):
return self.corner_ur[1] - self.corner_bl[1]
rect1 = Rectangle()
w = rect1.get_width()
h = rect1.get_height()
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
print('rect1 width: ', w)
print('rect1 height: ', h)
rect1.corner_bl = [4,1]
rect1.corner_ur = [9,2]
w = rect1.get_width()
h = rect1.get_height()
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
print('rect1 width: ', w)
print('rect1 height: ', h)
A method in a classes has access not only to the attributes within that class, but also the other methods.
# Version 3
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
# Methods
def get_width(self):
return self.corner_ur[0] - self.corner_bl[0]
def get_height(self):
return self.corner_ur[1] - self.corner_bl[1]
def get_area(self):
w = self.get_width()
h = self.get_height()
return h*w
rect1 = Rectangle()
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
print('rect1 width: ', rect1.get_width())
print('rect1 height: ', rect1.get_height())
print('rect1 area: ', rect1.get_area())
A constuctor is a special method that is called automatically when an instance of a class is created. This can allow us to set the values of class attributes when the instance is created. The constuctor of a class in Python is always named __init__().
# Version 4
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
# Methods
def __init__(self, corner_bl, corner_ur):
self.corner_bl = corner_bl
self.corner_ur = corner_ur
def get_width(self):
return self.corner_ur[0] - self.corner_bl[0]
def get_height(self):
return self.corner_ur[1] - self.corner_bl[1]
def get_area(self):
w = self.get_width()
h = self.get_height()
return h*w
rect1 = Rectangle([1,2], [6,5])
rect2 = Rectangle([3,2], [4,9])
print('rect1 area:', rect1.get_area())
print('rect2 area:', rect2.get_area())
Class methods are able to alter the value of the attributes of that class.
# Version 5
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
# Methods
def __init__(self, corner_bl, corner_ur):
self.corner_bl = corner_bl
self.corner_ur = corner_ur
def get_width(self):
return self.corner_ur[0] - self.corner_bl[0]
def get_height(self):
return self.corner_ur[1] - self.corner_bl[1]
def get_area(self):
w = self.get_width()
h = self.get_height()
return h*w
def grow(self, dw, dh):
self.corner_ur[0] += dw
self.corner_ur[1] += dh
return self
rect1 = Rectangle([0,0], [3,4])
rect1.grow(6,1)
print('rect1 corners:', rect1.corner_bl, 'and', rect1.corner_ur)
A class method is able to take instances of that class as parameters.
# Version 6
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
# Methods
def __init__(self, corner_bl, corner_ur):
self.corner_bl = corner_bl
self.corner_ur = corner_ur
def get_width(self):
return self.corner_ur[0] - self.corner_bl[0]
def get_height(self):
return self.corner_ur[1] - self.corner_bl[1]
def get_area(self):
w = self.get_width()
h = self.get_height()
return h*w
def grow(self, dw, dh):
self.corner_ur[0] += dw
self.corner_ur[1] += dh
return self
def is_smaller(self, other):
if(self.get_area() < other.get_area()):
return True
return False
rect1 = Rectangle([0,0], [3,4])
rect2 = Rectangle([2,3], [4,6])
print(rect1.is_smaller(rect2))
rect1 = Rectangle([0,0], [3,4])
rect2 = Rectangle([2,3], [5,9])
print(rect1.is_smaller(rect2))
It is possible for a class method to create and return a new instance of the class itself.
# Version 6
class Rectangle:
# Attributes
corner_bl = [1,3]
corner_ur = [5,6]
# Methods
def __init__(self, corner_bl, corner_ur):
self.corner_bl = corner_bl
self.corner_ur = corner_ur
def get_width(self):
return self.corner_ur[0] - self.corner_bl[0]
def get_height(self):
return self.corner_ur[1] - self.corner_bl[1]
def get_area(self):
w = self.get_width()
h = self.get_height()
return h*w
def grow(self, dw, dh):
self.corner_ur[0] += dw
self.corner_ur[1] += dh
return self
def is_smaller(self, other):
if(self.get_area() < other.get_area()):
return True
return False
def intersect(self, other):
x1 = max(self.corner_bl[0], other.corner_bl[0])
y1 = max(self.corner_bl[1], other.corner_bl[1])
x2 = min(self.corner_ur[0], other.corner_ur[0])
y2 = min(self.corner_ur[1], other.corner_ur[1])
new_rect = Rectangle([x1,y1], [x2,y2])
return new_rect
rect1 = Rectangle([1,3], [6,8])
rect2 = Rectangle([4,2], [9,5])
rect3 = rect1.intersect(rect2)
print('rect3 corners:', rect3.corner_bl, 'and', rect3.corner_ur)