Week 12: Writing Classes

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

Get Week 12 In-class Code

To copy over the week 12 in-class example programs, do the following (If you have trouble with either of these steps, ask a Ninja or your professor for help):

  1. Create a w12-classes subdirectory in your cs21/inclass directory, and cd into it:

    $ cd ~/cs21/inclass
    $ mkdir w12-classes
    $ cd w12-classes
    $ pwd
    /home/yourusername/cs21/inclass/w12-classes
  2. Copy over the week 12 files into your w12-classes subdirectory (check that they copied successfully copied by running ls:

    $ cp ~admin21/public/w12-classes/*.py ./
    $ ls
    employee.py employee_test.py playlist.py song.py

Week 12 Files

  • employee.py - defines an Employee class

  • employee_test.py - code that imports and uses Employee class

  • song.py - defines and uses Song class

  • playlist.py - defines Playlist class that has a list of Songs

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

  • StudentRecord

    • data - name, age, major, gpa

    • methods - get_name, etc.

  • Country

    • data - name, co2 levels, population

    • methods - getCO2, getPopulation

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 (id), 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":

  • getName provides access to the name

  • getID provides access to the id

  • getSalary provides access to the salary

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:

  • setName changes the name

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:

  • increaseSalary increases the salary 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 the print() 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, and salary.
"""

class Employee(object):

    def __init__(self, name, id, 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
         * id (int): the employee's ID number
         * salary (float): the employee's annual salary
        Returns: None. The name, id, 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.id = id
        self.salary = salary

    def getName(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 setName(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 getID(self):
        """
        Accessor method for employee's ID.
        """
        return self.id

    def getSalary(self):
        """
        Accessor method for employee's salary.
        """
        return self.salary

    def increaseSalary(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.id, 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.getName())
    print(e1.getID())
    print(e1.getSalary())

    # when we pass the object to print(), it calls the __str__() method
    print(e1)

    # now we can call the mutator methods
    e1.setName("Grace Brewster Hopper")
    e1.increaseSalary(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.getName())
    print(e2.getID())
    print(e2.getSalary())

    # when we change it, it doesn't change e2
    print(e1.getSalary())
    e2.increaseSalary(5000)
    print(e1.getSalary()) # e1's salary has not changed

main()