Weeks 12: Classes and Objects
Week 12 Goals
-
Understand connection of instances (objects) + classes
-
Understand key attributes of class definitions
-
member variables
-
constructor
-
accessors/getters
-
mutators/setters
-
-
Define classes with basic types (int, float, …)
-
Be able to write Python classes from scratch given an English specification
Week 12 Files
-
employee.py
- defines an Employee class -
employee_test.py
- code that imports and uses Employee class -
student.py
- defines a Student class
Object-Oriented Programming Revisited
Objects combine data and functionality (methods) together. We’ve seen lots of examples of objects in the class so far:
-
list
-
data - length, and all the data inside
-
methods - append, sort
-
-
str
-
data - each character in the string, the number of chars in the string
-
methods - isdigit, lower, split
-
-
Point
-
data - x, y
-
methods - getX, getY, draw
-
-
Circle
-
data - center, radius
-
methods - getCenter, getRadius, setFill, setOutline, setWidth, draw
-
-
Novel
-
data - title, author, year, gender, rank, rating, number of ratings
-
methods - get_title, get_author, etc.
-
Keep in mind the following terminology:
-
class: a template for how to build objects of a new type
-
object: a particular instance of a class that has its own data values in a running program
-
dot notation: how you call the methods of a class:
<instance>.<method>(<arg1>, …)
Designing a Class
In these notes, we will define our own class to represent the employee of an organization.
In designing this class, we should consider its data (or "instance variables" or "fields") and its functionality (or "methods").
For example, our employee may include the following data:
-
name
, represented as a string -
identification number (
idno
), represented as an int -
annual
salary
, represented as a float
We can use methods to allow other classes to read this data. These methods are called "accessors" or "getters" because they provide a means of accessing the data and typically start with the word "get":
-
get_name
provides access to thename
-
get_ID
provides access to theidno
-
get_salary
provides access to thesalary
We may also want to allow other classes to modify this data. These methods are called "mutators" or "setters" because they provide a means of mutating/changing the data and typically start with the word "set". In our case, we’ll say that only the name can be directly changed:
-
set_name
changes thename
Another way of modifying data is to provide a means of changing it but not setting it directly. This is still considered a "mutator" because it changes the value of the field. We’ll provide a method for increasing the salary:
-
increase_salary
increases thesalary
by a given amount
Last, there are two other important methods that are used by Python:
-
__init__
is the constructor, which is the method that is called when an object is created. It is used to initialize the fields. -
__str__
is a method that returns a string representation of this object. It is called when we use theprint()
function to print the object.
Defining a Class
Now that we’ve designed the class, we can write the code for it.
Our class will be called Employee
; it is the convention that Python class names start with uppercase letters.
The file that contains the definition of the class will be employee.py
. Although it is not required that it has the same name as the class, it makes it easy to understand, and the convention is that the file name starts with a lowercase letter.
Key to understanding the code below is the variable called self
.
This is a variable that refers to the object itself, and allows us to access its fields using dot notation, e.g. self.salary
.
You’ll notice that self
is the first parameter listed for all of these functions. Don’t forget to list it first and to use it within the function!
"""
Class to represent an employee at an organization.
This class includes the employee's name, ID number, and salary.
"""
class Employee:
def __init__(self, name, idno, salary):
"""
This is the constructor, which is the function that is called
when an instance of the class (object) is created.
Parameters:
* self: refers to this object, i.e. the object being created
* name (str): the name of the employee
* idno (int): the employee's ID number
* salary (float): the employee's annual salary
Returns: None. The name, idno, and salary are modified as a result.
"""
# note that we need to refer to the name instance of
# "this object" using the parameter "self"
self.name = name
self.idno = idno
self.salary = salary
def get_name(self):
"""
Accessor ("getter") method to access the employee's name.
Parameter:
* self: refers to this object; it is implicitly passed as
an argument when this method is called
Returns: the value of the name field (str)
"""
return self.name # note that it is not simply "return name"
def set_name(self, new_name):
"""
Mutator ("setter") method to change the employee's name.
Parameters:
* self: refers to this object
* new_name (str): the new name for this employee
Returns: None. The name field is modified as a result.
"""
self.name = new_name # note that it is not "name = new_name"
def get_ID(self):
"""
Accessor method for employee's ID number.
"""
return self.idno
def get_salary(self):
"""
Accessor method for employee's salary.
"""
return self.salary
def increase_salary(self, amount):
"""
Method that increases the employee's salary by the given amount.
"""
self.salary = self.salary + amount
def __str__(self):
"""
Method that creates and returns a string representation of this object.
This allows us to print the object using the print() function.
"""
return "Name: %s; ID: %d; Salary: %.2f" % \
(self.name, self.idno, self.salary)
Creating and Using Instances (Objects) of a Class
Now that we’ve defined the Employee
class, we can create instances of it in a program and then call methods on those objects.
The code below should look similar to past examples and labs that used objects, but now we are using our own custom Employee
class!
"""
Code that allows us to test the functionality of the Employee class.
"""
# this is so that we can access the Employee class in this file
from employee import *
def main():
# this creates an Employee object using the constructor
e1 = Employee("Grace Hopper", 8675309, 135000)
# these call the accessor/getter methods
print(e1.get_name())
print(e1.get_ID())
print(e1.get_salary())
# when we pass the object to print(), it calls the __str__() method
print(e1)
# now we can call the mutator methods
e1.set_name("Grace Brewster Hopper")
e1.increase_salary(10000)
# now we should see the updated values
print(e1)
# create a separate instance/object of this class
e2 = Employee("Ada Lovelace", 1234567, 125000)
# its fields have different values from those in e1
print(e2.get_name())
print(e2.get_ID())
print(e2.get_salary())
# when we change it, it doesn't change e2
print(e1.get_salary())
e2.increase_salary(5000)
print(e1.get_salary()) # e1's salary has not changed
main()
Practice creating a Student class
In student.py
, define a Student
class
-
Each
Student
has a name, class year, and major. -
The constructor should take the name and class year as parameters; the major should be set to "Undecided".
-
Write getters for the name, year and major.
-
Write a setter for the year. If the year is not between 2025-2028, don’t change the year and print a message.
-
Write a setter for the major. If the major is not in a small list of majors (e.g. "Comp Sci", "Economics", "Philosophy", "Undecided"), don’t change the major and print a message. You can choose the set of majors you want to accept.
-
Write some tests of the class in
main
. -
Call main only
if __name__=='__main__'