Object-Oriented Programming
0. Learning objectives
So far, we've seen two types of programming. The first one, functional programming, involved writing functions in which we achieved repetition using recursion: this was function-centric. The second type, imperative programming, involved using data structures such as lists and dictionaries to process larger chunks of data using loops (for- and while-loops): this was data-centric. What if we need both? Well, object-oriented programming gives us both. We will be able to define some new custom types that
|
![]() Everything in Python is an object.This means the types we have seen so far (integers, floats, strings, lists, dictionaries, tuples) are all objects. |
1. A first attempt: writing our own Snapchat
Suppose we want to write our own Snapchat application: let's call it MiddSnap
. Before doing so,
let's brainstorm some basic functionalities we want our application to provide:
|
![]() |
Of course, there are many more features we may like our MiddSnap
application to have, but let's
just stick to the above list for now. Some words are in bold-faced blue and some words are in bold-faced red.
Can you tell the difference? The bold-faced blue words are functions we would like our
application to achieve, whereas the bold-faced red words are data we would like to
store in our application. Of course, the functions operate on some of these pieces of data, so the two are
related. Let's focus on the data for now. You might notice the word "contact" appears several times, for
example, we store a "list of contacts", or we send/receive something to/from a contact. How should we represent
a contact? Specifically, what data do you need for a contact? How about:
- first name
- last name
- username
- phone number
- avatar
- number of points
We could keep a list of strings for the first names, a list of strings for the last names, a list of strings
for the usernames, a list of integers for the phone numbers, a list of MiddImage
images for the
avatars, and a list of integers for the score. That's a lot of lists! Also, we need to somehow make sure
corresponding items are stored at corresponding indexes to make access easier. On top of that we would need to
make sure that everything stays synchronized as we add or remove contacts... That's a lot of bookkeeping to do!
Maybe we can use some dictionaries, but that doesn't really get us much further.
Wouldn't it be nice if we just had some kind of "data structure" that represents a contact with all the aforementioned information?
Let's make things a bit worse! What if we want to store an actual Snap (maybe in our list of memories)? The
most obvious thing we should store for a Snap is an image. So maybe we can just store a list of images as our
memories (remember that you can make lists of whatever you want!). But wait - to reconstruct the memory, some
snaps have text, some don't, and some might have some drawings. So we should probably store an extra list of
booleans like has_text
or has_drawing
to fully reconstruct the Snap whenever we want
to see our memory again. And then we can store the actual text as a list of strings and the drawing as another
list of images. Since some snaps may or may not have some text or drawing, this adds a bit of complication to
the data we need to keep track of... This would require quite some planning to put together with the current
tools in our belt...
Again, it would be really nice if we could just store some all-encompassing piece of data for a snap that contains the image, text, drawing, etc.
2. Classes & objects Luckily, we can address all of the concerns we had when developing Next we would define some functions that are designed specifically for each object. For example, we would define a draw function for a snap. This function would be specific to a "snap" object - it wouldn't make sense to call a "draw" function for a "contact." Each object would be a nice package that contains some data and the functionality that goes with that data. In Please repeat this to yourself several times as it is often a source of confusion: an object is an instance of a class. |
![]() A class definition is like the blueprint for a house. |

Any house is a constructed instance of the blueprint, maybe with a different wall color or door color.
2.1. Attributes & methods Before we jump into some 2.2. Defining a class To define a class, we need to tell Just like with functions, you should place a docstring immediately after your class declaration (the
next line after the colon). Thus, typing Okay, now we're ready to define some methods. The first method you will always (always, Always, ALWAYS,
ALWAYS) define is the But where am I saving these attributes you ask??? Ah, the |
2.3. Creating an object
Remember when you type x = 2
, this really means "create an integer and store it in a variable
called x
". Well, the truth is that, in Python, x
is an object of class
int
(recall the output of type(x)
). This means that somewhere deep down in
Python
there is a class definition for an integer. In fact, all the types we have seen so far are
objects: floats, strings, lists, dictionaries, etc. Everything in Python
is an object!
Recall the methods we used to create an empty list (L = list()
) or an empty dictionary
(D = dict()
). What these do, behind the scene, is to create objects that are instances of a
list
or dict
class.
The same is true for our own custom classes - we create objects by calling obj = ClassName()
Yes,
we yse the name of the class followed by parentheses, as if it where a function because it is!. When you do so,
you are actually calling the __init__()
method defined for that class. If your
__init__()
method takes additional parameters, you can pass them in your call to
obj = ClassName()
. Note that the self
parameter is not included and the order these
come in is the same order as they are listed after the self
.
2.4. Accessing attributes and calling methods using an OBJECT
Now, suppose that you have created an instance of your class ClassName
, using
obj = ClassName()
. In order to access the attributes of the class (those defined in the
__init__()
method), we will use dot notation. For the example above, we could access
obj.attribute1
and obj.attribute2
.
In order to call a method of a class (those defined within the block of code in the class definition), we also
use dot notation. In the same example, we would call obj.method1()
or
obj.method2()
(remember to use parentheses for methods since they are functions). When
Python
sees this syntax, it implicitly passes obj
as the self
parameter, the first argument in the methods definition. So you don't have to pass anything to
self
.
2.5. Accessing attributes and calling methods from inside a CLASS definition
What if we want to access attribute1
from within method1
? Well, since we passed in a
reference to the object (the first argument, i.e. self
, to method1
), then we can
access attribute1
of self
directly! Remember, self
is an object (an
instance of the class) too, so we can access attributes and methods in the same way as the previous section.
Thus, using attribute1
within any method would look like self.attribute1
, calling
method1
from method2()
would look like self.method1()
, and calling
method2
from method1()
would look like self.method2()
.
2.6. Everything in Python
is an object
In reality, you have been using objects all along! Remember when we typed type(2)
or
type(1.5)
and saw <class 'int'>
and <class 'float'>
in
return? This is because, as we mentioned before, numbers, lists, dictionaries, strings, etc. are all objects.
You can use the dir
function to list out all the methods for one of these built-in types.
Remember how we kept using the word "object" and "method" when talking about MiddImage
? It's
because they're objects too! If you open up middimage.py
you'll see the class definition for
MiddImage
. A MiddImage
object has attributes for the width
,
height
, channels
and the pixels (stored in an attribute called _data
-
which is a valid variable name).
Another time we used objects, was actually with the turtle
module! In fact, we were using an
instance of the Turtle
class. We didn't mention this but, if we want to have multiple turtles, we
could create several instances (objects) of the Turtle
class.
Examples
Example 0: points
Before building more advanced applications with object-oriented programming, let's study some smaller
applications. In this example, we will write a class to represent a point in $2d$. We will break up this example
into a few parts. In this first part, we want to write a Point
class that simply represents a point
in the plane with $x$ and $y$ coordinates. In part II (the next section), we will add some attributes and
methods to visualize these points as dots.
When defining a class, you should ask yourself a few questions:
- What data (attributes) would I like to store in each instance (object derived from) of my class?
- What functions (methods) would I like to have available to interact with instances of my class?
In this first part, we'll keep things simple. We will just store the $x$ and $y$ coordinates of the point. We will also provide methods to calculate
- the distance between the point and the origin, and
- the distance from one point to another
Note that we used the __str__
method to print some information about a Point
object.
This is the function that is called when you type print(pt)
, where pt
is an instance
of the Point
class. In fact, it's equivalent to calling print( pt.__str__() )
. method
with two underscores (__
) on either side of the function name are special functions that
Python
looks for when you try to
- use other built-in functions on your custom objects (such as
print
,str
orlen
) or - perform operations on your custom objects. Yes, this means you can define
+
for your custom objects!
dir(2)
or dir([])
. You can also overload (redefine) the __repr__
function so that Python
will show a representation of your object when you type it in the console or
shell
Example 2: triangle soup
Let's do another geometric application (geometric applications are cool because they really help you visualize
the concepts). This time, we will write a Triangle
class which keeps track of the bottom left
corner of the triangle $(x, y)$ and the side length $L$. We will also store a "tilt angle" which is the
angle the bottom side of the triangle makes with the x-axis. A color can also be provided (defaults to
'blue'
if no color provided) that will be used when drawing a triangle object with the
turtle
. Thus we will write a draw
method for the Triangle
class.
It may also be useful to write area
and perimeter
methods, but we'll skip this for
now. What other methods do you think would be useful to define for the Triangle
class?
Example 3: bouncing balls
This is a great example combining a lot of the elements we have seen so far in something that uses a typical animation technique!