SmithLogo

CSC 111

Introduction to Computer Science Through Programming

Smith Computer Science



Lecture Notes 23: Intro to Classes and Objects





What is an Object? (in Python)

Some entities with which we interact are more than just a value.

If you think about the objects you interact with in your everyday life, there are







There are even complex ones that are not entirely physical, like a "College", which is composed of students, teachers, courses, etc.



You can think of complex entities as those that could be a "collection of values with some structure", or a "collection of values and methods".

Here, "methods" refers to the capacity to "do things" (for example: teach students, train other teachers, do research, etc).



In Python, we say that an object is an instance (one example entity) of one of these complex entities that can have (one or many) values and can "do things".




Abstraction with Encapsulation

When you want to use a microwave to heat a dish, you only need to know:

  1. how to open and close the door
  2. how to place the dish
  3. how much time to indicate
  4. how to enable the cycle
  5. (nobody really changes the power level, do they?)


What you don't do

What you don't do is study how the rotation of the dish causes the heating interferrence pattern inside the microwave to affec all areas of your dish.

Nor do you need to understand the programming behind the button interface.



Why?
(Wait; then Click)

Because you don't care.. you just want your damn noodles!





This idea... of hiding away what we don't need to know about and presenting only the information that is relevant in a given context... is called Abstraction!



The creation of objects that can have: is one of the main putposes of designing and creating objects.




Abstract Data Types

Abstract Data Types, or (ADTs) are examples of complex objects whose behavior and state is controlled by a carefully designed set of operations and an interface into these operations.

Built-in Objects

Python let's you declare and use objects. In fact, we've been using them all along!

Example: Strings!

Strings hold values and allow you to use their set of member methods by using the dot operator:

my_str = "Hi there, I am Yak, master of the universe"

my_str = my_str.replace("master", "disaster")
print(my_str)


Same deal with Lists, Dictionaries, Files, and others.




Making your own Objects: Classes

If the specific data structure or behavior you need is not provided by Python, you can design and create it yourself!





The class syntax (encapsulation)



1  
2  
3  
4  
5  
6  
7  
8  
class Student:
    name = "Andy"
    grades = [70, 80, 90, 100]

    def fun():
        print(Student.name + "'s grades are: ", Student.grades)
        
Student.fun()


Activity 0 [2 minutes]:
Run it in the Python Tutor and explain what is going on.
(We will explain the syntax soon... for now, simply try to guess what is going on)


This is not much use other than for "Packaging different things together"



Since we might want to use more than a single instance of this user-defined object, we are going to design a template for stamping out these new type of objects.

The way to do this is to define the template by using the class keyword.

A class is like a stencil, with which you can stamp out instances that are like copies of the stencil.

\(\rightarrow\)




The following is an extremely simple class that allows you to group information about a student's grades in a user-defined object we'll call Student





The class syntax (template)

The following is the template version of the Student class that allows you to group information about different student names and grades object stamped out of the new Student template class:

 1  
 2  
 3  
 4  
 5  
 6  
 7  
 8  
 9  
10  
class Student:
  def __init__(self, par1, par2):
    self.name = par1
    self.grades = par2


s1 = Student("Amy", [83, 88, 85, 94, 100])
print(s1)
print(s1.name)
print(s1.grades)


Activity 1 [2 minutes]:
Run it in the Python Tutor and explain what is going on.
(We will explain the syntax soon... for now, simply try to guess what is going on)
(Wait; then Click)

  • The class is "memorized" by Pyhton, but not used until we actualy instanctiate (stamp out) an object
  • In line 7, we are using the class name to create an object (s1) of the type defined by our class (Student)
  • The __init__ method takes care of initializing attributes for the class, which include:
    • the students name, and
    • the students list of grades
  • Notice that we need to pass in, as parameters to the class's __init__ method, the values that we will copy into the class's attributes.
  • in line 8.. we are printing the "reference" to the object s1
  • in lines 9 and 10, we are printing the values we obtain by accessing the class's attributes using the dot operator.


The __init__ method returns the address of where the new object was created in memory (so it can be stored in an object reference, like s1 )







The __init__ method and the self

The __init__ method is a specially named method inside the class that is activated when first creating an object.

We'll talk more about this next class, but you can think of this as a class method that constructs the specific instance object from input parameters... sort of like a pizza being made to your exact specifications (never with pineapple).
I don't actually care


The parameter self, is a variable inside the object that automatically references itself. It allows you to say things like:
"make my name, the value of the first parameter that was passed in"
or
self.name = name




One Class, Many Objects!

The power of this approach is that, while the Class "describes" the structure of each object,
it is each individual object that can be used and modified to hold different combinations of values (different state).

Try extending our example like this:

 1  
 2  
 3  
 4  
 5  
 6  
 7  
 8  
 9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
class Student:
  def __init__(self, par_name, par_grades):
    self.name = par_name
    self.grades = par_grades


s1 = Student("Amy", [83, 88, 85, 94, 100])

s2 = Student("Beth", [73, 78, 75, 84, 90])

s3 = Student("Cathy", [63, 68, 65, 74, 80])

avg1 = sum(s1.grades)/len(s1.grades)

avg2 = sum(s2.grades)/len(s2.grades)

avg3 = sum(s3.grades)/len(s3.grades)

print ("{} got a {}\n".format(s1.name, avg1))

print ("{} got a {}\n".format(s2.name, avg2))

print ("{} got a {}\n".format(s3.name, avg3))


Activity 2 [2 minutes]:
Before running it, predict what is going on!
Now run it in the Python Tutor
(Wait; then Click)

  • In lines 7, 9, and 11, we instanctiate 3 different objects (from the same class!)
  • In lines 13, 15, and 17, we use each setof internal values to calculate some useful numbers for each object
  • We print some results




Instance Methods

In addition to the value attributes for each instance object, we can have each object be capable of perfoming actions inside its own functions, which we call methods.

For example, it is kind of annoying to say that we are encapsulating the values for each student, but have to calculate the grade averages "outside each object".

We can improve this by having the class also include a get_average method that does this for us.

Defining methods

Simply write the method inside the class and use the self keyword to refer to "the instance whose method is being used right now".

Example


 1  
 2  
 3  
 4  
 5  
 6  
 7  
 8  
 9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
class Student:
  def __init__(self, par_name, par_grades):
    self.name = par_name
    self.grades = par_grades
    self.average = -1

  def get_average(self):
    self.average = sum (self.grades)/len(self.grades)
    return self.average


s1 = Student("Amy", [83, 88, 85, 94, 100])

s2 = Student("Beth", [73, 78, 75, 84, 90])

s3 = Student("Cathy", [63, 68, 65, 74, 80])

print ("{} got a {}\n".format(s1.name, s1.get_average()))

print ("{} got a {}\n".format(s2.name, s2.get_average()))

print ("{} got a {}\n".format(s3.name, s3.get_average()))


Note that the parameter for the method is self but one does not need to provide an argument for that parameter when invoking the method, since it is known for which instance it was called.

For example:


You can verify this in the Python Tutor




Special methods

Special methods (those that should be "reserved" to do a special task) can be identified by the surrounding double underscores, like for __init__.




Class vs Object attributes

Continuing with our stencil analogy, you could:

Use the Stencil itself, to perform an action (make a sign) knife
\(\rightarrow\)
stamp out something using the stencil and use that instead (make a sign) nums




Another example:

Use the Stencil itself, to perform an action (draw or measure) knife
\(\rightarrow\)
stamp out something using the stencil and use that instead (use the stamped ruler to measure) nums




Using the Class itself: Attributes

A class can have its own attributes!, which are common to all instances for that class.

The following is an example:

 1  
 2  
 3  
 4  
 5  
 6  
 7  
 8  
 9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32
33
34
35  
36  
class Student:
  course_name = "CSC111"
  num_students = 0

  def __init__(self, par_name, par_grades):
    self.name = par_name
    self.grades = par_grades
    self.average = -1
    Student.num_students += 1
    # this is also valid: 
    # self.num_students += 1

  def get_average(self):
    self.average = sum (self.grades)/len(self.grades)
    return self.average


s1 = Student("Amy", [83, 88, 85, 94, 100])

s2 = Student("Beth", [73, 78, 75, 84, 90])

s3 = Student("Cathy", [63, 68, 65, 74, 80])

print ("{} got a {}\n".format(s1.name, s1.get_average()))

print ("{} got a {}\n".format(s2.name, s2.get_average()))

print ("{} got a {}\n".format(s3.name, s3.get_average()))

print (f"The course name is {Student.course_name} and it has {Student.num_students} students\n")

list_all = [s1.get_average(), s2.get_average(), s3.get_average()]

avg_all = sum(list_all)/len(list_all)

print ("The average for course {} is {}\n".format(Student.course_name, avg_all))


Try it out in the Python Tutor



We will talk about possibly building methods for the class in the future.

These would be able to modify ONLY class-owned attributes.




Object-Oriented Programming

So what does all of this (classes and objects) allow us to do?

It lets us program in a fundamentally different way.

The following image illustrates the difference:

nums



Procedural Programming

This is what we've been doing so far:

Object-Oriented Programming (OOP)

This is what we'll do from now on:

Example programs look like this:

 1  
 2  
 3  
 4  
 5  
 6  
 7  
 8  
 9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
# parent class
class Bird:
    
    def __init__(self):
        print("Bird is ready")

    def whoisThis(self):
        print("Bird")


# child class
class Penguin(Bird):

    def __init__(self):
        # call super() function
        super().__init__()
        print("Penguin is ready")

    def whoisThis(self):
        print("Penguin")

    def run(self):
        print("Run faster")

    def fly(self):
        print("Penguin can't fly")   

    def swim(self):
        print("Penguin can swim")         

# child class
class Parrot(Bird):

    def __init__(self):
        # call super() function
        super().__init__()
        print("Parrot is ready")

    def whoisThis(self):
        print("Parrot")

    def run(self):
        print("Run faster")

    def fly(self):
        print("Parrot can fly")
    
    def swim(self):
        print("Parrot can't swim")


# common interface
def flying_test(bird):
    bird.fly()

def swimming_test(bird):
    bird.swim()    

#instantiate objects
blu = Parrot()
peggy = Penguin()

# passing the object
flying_test(blu)
flying_test(peggy)

swimming_test(blu)
swimming_test(peggy)


We will study this next class, in the meantime, Check this out




Additional readings

Check out this article on Object-Oriented Programming

Also this one with a Course on using classes




Homework

[Due for everyone]
Homework 07 is Due Friday 04/01 by 5PM (With the usual extension until Monday 04/04 by 5PM)


[Optional]