Module #09

In this module you will learn about the basics of Object Oriented Programming, a design paradigm that lets you 'tightly couple' data elements together in order to better organize your code and solve more complicated programming problems.


Introduction to Object Oriented Programming


Classes, Properties & Instances

The first thing we need to do when writing a new program that takes advantage of this "object oriented" approach is to design the organizational structure for our entities. We call this a "class". Classes are "blueprints" that we use for setting up objects that follow the same organizational pattern. Just like a blueprint, a class is nothing more than a design. For example, let's design a "class" for the Student entity that we discussed in the previous video.

class Student:

	# description of our ‘class' will go here!

Let's tackle the first item - what does an entity need to know about itself? In the case of a student we need to keep track of a first name, last name and a major. We could do something like this if we wanted to:

class Student:
	first_name = "pikachu"
	last_name = "pokemon"

Now we've tightly coupled together two strings (first_name and last_name) into our Student class. We set these up using some silly data for now, but we will change this in a moment. We call this "encapsulation".

One very important thing to keep in mind is that classes are just "blueprints" - they aren't actually anything in our program until we ask Python to create what we call and "instance" of the class. We do this by calling a function by the same name of the class, like this:

student_a = Student()

Now we've created an ‘instance' of the class. "instances" are often referred to as "objects", hence the term Object Oriented Programming. Let's print out this instance/object to see what happens:

print (student_a)

Hmmm, that's weird. I don't see our two variables anywhere in there! So what is showing up here? This is actually Python's way of saying "this is an object! if you were to look in the computer's memory at this address you would find it here." In order to access a specific variable within an instance we need to use "dot syntax", like this:

print (student_a.first_name)
print (student_a.last_name)

This says "access the student_a instance and then extract its first_name variable". Because these variables are attached to an instance we call them "instance variables". We also sometimes refer to them as the "properties" of an object. By default instance variables can be changed (they are mutable) - you can do this with a simple assignment statement:

student_a = Student()
print (student_a.first_name)
print (student_a.last_name)

student_a.first_name = ‘Charmander'

print (student_a.first_name)
print (student_a.last_name)

One of the major advantages of designing an your program to be object oriented is the ability to create as many objects as you'd like! For example, the following program creates 3 students:

student_a = Student()
student_b = Student()
student_c = Student()

All 3 students start off with two instance variables:

print (student_a.first_name, student_a.last_name)
print (student_b.first_name, student_b.last_name)
print (student_c.first_name, student_c.last_name)

However, each of our student objects has its own "copy" of each instance variable, meaning that you can change one instance and not affect the others:

student_a = Student()
student_b = Student()
student_c = Student()

student_b.first_name = "Charmander"
student_c.first_name = "Squirtle"

print (student_a.first_name, student_a.last_name)
print (student_b.first_name, student_b.last_name)
print (student_c.first_name, student_c.last_name)

Constructors

In the previous example we set up a new class that contained two instance variables. These instance variables were always set to the same value every time we created a new instance of the class:

class Student:
	first_name = "pikachu"
	last_name = "pokemon"

If we want to set up our instances so that they have their own custom values stored in their instance variables we needed to manipulate them after the object was instantiated, like this:

student_a = Student()
student_a.first_name = "John"
student_a.last_name = "Smith"

This can be cumbersome, so one thing we often do when designing a class is to set up a "constructor" function to handle the initial setup of an object. Constructor functions are designed to run one time when an object is created. To set up a constructor function you need to refer to it using a special name - '__init__' (two underscores before and after the word 'init') - this function accepts a single argument which is a reference to the instance that is being created. Here's an example:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self):

		print ("A new student was instantiated!")

Note the strange 'self' variable that is being sent as an argument to the function. This variable is actually a link to the object that is being constructed:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self):

		print ("A new student was instantiated!")
		print (self)

a = Student()
print ("After student has been instantiated")
print (a)

Note that printing out the object itself (after instantiation) prints out the memory address of the object. Printing out the 'self' variable (inside the constructor) prints out the same thing!

Remember that we can assign properties to an object after it's been instantiated:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self):

		# startup code goes here

a = Student()
a.name = "Pikachu"
print (a.name)

We can do the same thing inside of the 'constructor' function as well:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self):

		# startup code goes here
		self.name = "Pikachu"

a = Student()
print (a.name)

… and we can also send our own variables into the constructor function to have it store our own custom values for us!

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self, first, last):

		# use our arguments as our instance variables
		self.first_name = first
		self.last_name = last

a = Student("John", "Smith")
print (a.first_name, a.last_name)

Methods

So far we have created classes that describe objects in terms of their properties (variables). In addition to attaching values to an object we can also attach functions to our objects as well. For example, let's go back to our Student class:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self, first, last):

		# use our arguments as our instance variables
		self.first_name = first
		self.last_name = last

Let's say we want to add in way for the student to print out its first and last name. We could add a function into our class description like this:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self, first, last):

		# use our arguments as our instance variables
		self.first_name = first
		self.last_name = last

	# a function that will cause the object to print out its name
	def say_hi(self):
		
		print (self.first_name, self.last_name)

Note how the function is designed to accept the 'self' argument, just like the constructor function does. We call functions defined in this way as 'methods' of the object. Now we can instantiate a new object and ask it to print out its variables like this:

a = Student('Christine', 'Jones')
a.say_hi()

Note that we do not need to pass an argument to this function, even though it is set up to require one! This is because all methods will automatically be sent a reference to their instance.

Let's make this program a little more complicated. Let's add in the ability for our program to keep track of grades for a student. First, our student needs to store an instance variable to hold all of our grades:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self, first, last):

		# use our arguments as our instance variables
		self.first_name = first
		self.last_name = last

		# empty list to store our grades
		self.grades = []

	# a function that will cause the object to print out its name
	def say_hi(self):
		
		print (self.first_name, self.last_name)
Now we need a method to add a new grade:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self, first, last):

		# use our arguments as our instance variables
		self.first_name = first
		self.last_name = last

		# empty list to store our grades
		self.grades = []

	# a function that will cause the object to print out its name
	def say_hi(self):
		
		print (self.first_name, self.last_name)

	# a function to add new grades
	def add_grade(self, score):
		
		self.grades.append( score )

And a function to get our average:

class Student:

	# constructor function - this function runs one time when
	# the object is instantiated
	def __init__(self, first, last):

		# use our arguments as our instance variables
		self.first_name = first
		self.last_name = last

		# empty list to store our grades
		self.grades = []

	# a function that will cause the object to print out its name
	def say_hi(self):
		
		print (self.first_name, self.last_name)

	# a function to add new grades
	def add_grade(self, score):
		
		self.grades.append( score )

	def print_average(self):

		print( sum(self.grades)/len(self.grades) )

And we can use this functionality by instantiating a new object in our main program, like this:

a = Student('Justin', 'Bieber')
a.add_grade(90)
a.add_grade(80)
a.print_average()

Quiz

Now that you've completed this module, please visit our NYU Classes site and take the corresponding quiz for this module. These quizzes are worth 5% of your total grade and are a great way to test your Python skills! You may also use the following scratch space to test out any code you want.

Feedback

Tell us what you thought about this module (it's anonymous).
How helpful did you find this module on a scale of 1-5:
Very unhappy face
Unhappy face
Neutral face
Happy face
Very happy face
Which resource(s) in this module did you find the most helpful (check all that apply):

Copyright 2014-2018