CS21 Week 13: Object-Oriented Programing
Week 13 Topics
The implicit self parameter
Access methods (getters)
Mutator methods (setters)
Before moving on to new content, let’s wrap up recursion.
So far, we have used lots of objects and their methods. Recall that an object consists of both data and methods — an object knows stuff and can do stuff. Here are some examples of objects we have used:
string objects (type
data: characters in the string
list objects (type
data: elements in the list
Zelle Graphics objects (e.g.
data: radius, center point, fill color, etc.
HydroDam objects from labs 9 & 10
Consider the student records CSV file we looked at during weeks 9 and 10; the first time through, we just stored this file as a list of strings (one per line), and then split each line into another list of strings (one per field).
Then later we used a class called StudentRecord to store each line instead of just a list of strings. This made our code easier to read, write, and understand; compare the following:
grade = students[i].get_GPA()
grade = float(lines[i].split(","))
Even if both lines accomplish the same thing, it’s a lot more clear what’s going on in the first one. Our goal in using classes is to make development easier by increasing modularity and abstraction, so we don’t have to remember every detail (such as which index in a list of strings contains the GPA) at all times.
Classes and Object-Oriented Programming
In object-oriented programming, we saw that objects described
complex types that contained data and methods that could act upon the
data. For example, we saw that lists and strings were objects, as
were elements of the Graphics library. Each of the objects we created
stored data (e.g., a center, radius and color for
and methods (e.g.,
The idea of encapsulation is to take a bunch of related data and functionality, and package ("encapsulate") it together in a single object. This has several advantages:
can pass a single argument to a function, instead of having to pass a whole bunch of related parameters
once it’s written and works, we don’t need to worry about the details of how (e.g. we don’t know what the inside of the
listclass looks like, and that’s ok because we don’t need to)
Defining a Class
Creating a class defines a new type of object. Classes define the
specific methods and data an object stores. To start, let’s look at the
definition of the
StudentRecord class we’ve been using.
student.py. What do you think each line is doing? What can
you explain with your neighbor?
The first key element is the class definition, which follows the format:
<ClassName> is a placeholder for the actual name you want to give;
class is a keyword to indicate that you are defining a new class.
The additional item,
(object), is an indication that the class will
follow certain protocols (note that
object is what you’ll type, this one is
not a placeholder). You should assume that this will always be there (you
can put something else in there if you want to build upon an existing class,
but that’s beyond our CS 21 discussion).
Within the class, you will see several class methods. Methods follow the same format as functions, but have the distinction of belonging to a specific class. As always, Python uses indentation to define what belongs, so we every definition that’s part of a class must be indented in the class definition block.
class ClassName(object): def __init__(self, param1, ...): #Constructor - initializes object and data def getter1(self): #Return some piece of information def setter1(self, valToSet): #Change something about the data
Normal methods can be named and defined like any other function, but must have a
special parameter called
self, which must always be the first parameter.
self is a reference to the object the method is being called on, and is used
in a method to refer to data in the object.
self.name: a "name" field that’s part of the object referred to by
self; this is called an instance or member variable
name: this is just a regular variable that will behave like it would in any other function
The key distinction is that member variables are part of the object, so they last as long as the object does; this is different from normal "local" variables that go away when the function or method returns.
Python has some special methods that use double underscores around their names; these get called automatically in some circumstances. There are a number of them, but the two most useful ones for us will be:
__init__: the constructor, called automatically when an object is created
__str__: the to-string method, called automatically when an object is passed to
Python will run the
__init__(self, x, y) method of a class every time we
create an object of that type. Every class must have a constructor — it
specifies the initial data we need to keep track of and gives it a starting
value. There are many other special methods that we’ll skip for right now.
__len__ (to obtain the length of a sequence e.g.,
list.__len__() method) and
__str__ (to convert an object to a
self is perhaps the single most confusing feature of python classes. You will
self everywhere when you are writing and designing a class. However,
you will not see
self at all when using a class, which is why we haven’t
talked about it until this week.
Notice that all methods in a class have a
self parameter as the first
parameter. This is required, but users of the class do not provide this
argument; instead Python automatically assigns the object that called the
method as the
self parameter. For example, if we create a
student and call
self parameter points to
student. This is common source of confusion when writing your first class. If
you look at the definition of
get_gpa(self), it looks like you need to pass
in one argument for the
self parameter. But since Python does this
automatically, users actually call this method with zero arguments, e.g.,
get_gpa with an extra argument will result in
a seemingly bizarre error message:
student.get_gpa(1) TypeError: get_gpa() takes exactly 1 argument (2 given)
Note that providing one user argument to
get_gpa() reports an error that
two arguments were provided. The automatic
self was the other (and
technically first) argument.
In summary, always put
self as the first parameter of a class method when
writing a method, but ignore this parameter when calling the method.
The meaning of self
You may be wondering: if
self is part of every method, but it’s ignored by
the user, why do we need it in the class definition? Recall that objects both
know stuff and do stuff. The method definitions inside the class are how
classes/objects do stuff, and the
self variable is how a particular object
knows stuff. In any method, we can declare a variable with a
self.course = "CS21"). If we set the value of one of these special
attributes in one method, we can access the value in another method without
passing an additional parameter. In effect, all methods share all the
Typically we use the
__init__ method to define and initialize values for all
attributes we will be using at some point in the class.
Testing a Class
Just as with top-down design, you should practice incremental development when defining a class. Usually, this involves writing a function, thoroughly testing it, moving onto the next function, etc. There are several options for how to test, including using a separate main program. However, a common strategy is to include some test code at the bottom of your class definition file. This way, the test code can always be run in the future when updates are made. To do so, remember to include the following lines at the bottom of your file:
if __name__ == "__main__": # Write testing code here
The first line ensures that the testing code does not run when you import the file, but rather only when someone calls the file from the command line:
$ python3 student.py
Exercises: Modify Student class and test
Let’s modify the
StudentRecordclass to add some getter and setter methods;
be sure to test your program at each stage, and don’t move on until you’re confident it works correctly.
get_age()method to the class
readdata-obj.pyto use this new method
set_gpa()method to the class
set_age()method to the class; make sure it does not allow negative values
Objects, scope, and internal method calls
Reminder: inside a class definition, the object is referred to as
self. If we want to
access its data, we use dot notation e.g.,
self.name is that records
the student’s name. The
name variable’s scope is the object
itself rather than the function it was originally defined in.
Similarly, from within a method of a object, you can call another method by
referring to the
self variable. For example, if a class has two methods,
method1(self, …) and
method2(self, …), you can call
class Thing(object): def method1(self, x, y, z): ... def method2(self, ...): ... self.method1(a, b, c) # Call method1() on self from within method2 ...
Objects provide encapsulation. In computer science, encapsulation can mean one of two related things:
A mechanism for restricting access to some of the object’s components.
A mechanism for bundling of data with methods operating on that data.
Classes and objects provide both mechanisms. On larger programming projects, it is common to have different people work on different parts of the program. Classes are a good place to divide the work. In this case, the class writer and the class user can agree on an interface. The interface specifies what methods the class has and what they should do. The class user doesn’t need to know or care how a class is implemented, only how to use the objects. The class writer doesn’t need to know or care how a class is used, only how to implement the interface.
Object-oriented programming also facilitates modularity. Modular programming is a software design technique that focuses on separating the functionality of a program into independent, interchangable pieces (aka "modules"), so that each piece contains everything needed to perform one aspect of the desired functionality.
Class definitions provide reusability, i.e., they let you create/reuse functionality while hiding technical details. With classes, you can rapidly create new and complex code by grabbing existing code "off the shelf" and reusing it for novel purposes.
Exercises: Modify Student class and test
Let’s modify the
StudentRecordclass to add a list of course grades; be sure to test your program at each stage, and don’t move on until you’re confident it works correctly.
add a line to the constructor that creates an empty list called
define a method
get_gradesthat returns the list of grades
define a method
add_gradethat takes a grade as a parameter and appends it to the list of grades
define a method
compute_gpathat goes through the list of grades and re-calculates the student’s GPA. This method should change
self.gpaso it has the new value; it should also return that new value.