Pieces of this lab are adapted from A Primer on Classes in Python by Aquiles Carattino, and was is was adapted for this course in November 2018 by R. Jordan Crouser, and updated by Alicia M. Grubb in Spring 2019/202.
In order to be sucessful in computer science, an important skill that you must develop is understanding how to implement and develop a specification, and knowing when you can take liberty with the specification. You also need to distingush between implementing and developing a specification. In the course work for CSC111, we ask for some things very precisely. It is important that you read every word of lab and assignment descriptions. Misreading or ignoring directions will result in incorrect software/code.
In this lab, you’ll practice working with classes and object-oriented programming techniques through several exercises, and finally by revisiting our YouTube Playlist from our previous lab.
OOP
Object-oriented programming (OOP) is a programming paradigm that enables us to specify the operations that can be performed on objects.
classes
When talking about OOP, its hard not to interchange the words class
and object
. In truth, the difference between them is quite subtle: an object
is an instance (or “a specific example”) of a class
. For example, "hello!"
is an instance of a str
, 3
is an instance of an int
, and 54.12
is an instance of a float
.
While we’re learning, we’ll try to be precise with our language: we will use the word class
when referring to the type of variable, while we will use the word object
to the specific variable itself. This will become clearer as we progress.
class
Let’s start by defining a class. A class definition consists of the class name, and a constructor __init__
, where attributes are typically given values.
class Person():
def __init__(self):
self.name = ""
Here our class Person
has one attribute name
and no methods.
Create a new file/document in Thonny and save it locally with the file name lab7p1.py
. Add the course header with the relevant information.
Below is a more complete example (add it to your lab file):
class Person():
def __init__(self, name):
self.name = name
def sayHi(self):
print("Hi, my name is", self.name)
def main():
my_friend = Person("Bob")
my_friend.sayHi()
if __name__ == "__main__":
main()
Instantiating is the moment in which we call the constructor and assign it’s value to a variable. The instantiation of the class happens when we say my_friend = Person(
Bob)
. Notice that this instantiation is outside the class.
In the first example, the constructor did not take name
as a parameter. This would cause issues if someone calls the sayHi()
method before actually having a name stored. To enforce all people to have names, we add a required parameter to the constructor.
To really drive home what’s happening here, let’s create three different instances, and then call the .sayHi()
method on each of them:
my_friend_1 = Person("Alice")
my_friend_2 = Person("Bob")
my_friend_3 = Person("Chloe")
my_friend_1.sayHi()
my_friend_2.sayHi()
my_friend_3.sayHi()
Even though the .sayHi()
method was only defined in one place (the definition of the Person
class
), it has slightly different behavior on each instance
. That’s because each Person
has their own name
attribute, and that is what the .sayHi()
method is referencing when it gets called on each instance
.
Another cool thing about attributes? They can be any data type - even other classes!
Person
class:
age
(initialized to a random integer between 1 and 99)favoriteColor
(initialized by passing an additional parameter to the constructor).sayHi()
method to include these attributes in the printed message, e.g. Hi, my name is Kacey, I am 21 years old and my favorite color is green.
Begin by downloading the short answer starter file (click this link), and save it locally. Open the file with either Thonny or a text editor. Answer the following short answer question (1-2 English sentences.):
Create a new file/document in Thonny and save it locally with the file name lab7p2.py
. Add the course header with the relevant information.
Now that we’ve started to get the hang of classes, let’s return to our YouTube Playlist from Lab 6. Recall that our program looked similar to the example below (we’ll omit the code to play the songs for brevity). Do not copy this code into your lab file. We are going to build the example from scratch.
def main():
# Initialize empty playlist
playlist = []
keepGoing = ""
while(keepGoing != "DONE"):
# Initialize empty song dictionary
song = {}
# Ask for info about song
song["title"] = input("Song title: ")
song["url"] = input("YouTube video: ")
song["duration"] = input("Video duration (MM:SS): ")
playlist.append(song)
keepGoing = input("Add another song? YES to continue, DONE to end: ")
# Print out the playlist
for song in playlist:
print(song["title"], "(" + song["duration"] + ")")
if __name__ == "__main__":
main()
Notice how our playlist was actually just a list of dictionaries? This wasn’t terrible, but it meant that our code for printing information about the songs (as well as playing them) was completely separated from where the relevant data was stored. Now that we know how to define our own class
, let’s see if we can do better.
We will begin by defining a class
to store the songs. Write the song class given the following specification (i.e., what needs to go in it). Be sure to comment your code as you go.
Class Song: A class that represents a single song.
Attributes:
Contructor and Methods:
Create a main
function (outside your Song class). We will use this function to test our class and will expand on it further in the next step.
You must test each method once you create it. You can write a helper functions (outside your class), to test it. Add the following code above your main
function.
def testSongPrintInfo():
s1 = Song("Help","https://www.youtube.com/watch?v=2Q_ZzBGPdqE", "02:19")
s1.printInfo()
Then call the following code from inside your main
function:
testSongPrintInfo()
Once your program works for printInfo
, add the play
method. Review our previous lab to recall how to play a song.
Add an additional test method entitled testSongPlay()
, to test your play
method.
Just having individual song objects isn’t all that helful because we still need to collect them somehow and be able to play music. The PlaylistManager
is a class that is responsible for keeping a list of the songs a user wants to store and play. Think of it like an iPod. You only need one of them, it initially comes out of the box with no songs stored in it. It has the ability to add a new song, play a song, shuffle songs, delete a song, list all the songs, etc. Our PlaylistManager will have only some of these abilities but you could easily keep working on this to make it have more and more of them.
Class PlaylistManager: Holds a list of Song objects in a playlist and provides methods for performing the operations on the playlist.
Attributes:
Song
objectsContructor and Methods:
For example, the print_all()
function may display:
Again, only write one method at a time and test it individually (add helper methods). It is more important to write and test printAll, before playAll because it will reveal basic bugs prior to trying to play songs.
To wrap up today’s lab, finish the main()
function below. It should create an instance of your PlaylistManager
, allow the user to add songs until they say they’re done, and then play the songs in the playlist. Use the methods you defined in your PlaylistManager
and Song
classes to do most of the work!
def main():
# Instantiate PlaylistManager
playlistMgr = PlaylistManager()
keepGoing = ""
while(keepGoing != "DONE"):
# Ask for info about song
title = input("Song title: ")
url = input("YouTube video: ")
duration = input("Video duration (MM:SS): ")
## TODO: Add the Song to the PlaylistManager
keepGoing = input("Add another song? YES to continue, DONE to end: ")
## TODO: when done adding songs print all the songs
## TODO: after printing all songs, play them all.
if __name__ == "__main__":
main()
Note: In this case, main does not have access to any of the song objects. This is an example of encapsulation.
lab7SA.txt
, lab7p1.py
, and lab7p2.py
files.