CISC-121
20130301

Classes and Objects continued

We completed our brief introduction to Classes and Objects in Python.

Importing Class Definitions

One of the big advantages of Object Oriented Programming is that it lets us define all aspects of our new data type in one place - then we can simply copy the definitions into any program where we want to create and manipulate objects of that type.  The problem with that is if we ever want to change the definition of a class (say, add another attribute or change the working of a function) then for consistency we should edit every program where the definition is included.  Python gives us a much cleaner way to do this: use the import command that we are already familiar with.

Suppose we have created a Date class (see the accompanying Python file Date.py).  Then if we want to be able to use Date objects in another program, we can put "from Date import *" at the top of the program (see the accompanying Python file Book.py).  Note that Date.py should be in the same directory as the program doing the importing, or in one of the directories that Python automatically searches when importing - in this course we will just put our class definitions in the same directory as our programs.  

This means that if we need to change or add to the definition of the Date class, we only have to do it in one place.

The __init__ function

The __init__ function is used to establish the attributes of each new object created in the class, typically by means of parameters with default values.  The Date class contains a typical __init__  :

def __init__(self,
             year = 0,
             month = 0,
             day = 0
            ):
        self.year = year        # these three lines create the attributes of the new Date object being created
        self.month = month
        self.day = day

However, it sometimes useful for the __init__ function to do other things, and it can do anything that any other function can do.  For example, we might want it to report what it is doing:

def __init__(self,
             year = 0,
             month = 0,
             day = 0
            ):
        self.year = year
        self.month = month
        self.day = day
        print "Creating Date object with year =",self.year,", month =",self.month,", day =",self.day


Here is another example of an __init__ that might be used for a Rectangle object:

class Rectangle(object):

    def __init__(self, height = 0, width = 0):
        self.height = height
        self.width = width
        self.area = height*width

Note that this gives each Rectangle object an attribute that is not supplied, but is calculated using the parameter values given when the Rectangle instance is created.

box_1 = Rectangle(7,6)

creates a Rectangle object with three attributes: height, width, and area.


Object Instance Methods

We can give our new objects any number of operations that they can carry out on themselves.  The Python file Book.py contains several examples of things a Book object can do (adding a borrower, printing its borrowers, etc.).   Note that each instance method has self as its first parameter, and we don't have to provide anything as an argument to match that parameter.  This is just the way Python does it - when you are writing instance methods, remember to put self as the first parameter, and then ignore it.   Note also that we have to put self. in front of every attribute.  This actually makes sense because in many applications objects are allowed to modify the attributes of other objects - so each attribute needs a prefix to specify exactly which object it belongs to.


Class Attributes

Sometimes it is useful to store information about an entire class of objects, as opposed to about specific instances.  We can do this using class attributes.  A class attribute looks like a variable initialization in the class definition (usually at the top of it).  For example, in our Book class (see the accompanying Python file) we might want to keep track of how many Book objects have been created.  We can do this with a class attribute called num_books (or any other name we like, such as counter or size_of_library or whatever).  Then when we can add a line to __init__ so that every time we create a Book object, the num_books class attribute gets updated.

In the accompanying Python file Book.py, you will see two class attributes.  The __init__ function updates both of them each time a Book object is created:

from Date import *

class Book(object):
   
    # num_books and list_of_books are class attributes.  
    num_books = 0
   
    list_of_books = []
   
    def __init__(self,
            title = "",
            author = "",
            date = Date(),
            language = "",
            value = 0.0,
            borrowers = []
            ):
        self.title = title
        self.author = author
        self.date = date
        self.language = language
        self.value = value
        self.borrowers = borrowers
        Book.num_books += 1
        Book.list_of_books.append(self)



As well as class attributes, we can define class methods.  These allow us to work with class attributes without having to refer to specific objects within the class.  For example, in the accompanying Python file you will find

    @classmethod       
    def how_many(Book):
        print "There are",Book.num_books,"books in the library"
   
    @classmethod
    def print_all_titles(Book):
        for b in Book.list_of_books:
            b.print_title()

These are very simple examples - a class method can include any operation you want.  Note that the class attributes are prefixed with the class name, (e.g. Book.num_books)


As an exercise, think of a type of object of which you can think of several instances (phones, family members, shoes, board games) and create a class definition for that type of object.  Write one or two instance methods and class methods to become familiar with these concepts.  If you can't think of anything, try the old stand-by: create a class definition for CISC_121_Student.  But really, wouldn't Dr_Who_Episode be more interesting?