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)