From next week onwards we will move on to actually implementing some data structures in Python, starting with the stack and the linked list. However, in order to do this effectively and cleanly, you need to be familiar with object-oriented programming. You will be introduced to this topic in more depth in COM411 later, but I will introduce this week just enough object-oriented programming for you to be able to code your own data structures.
(Note that Python comes with an extensive range of built-in data structures, for example you can treat the standard list
type as a stack and perform push
and pop
operations, but we are going to build them from scratch, in order to gain a deeper understanding of how the various data structures work.)
See the Python documentation for classes and objects.
(This is a simplified explanation. More depth will be provided in COM411.)
A class
can be thought of as a complex data type. Classes provide a way to define our own custom data structures. For example, we could create a Cat
class to represent a cat, a Stack
class to represent a stack, or a LinkedList
class to represent a linked list. Classes contain two key components:
Methods. A method represents an action that you can perform with a class. For example:
Cat
class we could have eat
, sleep
and meow
methods.push
and pop
methods. addNode
method, to add a node onto the end of the linked list (and update the reference to the last node).Attributes. An attribute represents an item of data associated with the class. Last week, we saw that a stack uses an array to store its data. So :
Cat
class could include the name, the age and the weight of the cat.Stack
class could be the underlying array. LinkedList
class. An object is a specific instance of a class, for example, a specific cat, stack or linked list. A class can be thought of as a blueprint, or specification, for how a particular data structure should work. However an object is a specific example of that data structure.
For example, each of the two cats in the photo below (Binnie and Clyde) could be represented in code with an object. One object for Binnie, and another for Clyde.
We could define a Cat class and then create many cat objects, reperesenting individual cats.
We could define a Stack class, and then create two Stack objects. One Stack object could be used in a web browser and contain your browsing history, whereas another could be used in a paint program and represent each drawing operation you do, allowing you to undo them.
Or, you could define a LinkedList
class, and then have one LinkedList
object to store students at a university, another to store courses, and yet another to store staff.
We will start with a simple class representing a cat.
class Cat:
def __init__(self, name, age, weight):
self.name = name
self.age = age
self.weight = weight
def eat(self):
self.weight += 1
This code does not create any actual cats. It just creates a class, or a blueprint or specification, for what cats are and what they do. Note, in particular, the following:
We define the class with the keyword class
followed by the name of the class (Cat
here). Note the colon and the indentation. The colon defines what is called a code block - here, the definition of the class. Note how everything within that block is indented (This can be with a tab, or a given number of spaces - typically 4). The block ends as soon as the indentation ends. You should be learning about this in COM411 this week.
Note how we list each method. We use the keyword def
to define a method. (You will do functions in COM411 next week; a method is essentially a function within a class).
Note how each method is itself a code block, so we use a colon and indent the code within the method.
Note how we define two methods. We have eat()
to define one of the most fundamental actions of a cat. However we also have a second method, __init__()
. What is this? It is a special initialisation method. It runs whenever an object of the class is first created, and can be used to initialise the object. Here, we are using it to initialise the three attributes of the cat, the name
, the age
and the weight
(see below for more detail).
Note how each method contains, as a parameter, the keyword self
. What is this? It is a reference to the current object that we're working with. Remember from the discussion on objects, above, that we might have multiple objects of a given class, for example, multiple cats. self
refers to whichever object we are dealing with right now.
Going back to the eat()
method, note that it contains the code:
self.weight += 1
What does this do? Remember that self
is the current object, in other words the current cat. The operator +=
increases a variable by one. So self.weight += 1
will increase the weight of the current cat by one.
To create actual cats, we need to create Cat
objects, as follows:
cat1 = Cat("Binnie", 4, 4)
cat2 = Cat("Clyde", 1, 2)
cat1.eat()
cat2.eat()
print(cat1.weight)
print(cat2.weight)
This code creates two specific cats, cat1
and cat2
. The lines:
cat1 = Cat("Binnie", 4, 4)
cat2 = Cat("Clyde", 1, 2)
actually create the two cats. In each case, the initialisation method __init__()
, which we saw above, is called, and the data about that cat is passed into the object.
Next, we actually make the cats do something by calling methods. Firstly, we call the eat()
method on each cat:
cat1.eat()
cat2.eat()
We then print the weight
of each cat, to show that eating has increased the weight by one:
print(cat1.weight)
print(cat2.weight)
class Cat:
def __init__(self, name, age, weight):
self.name = name
self.age = age
self.weight = weight
def eat(self, amount):
self.weight += amount
In many cases, we need to pass information in to a method to tell it what to do. For example, it would be useful if we could tell the eat()
how much food the cat needs to eat. We do this by specifying one or more parameters in the method, separated by commas. So:
def eat(self, amount)includes a parameter
amount
specifying how much food the cat should eat. We then use that in our statement to increase the weight, by increasing it by amount
:
self.weight += amountWhen calling the method, we then specify the parameter, e.g:
cat1 = Cat("Flathead", 3, 4) cat2 = Cat("Cupra", 2, 3) cat1.eat(3) cat2.eat(2) print(cat1.weight) print(cat2.weight)
Try out this example. Once it's working, do the following:
walk()
method inside the Cat
class. This should reduce the cat's weight by one.if
statement, change the walk()
method so that the cat cannot walk if the weight is below 1. (The intention is to avoid starving the cat). Try out this additional exercise using classes and objects.
Student
to represent a student.
Pass the following as parameters to the __init__()
method, and initialise the appropriate attributes.
id
, representing the student's IDname
, representing the student's namecourse
, representing the student's coursemark
, the student's markStudent
class a __str__()
method, which returns the student details as a string.Student
object each time the loop runs, using details the user entered from the keyboard. Then, still within the loop, display each student by printing it.
setMark()
method to your Student
class, to set the student's mark. The method must validate the mark, and check that it is between 0 and 100. The mark should only be updated if it is valid. Return a boolean to indicate whether the method was successful or not: it should return True
if the mark was valid and False
otherwise.getGrade()
method to Student
. This should return the student's grade as a string based on the mark, according to this scheme :
didPass()
method to Student
. This should return a boolean, depending on whether the mark is above or below 40.Student
__init__()
method so that the mark is no longer passed in as a parameter, and instead, set to 0. The idea is that setMark()
will be used instead to set the mark.setMark()
method. When all the details have been entered, print out the student's status (grade, and whether they passed or not) by calling the getGrade()
, and the didPass()
methods, and printing the return value of each.