RobotRogue
1

Lesson 1 - Use Functions

Take A Break!

Write a program that reminds a user to take a break. The reminder will execute every two hours, and when it does, it opens a browser to a specific Youtube video.

Python has an existing function called webbrowser.open... and if you provide a URL as a string, it will open to that URL. Here's the syntax of that:

webbrowser.open ("https://www.youtube.com/watch?v=QH2-TGUlwu4")

Once you put the above line into IDLE, save it as a .PY file (break time.py), then click Run > Run Module to execute it, and see what happens.

Result: NameError: name 'webbrowser' is not defined

Okay, we forgot to tell Python what browser to use. Use import webbrowser in the first line to fix that.

So here is our code so far:

import webbrowser
webbrowser.open ("https://www.youtube.com/watch?v=QH2-TGUlwu4")

Now let's figure out how to make the program wait to run for a defined period of time (such as, 2 hours)...

time.sleep()

This lets you let the program sit idle for the time you define. Oh, and one more thing, don't forget to use import time first, or else it will error. Lastly, the time.sleep value is in seconds. (Another hint, you can actually use time.sleep(2*60*60) -- which takes the number, times 60 seconds, times 60 minutes...)

Here's the updated code:

import time
import webbrowser

time.sleep(7200)
webbrowser.open ("https://www.youtube.com/watch?v=QH2-TGUlwu4")

Now, how would we add a loop so it runs this code to take a total of three breaks?

  1. 1) We need a counter, so every time the loop is run, the counter either increments to 3, or counts down from 3 to zero.
  2. 2) We need to check if the counter is equal to our total amount of times to run. (Such as, if counter < 3:)
  3. 3) We may need to break out of the loop when finished?

My attempt:

counter = 0
while counter != 3:
print "Hey - take a break maybe?"
counter = counter + 1

Instructor Answer:

total_breaks = 3
break_count = 0

while (break_count < total_breaks):
(codeblock)
break_count = break_count + 1

And to fancy the code up a bit, we can use time.ctime with print to show what time we kicked off the program. Below is our updated code:

import time
import webbrowser

counter=0

print ("This Timer Started on "+time.ctime())
while counter != 3:
time.sleep(2*60*60)
print "Break Time!!"
webbrowser.open ("https://www.youtube.com/watch?v=QH2-TGUlwu4")
counter = counter + 1

That's it! Python has a lot of great built-in functions in the Python Standard Library. Take a look through the library to see what else they have in there.

Secret Message

Someone has stolen your keys, and the answer to where they are is hidden in a folder full of pictures on your computer. The files are alphanumeric. The alpha characters when by themselves, sort the photos into the answer. But because they contain numeric characters, the sort is all messed up. Problem is, the numerics are random. Some at the start of the filename, some at the end, and maybe some in the middle.

Having to rename all those files to remove the numeric characters would take forever! Okay, so can we write a program that will do this for us? And how would you do it?

Rename a bunch of files on your computer -- how would you do it?

  1. Point the program to a specific folder
  2. Look for numeric characters in the filenames
  3. Rename the file while removing the numeric characters

Is that the correct way to go about it? Maybe... we'll find out.

The instructor suggested:

  1. Looking at a folder, and getting a list of the filenames in that folder.
  2. Rename those files.

Okay, let's get to building the program! (File path: c:\prank) - the zipfile can be found here: https://s3.amazonaws.com/udacity-hosted-downloads/ud036/prank.zip

To start, we want to use a built-in standard function called OS. A sub-function of that is os.listdir('path') which as the name implies, returns a list of files in the path you specify. One thing to note about the path... start it with the letter r it stands for raw path, so it takes the path string as it is, and doesn't interpret it as anything else.

Here's a high-level of what we're going to create:

import os
def rename_files():
file_list = os.listdir(r'c:\prank')
print file_list
# code block to rename the files...

rename_files()

So as before, if you want to call a function from the Python Standard Library, you have to import the function first. Next, we gave the function a name - rename_files, and then we made the function get a list of files from 'c:\prank', but we assigned the list to a variable called file_list - and then we will display (print) the file_list variable to make sure it's correct.

Next, we want to figure out how to rename the files in that directory (or list?) -- and as it turns out, if you search the Python Standard Library - OS function section -- you will find that there is a function that does just what we need... called os.rename(src,dst) -- now also when you read what that function does, you can see there are also a lot of considerations to take, especially regarding the destination (dst) and OS-specific issues.

Because there are a lot of files in that folder, we will probably have to use a loop to get that function to rename each file. So let's write a loop, using the os.rename function as the main action of the loop.

for file_name in file_list:
os.rename(file_name, )

Okay, so we've gotten that far, but we still have no idea how to strip the numeric values from the file name... so we should figure out how to do that part next before we can finish writing the for loop.

Google to the rescue - turns out there is something that does just what we need, and that way we don't have to code a complex function ourselves. It's called the string.translate method, that returns a copy of the input string, but all the characters have been translated using either the table or deletechars parameters.

Now, to make the string.translate function work for us... we need to tell it what we want it to do.

  1. We start with specifying the string name -- file_name.translate
  2. The first parameter of the function is table - which translates one set of characters to another. Since we aren't doing that, we specify the parameter as none -- so now our code looks like this: file_name.translate(None,
  3. The second parameter of the function is a list of all the characters we want to remove... since we want to remove all numeric charaters, this is easy to define. So our code will look like this: file_name.translate(none, "0123456789")

If you try running that in the python shell just to test, you'll see what your result of the test will be:

file_name="48athens.jpg"
print file_name
file_name.translate(None, "0123456789")

So the result was a success! Now we have to figure out how to put that into our 'rename_files' function, which will look like this:

import os
def rename_files():
file_list = os.listdir(r'c:\prank')
print file_list

for file_name in file_list:
os.rename(file_name, file_name.translate(None, '0123456789'))

rename_files()

Because good programming involves good testing (or, testing at all!) - run the program and see what happens.

Looks like we got an error. But what does the error message mean?

  1. My guess: There was no input specified when calling the rename_files() function.
  2. Instructor answer: We may not be working for the correct directory.

Python being a fairly competent programming language, there happens to be a way we can check to see what's going on. Using os.getcwd() or 'Get Current Working Directory'... we can actually see where the program is looking.

To put that into our code, we can assign os.getcwd to a variable, and print that variable... now, we probably want it to do this before we run the for loop, so we can check our work. Here's how...

import os
def rename_files():
file_list = os.listdir(r'c:\prank')
print file_list
saved_path = os.getcwd()
print ('The current working directory is ' + saved_path)

for file_name in file_list:
os.rename(file_name, file_name.translate(None, '0123456789'))

rename_files()

After running that as a test -- you can see that the directory is totally wrong. I guess that means we have to set the path/directory. We can do that by using the os.chdir(path) aka the 'change directory' function built into Python. One thing to remember is the right place in the code to put this call. Here's how it will look after we use it:

import os

def rename_files():
file_list = os.listdir(r'c:\prank')
print
print file_list
saved_path = os.getcwd()
print
print ('The current working directory is ' + saved_path)

os.chdir('c:\prank')
for file_name in file_list:
os.rename(file_name, file_name.translate(None, '0123456789'))
os.chdir(saved_path)

rename_files()

One thing you'll notice is after we set the path, to c:\prank, after the for loop, we set the path back to the original current working directory value. It's generally a good idea to 'leave things the way you found them' if you make directory changes and whatnot. It will save you headaches later trying to figure out why something isn't working.

Okay, so if you run the code, it won't error. It did what it was supposed to do! (Confirm this by checking the c:\prank folder) -- one final note -- if you want to see the program in action, you can get it to print the before/after filenames to the console as it makes it's changes.

After a bit of reworking to 'make it my own' my final code looks like this:

import os

def rename_files():
file_list = os.listdir(r'c:\prank')
saved_path = os.getcwd()
print ('The current working directory is ' + saved_path)
print

os.chdir('c:\prank')
for file_name in file_list:
print ('Old name - ' + file_name + ' --> New name - ' + file_name.translate(None, '0123456789'))
os.rename(file_name, file_name.translate(None, '0123456789'))
os.chdir(saved_path)

rename_files()

Check out more fun stuff in the next lesson.

2

Lesson 2 - Use Classes

Draw Turtles!

If you were to tell someone how to draw a box by walking around, what instructions would you give them?

You would probably tell them something along the lines of:

  • Step forward two steps
  • Turn left 90 degrees
  • Step forward two steps
  • Turn left 90 degrees
  • Step forward two steps
  • Turn left 90 degrees
  • Step forward two steps
  • Turn left 90 degrees
  • Stop

We can actually do the same thing, but using python instead. How? With a built-in function called turtle. Now, to use turtle, we have to use import again, and of course in order to draw using Python, we need to output it to a graphics window. To do that we will use "window=".

Alright, with that in mind - let's get some code written to do what we explained previously in common english - but this time in code.

import turtle

def draw_square():
window = turtle.Screen()
window.bgcolor("black")

Nelson = turtle.Turtle()
Nelson = turtle.Pen()
Nelson.shape("classic")
Nelson.color("#00ccff")
Nelson.speed(1)

Nelson.forward(100)
Nelson.right(90)
Nelson.forward(100)
Nelson.right(90)
Nelson.forward(100)
Nelson.right(90)

Sara = turtle.Turtle()
Sara.shape("turtle")
Sara.color("white")
Sara.circle(75)

window.exitonclick()

draw_square()

So first we start by importing turtle into Python. We then define a new function called draw_square. We then do some stuff we haven't done up until now: we call classes from inside turtle.

We do this in two places - one by using window = turtle.Screen (which calls the Screen class within turtle, and assigns it to window) and then Nelson = turtle.Turtle which calls the Turtle class from within the turtle library, and assigns it to the name Nelson.

What we are doing when we call a class and give it a name is creating an object or instance of that class.

After all of that, we can assign specific attributes to the instance of the class such as the shape, the speed, and the color among many of the other possible attributes available.

Once we've done that, we can get to the business end of drawing that square. Notice how the language is pretty similar to common english, forward, right, etc.

Now that we drew one square called Nelson, we can create a second shape with its own unique attributes called Sara. They can be controlled the same using the same language, but behave and look a little different.

We can do better...

Now our code looks pretty simple, but there are some issues with what we've written so far. (Computer Science majors would be screaming at us right now) -- Namely, we're doing a lot of repetitive tasks, and well, drawing a circle and a square in a function that we called "draw_square" doesn't quite make sense.

So let's fix our code, and try some more things while we're at it!

import turtle

def turtle_art():
window = turtle.Screen()
window.bgcolor("black")

draw_square()
draw_circle()
draw_triangle()

window.exitonclick()

def draw_square():
Nelson = turtle.Turtle()
Nelson.shape("classic")
Nelson.color("#00ccff")
while s in range(1,5):
Nelson.forward(100)
Nelson.right(90)

def draw_circle():
Sara = turtle.Turtle()
Sara.shape("turtle")
Sara.color("white")
Sara.circle(75)

def draw_triangle():
Horace = turtle.Turtle()
Horace.shape("triangle")
Horace.color("green")
for t in range(1,4):
Horace.backward(150)
Horace.left(120)

turtle_art()

So what did we do?

First, we broke each shape into its own separate function... square, circle, triangle... and then created a function called turtle_art that called all three shape functions. Technically we could have just renamed the draw_square function to draw_art and lumped everything into that, but having separate functions makes things a bit more controllable.

We then added for loops into the mix to handle the repetitive parts, which shortens the code, and also allows us for more flexibility down the road. We also used range in the loop to control how many loops get run, which we can also change as needed.

Send Text

To send SMS messages using Python, we'll need to call a library called Twilio. However, Python doesn't include Twilio by default, you'll have to install it. Installing external libraries into Python is common practice, although Python has an extenside built-in library, many external libraries have been created to extend functionality.

To import a library into Python, there are a few resources available to help you figure it out (aside from Google):

After you have Twilio installed, you'll want to create an account on their site, set up a Twilio test phone number, and get an Auth Token and you Account SID.

Once you've gone through the Twilio account setup... use this Python code:

from twilio.rest import TwilioRestClient

# Your Account Sid and Auth Token from twilio.com/user/account
account_sid = "{{ACCT SID GOES HERE}}"
auth_token = "{{AUTH TOKEN GOES HERE}}"
client = TwilioRestClient(account_sid, auth_token)

message = client.messages.create(body="Hey Nelson! You can program!",
to="+16045555555", # Replace with your phone number
from_="+17785555555") # Replace with your Twilio number
print message.sid

Now, we're used to using import... but this time we see the word from, then import. What does from mean?

From makes a reference to the library or a module from within a library, and then we specify what to import from within that library, or specific attributes of that module. (Here's the reference article: http://www.tutorialspoint.com/python/python_modules.htm)

Now, if you want to see what this portion of the library actually does - check it out on GitHub - https://github.com/twilio/twilio-python

In our lesson we were asked: "What is a class?" and "What is an instance of a class" and also asked to see if we could explain what classes were like, using another metaphor if we could.

What is a Class?

A class can be described as an element of a library. Twilio is a library. Twilio has a whole set of elements inside that library. Each element has it's own abilities. The specific behaviors for the abilities are defined in the object/instance.

So a the Library is the container that stores all the Classes. The classes inside that library do things, each class does it's specific thing. When you create an instance/object of a class, you can further specify what the object does, if the Class allows for it.

What is an instance of a class?

An instance or object is like a child of the class, with it's own attributes and behaviours that follow the rules that the Class has outlined.

The library can be thought of as a blueprint for a house. Classes can be thought of as the rooms, the elevators, the heating/cooling, etc. When you create an instance/object - it's like you are picking a room. The class says a room has 4 walls, a floor, a ceiling, and a door. Your instance/object of the class, you can specify other things -- the position of the door for example, or the area/size of the room, the type of floor, the color of the walls, and so on.

Is there another way to explain classes?

Think of a novel. The characters in the novel can be considered classes. Each character can be an instance - because each character exists in the book, but each character has their own attributes, such as hair color, personality, etc.

Profanity Check

How do we make a profanity filter that lets you know if your document has any profanity in it?

  1. Read text in document
  2. Check text for profanity

Part One:

For the first part, we'll write a method that locates a file, reads it, displays it's content, and closes the file. Here's how:

def read_text():
quotes = open("C:\detect_profanity\swear_doc.txt") #Opens the swear doc and assigns it a name of quotes
contents_of_file = quotes.read() #by using .read - you are telling python to call the function 'read', and assigning all that text a variable of contents_of_file for future use
print (contents_of_file) #displays the quotes to the console
quotes.close() #Once the file has been read, we should close it -- it's good coding convention to do so

read_text()

One thing we'll need to make that work is to create a folder in c:\ called detect_profanity, and add a text file inside of it called swear_doc.txt... and lastly put some text into that text file (you can copy and paste some of you favorite movie quotes, but 'accidentally' misspell one word to make it a swear word)

Once you run the above code in Python, you will see the movie quotes from the file displayed in the IDLE console. We're off to a good start.

Now, for def open()... where would you put that in the grand scheme of things? Inside the OS module? somewhere in the python standard library? In this case, it's part of the standard library, because it gets called so often in programming, that it would be better served to do so, instead of forcing programmers to have to call import OS or some other module.

Interestingly, the function Open returns an 'object' of the file type... wait... object, eh?

Remember this? nelson = turtle.Turtle() ? nelson is an object of Turtle. Just like when we have quotes = open(file_location) ? quotes is an object of file. Kind of the same but sort of different. We'll learn why later on.

Part Two:

For the second part, we have to check the text that was read for profanity. Here's how we will go about doing that...

In this case, we won't be using a dictionary file... those are cumbersome and require constant updating. Instead, there is a great site out there called wdyl.com, where you can pass in a query string, and it will return a true for false.

Since the WDYL web service exists, we have to learn how to use it. In this case we have to pass a query to it - but how do queries work? Here's some documentation: http://en.wikipedia.org/wiki/Query_string

Okay, let's get some code together, and I'll include some comments on what does what and why:

import urllib #we have to import this because check_profanity calls it

def read_text():
quotes = open("C:\detect_profanity\swear_doc.txt") #Opens the swear doc and assigns it a variable name of quotes
contents_of_file = quotes.read() #by using .read - you are telling python to call the function 'read'
print (contents_of_file) #displays the quotes to the console
quotes.close() #Once the file has been read, we should close it -- it's good coding convention to do so
check_profanity(contents_of_file) #this runs the check_profanity function we wrote, and passes in the contents_of_file data that we read

def check_profanity(text_to_check):
connection = urllib.urlopen("http://www.wdyl.com/profanity?q="+text_to_check)
#urllib which helps us get information from the internet, and .urlopen is a function which takes in a link to a website
#we then pass in the url, and add in the text string we want to check as part of the same URL
#we want to name this 'connection' for future use

output = connection.read()
#connection will return a true or false, we need to read that response and we then will store the response in a variable called output
print(output)
connection.close()
#Remember, when you open a connection, its a good idea to close it afterwards.

read_text()

Hopefully that all made sense. Now for a quick question - where does urllib and urlopen fit in to the Python Standard Library? urllib is its own module in the library, urlopen is a function within urllib.

Alright, since the output is not too user-friendly, we made some small changes to the code to hide some of the outputs, and added an if statement to convert the true/false output into something more readable.

import urllib #we have to import this because check_profanity calls it

def read_text():
quotes = open("C:\detect_profanity\swear_doc.txt") #Opens the swear doc and assigns it a variable name of quotes
contents_of_file = quotes.read() #by using .read - you are telling python to call the function 'read'
#print (contents_of_file) #displays the quotes to the console
quotes.close() #Once the file has been read, we should close it -- it's good coding convention to do so
check_profanity(contents_of_file) #this runs the check_profanity function we wrote, and passes in the contents_of_file data that we read

def check_profanity(text_to_check):
connection = urllib.urlopen("http://www.wdyl.com/profanity?q="+text_to_check)
#urllib which helps us get information from the internet, and .urlopen is a function which takes in a link to a website
#we then pass in the url, and add in the text string we want to check as part of the same URL
#we want to name this 'connection' for future use

output = connection.read()
#connection will return a true or false, we need to read that response and we then will store the response in a variable called output
#print(output)
connection.close()
#Remember, when you open a connection, its a good idea to close it afterwards.

if "true" in output:
print("There's a swear in there!!")
elif "false" in output:
print("Don't worry, your document is swear-free!")
else:
print("Uhh, something went wrong with reading the file. Sorry.")
#The if statement basically converts the output into something more readable, and displays that
#The elif is 'else if' so when the if statement fails, it moves onto else if, if that faisl, it goes to the else statement
#The else can be seen as a form of 'error handling' or a 'graceful error'

read_text()

And now to summarize some ideas...

  • brad = turtle.Turtle >>> brad is an object of Turtle.
  • quotes = open(file) >>> quotes is an object of File.
  • connection = urllib.urlopen() >>> connection is a file-like object

If you look at how open and urlopen are implemented in Python, you'll find that the init function is being called, like in classes.

3

Lesson 3 - Make Classes

Movie Website

In order to further understand classes, we should begin by creating one. So lets make a new class, 'Movie' - that our movie website can consume, and each individual movie is an instance/object of the movie class.

Thing is, what data points should be part of the Movie class? Title? TV_Station? Youtube_trailer? Reviews? Storyline? Season? Poster_image? Episode_number?

Well, almost all of the above, save for tv_station, season, and episode_number... those are more specific to TV shows instead of movies.

Here's an example of how we want to use this:

toy_story.storyline()
>>> A story of a boy and his toys that come to life

avatar.show_trailer()
>>> The trailer for Avatar will play

Lets get started writing our code! First, let's start with things for class Movie to remember: Title, Storyline, Poster_image, Trailer_youtube...

Next, when we define the name of the class, notice how we use uppercase for the name... well, this is done based on what's described in the Google Style Guide for Python (http://google-styleguide.googlecode.com/svn/trunk/pyguide.html) - it's basically a 'best practices' guide that Python programmers use to keep with standardized conventions, so other people familiar with the style guide can read your code easier.

We will write a new python file called media.py... inside that will be our Movie class (and potentially other classes), and also write a new python file called entertainment_center.py, which will import the media.py file, and call the Movie class to be used.

• media.py

class Movie(): #this creates a new class in media.py, and gives it a name of Movie
def __init__ (self, movie_title, movie_storyline, poster_image, trailer_youtube):
#__init__ reserves some space in memory for the rest of the arguments in the object, we don't specify an argument/value for self since it is implied/created/needs no value
#self is the object being created, and we also define arguments that get used as well
self.title = movie_title
self.storyline = movie_storyline
self.poster_image_url = poster_image
self.trailer_youtube_url = trailer_youtube

• entertainment_center.py

import media #imports media.py for use

toy_story = media.Movie("Toy Story",
"A story of a boy and his toys that come to life",
"http://upload.wikimedia.org/wikipedia/en/1/13/toy_story.jpg",
"https://www.youtube.com/watch?v=vwyZH85NQC4")
#media is the name of the python file
#Movie is a class within the media python file
#Everything else you see are arguments that get passed to media.py > init
print(toy_story.storyline)

So far so good... but what's going on behind the scenes when we run the code for toy_story = media.Movie(etc....)?

Well when we run it, here's a breakdown of what is going on:

  • toy_story >>> __init__ gets called.
  • self = toy_story
  • movie_title = "Toy Story" >>> Assigns the value of "Toy Story" to the movie_title variable
  • movie_storyline = "Toys come to life..." >>> Assigns the value of "Toys come to life..." to the movie_storyline variable
  • poster_image = "(link to the poster)" >>> Assigns the URL value to the poster_image variable
  • trailer_youtube = "(link to the YouTube video)" >>> Assigns the URL value to the trailer_youtube variable

So when we create the object toy_story, all of the variables get initialized into memory space with the correct values.

One thing we want to do next, is to define a function show_trailer... that will open a browser window, and play the YouTube link for that trailer.

In this part, we will define show_trailer...

import webbrowser # this is done to be able to make calls to the default web browser and pass links/urls to it

def show_trailer(self): #we always add self as the first argument for instances defined within a class
webbrowser.open(self.trailer_youtube_url) #the URL is stored in the instance variable self.trailer_youtube_url

Below is the 'finalized' code to see how it all works together... run the entertainment_center.py file and see what happens!!

• media.py

class Movie():
def __init__ (self, movie_title, movie_storyline, poster_image, trailer_youtube):
self.title = movie_title
self.storyline = movie_storyline
self.poster_image_url = poster_image
self.trailer_youtube_url = trailer_youtube

def show_trailer(self):
webbrowser.open(self.trailer_youtube_url)

• entertainment_center.py

import media #imports media.py

toy_story = media.Movie("Toy Story", "A story of a boy and his toys that come to life", "http://upload.wikimedia.org/wikipedia/en/1/13/toy_story.jpg", "https://www.youtube.com/watch?v=KYz2wyBy3kc")

avatar = media.Movie("Avatar", "A space marine on an alien planet", "http://upload.wikimedia.org/wikipedia/id/b/b0/Avatar-Teaser.Poster.jpg", "https://www.youtube.com/watch?v=5PSNL1qE6VY")

high_fidelity = media.Movie("High Fidelity", "Rob, a record store owner and compulsive list maker, recounts his top five breakups, including the one in progress.", "http://upload.wikimedia.org/wikipedia/en/8/8f/High_Fidelity_poster.jpg", "https://www.youtube.com/watch?v=F2L58znA2e0")

high_fidelity.show_trailer()

Once you've run the file, you'll see what it does. Pretty cool eh?

Advanced Topics

Class Variables - Class Variables are variables that ALL instances or objects of the class will use/share.

For example: valid_ratings = ["G","PG","PG-13","R"]

Above is a list (can also be an array) of all possible ratings a movie could have. (I know there are way more ratings, but this is just an example)

Each instance/object can have it's own rating. Toy Story would be G, whereas Avatar would be PG-13... instead of specifying the ratings in each instance, you can create the class variable, and then each instance would call it, and set the value at the instance/object level.

Now, the Google Python Style Guide says that if we use a variable that is likely never going to change (or rarely), you put it in all uppercase. It's safe to assume that valid_ratings won't change any time soon, so in your media.py file, when you specify the variable, put it in all caps.

DOC Strings - The __doc__ string allows you to view the documentation in the class that you call (if it exists). The syntax looks like this:

turtle.Turtle.__doc__

If you were to run that at the console, it would display some documentation about the class Turtle. Give it a try and see for yourself!

Now how would you use it in your class Movie? Here's an example:

import webbrowser

class Movie():
""" This class provides the user a way to store movie related information. """
VALID_RATINGS = ["G","PG","PG-13","R"]
def __init__ (self, movie_title, movie_storyline, poster_image, trailer_youtube):
self.title = movie_title
self.storyline = movie_storyline
self.poster_image_url = poster_image
self.trailer_youtube_url = trailer_youtube

def show_trailer(self):
webbrowser.open(self.trailer_youtube_url)

Okay, now that we played around with that, you should be aware that Python has a number of pre-defined variables. (http://www2.lib.uchicago.edu/keith/courses/python/class/5/)

There are a couple more that we can play with, such as __name__ and __module__

  • __name__ will output the name of the Class that is being called
  • __module__ will output the name of the Module that is being called

Inheritance - Inheritance is a fairly simple but also a somewhat complex concept to understand.

  • A parent is a top-level object or class. The parent can have some attributes associated with it such as -- last_name and -- eye_color...
  • A child is the next level under a parent. It will inherit the partent attributes. It can also have its own attributes on its own such as -- number_of_toys...

Some sample code might help explain it more:

class Parent():
def __init__(self, last_name, eye_color):
print("Parent Constructor has been called.")
self.last_name = last_name
self.eye_color = eye_color

class Child(Parent):
def __init__(self, last_name, eye_color, total_toys):
print("Child Constructor has been called.")
Parent.__init__(self, last_name, eye_color)
self.total_toys = total_toys

#billy_cyrus = Parent("Cyrus","blue")
#print(billy_cyrus.last_name)

miley_cyrus = Child("Cyrus","Blue",5)
print(miley_cyrus.last_name)
print(miley_cyrus.total_toys)

• First we define the Parent class.
• Next we define the __init__, which has to include self (always), and then any arguments (in this case, last_name, eye_color) that we will define.
• Next we will put in a print statement, so we know whenever the Parent class is being called.
• Next we define the two arguments attributes -- self.last_name = last_name and then self.eye_color = eye_color...

• Now we define the class Child... BUT... we include Parent as an argument. Because the child will inherit the parents attributes.
• Next we define the __init__ again, passing in self, then the two arguments/attributes from the parent, and then one argument/attribute that the child will have.
• Next we will put in a print statement, so we know whenever the Child class is being called.
• Next we define the argument attributes -- self.total_toys = total_toys

Making sense so far? When we run the following code... the output may seem strange, but we'll explain why in a minute...

miley_cyrus = Child("Cyrus","Blue",5)
print(miley_cyrus.last_name)
print(miley_cyrus.total_toys)

>>>
Child Constructor has been called.
Parent Constructor has been called.
Cyrus
5

Why did the output look like that? Here's why:

miley_cyrus = Child("Cyrus", "Blue", 5)

  • creates an instance of class Child, and gave it a name of miley_cyrus...
  • as soon as that is run, the __init__ inside class Child gets run.
  • the first line in the class Child is the print statement --> that's why it prints Child Constructor has been called.
  • after that, in the class Child, the constructor for the class Parent gets called.
  • the first item in the class Parent is the print statement... which is why it then prints Parent Constructor has been called to the console.
  • then the two instance variables last_name and eye_color get initialized into memory.
  • since there is nothing left to do in the Parent class, it moves back to the next line in the Child class - which is the instance variable for Child which has yet to be initialized
  • the instance variable for the Child class -- total_toys -- gets initialized into memory.
  • then there is nothing left to do, it will then move on to the next bit of code.

print(miley_cyrus.last_name)

  • this gets executed, and outputs the last_name

print(miley_cyrus.total_toys)

  • this gets executed, and outputs the total_toys

Inheritance: Reusing Methods

Let's begin with making some changes to our inheritance.py file... and add in a new method called show_info...

class Parent():
def __init__(self, last_name, eye_color):
print("Parent Constructor has been called.")
self.last_name = last_name
self.eye_color = eye_color

def show_info(self): #the first argument of this method is self... and all this method does, is prints out the last_name and eye_color of the parent
print("Last Name - "+self.last_name)
print("Eye Color - "+self.eye_color)

class Child(Parent):
def __init__(self, last_name, eye_color, total_toys):
print("Child Constructor has been called.")
Parent.__init__(self, last_name, eye_color)
self.total_toys = total_toys

billy_cyrus = Parent("Cyrus","blue")
#miley_cyrus = Child("Cyrus","Blue",5) billy_cyrus.show_info() #This calls the show_info method using the Parents' instance billy_cyrus...

So when you run it, it will print out the last name and eye color of the billy_cyrus parent object/instance...

And now for the cool part. The Child instance can actually also call the show_info method from the Parent, even though we never defined it in the Child class. (See the example code change below:)

class Parent():
def __init__(self, last_name, eye_color):
print("Parent Constructor has been called.")
self.last_name = last_name
self.eye_color = eye_color

def show_info(self):
print("Last Name - "+self.last_name)
print("Eye Color - "+self.eye_color)

class Child(Parent):
def __init__(self, last_name, eye_color, total_toys):
print("Child Constructor has been called.")
Parent.__init__(self, last_name, eye_color)
self.total_toys = total_toys

billy_cyrus = Parent("Cyrus","blue")
#billy_cyrus.show_info()

miley_cyrus = Child("Cyrus","Blue",5)
miley_cyrus.show_info()

The output of this code would read as follows:

  • >>>
  • Parent Constructor has been called.
  • Child Constructor has been called.
  • Parent Constructor has been called.
  • Last Name - Cyrus
  • Eye Color - Blue

That's all fine and dandy, but what happens if we also define a method called show_info inside the Child class?

class Child(Parent):
def __init__(self, last_name, eye_color, total_toys):
print("Child Constructor has been called.")
Parent.__init__(self, last_name, eye_color)
self.total_toys = total_toys

def show_info(self):
print("Last Name - "+self.last_name)
print("Eye Color - "+self.eye_color)
print("Number of Toys - "+str (self.total_toys)) #Note, to get this to work, we had to wrap the number inside a string function... hence the +str

Okay, what happens when you run the changed code? It output the show_info method defined in the Child class instead.

So even though the parent had a method inside by the same name, we overrode that by defining a method by the same name in the Child class. This overriding of behavior is called "Method Overriding".

Abstract Thinking: About Inheritance

So we learned a bit on inheritance and reusing methods. We can tie the concept of inheritance to something else that we had previously learned in this course: CSS Inheritance.

In an abstract way, they relate to Python Classes because the Parent (or top-level) is the building block, and each child (lower-level) inherits the parents attributes, but can also have their own... but here's how CSS is similar:

• Example 1:

Let's try to explain this with some examples. Let's say in your CSS document, you created a class called big_pink_text. When you apply that class to a <div> </div> tag, it will make everything inside it big and pink.

CSS:

.big_pink_text{
color: #FF66CC;
font-size: 18px;
}

So when you create a <div> and assign it the class big_pink_text, everything inside would look like this: some text...

You can think of that CSS Class as a Parent in a way. And everything inside that <div> is the child.

When you use the <em></em> and <b></b> tags inside that parent, they inherit the style (big, pink text) from the parent.

So this:

<div class="big_pink_text">
The <em>emphasis</em> and <b>bold</b> tags inherit the behavior of the parent class big_pink_text.
</div>

Would look like this:

The emphasis and bold tags inherit the behavior of the parent class big_pink_text.

• Example 2

One more example using <div> tags and inheritance:

So we created the class called big_pink_text, and we saw what that does. Now lets create another class called ugly_font that when used, would apply an unpleasant font.

When we put a <div> inside another <div> and apply different classes to those, you can see how inheritance works again...

First, here's the CSS for each class:

.big_pink_text{
color: #FF66CC;
font-size: 18px;
}

.ugly_font{
font-family: "Comic Sans MS", cursive, sans-serif;
font-style: italic;
}

And now for what the HTML would look like:

<div class="big_pink_text">
Here is some text in the 'parent' class of Big Pink Text...
<div class="ugly_font">
Here is some text using another class called Ugly Font. Because this is being used underneath (or inside) of the Parent class of Big Pink Text, notice how it inherits the color, and the size, but as a child it also has it's own attributes - which happen to be the ugly font that we defined.
</div>
And lastly, here is some more text, but it is outside of the 'child' class of Ugly Font, and therefore doesn't inherit it's additional attributes of the ugly font.
</div>

And this is how it would look in the webpage itself:

Here is some text in the 'parent' class of Big Pink Text... notice how it is taking on the attributes of the parent class? But it's not taking on any other attributes from other classes...

Here is some text using another class called Ugly Font. Because this is being used underneath (or inside) of the Parent class of Big Pink Text, notice how it inherits the color, and the size, but as a child it also has it's own attributes - which happen to be the ugly font that we defined.

And lastly, here is some more text, but it is outside of the 'child' class of Ugly Font, and therefore doesn't inherit it's additional attributes of the ugly font.

Hopefully that explains a little better how inheritance works, and how it relates to Python. In the previous section about Class Inheritance, think of it in terms like this: Billy Cyrus is the big pink text, and Miley Cyrus is the ugly font.

And now for Method Overriding:

While we generally want a child object to inherit it's parents behavior and attributes, sometimes we want our child to behave a little different from the parent. We can do this with a Method Override.

Going with the previous examples, in CSS you can override styling, much in the same way as you can override behaviors in Python. Now, in Python you do so by specifying this behavior using the same name... and in CSS it's not any different.

Remember how we specified two different classes in CSS, big_pink_text, and ugly_font? Well what if we wanted ugly_font to be smaller than the parents behavior?

In the previous example, the parent font was 18px. But we want the ugly_font to be way smaller, so we have to override what it is inheriting from the parent.

How do we do this? Here's how:

.big_pink_text{
color: #FF66CC;
font-size: 18px;
}

.ugly_font{
font-family: "Comic Sans MS", cursive, sans-serif;
font-style: italic;
font-size: 12px; <-- See this right here? That's the override.
}

Okay, but how does that look in the webpage? The HTML will still be exactly the same, the class used will still be the same... we just added one more attribute to the class ugly_font. Well, here's how it appears after the override:

Here is some text in the 'parent' class of Big Pink Text... notice how it is taking on the attributes of the parent class? But it's not taking on any other attributes from other classes...

Here is some text using another class called Ugly Font. Although this is being used underneath (or inside) of the Parent class of Big Pink Text, the parent class used font-size 18, but our child class used font-size 12, so because the attribute has the same name, it overrides the inheritance from the parent.

And lastly, here is some more text, but it is outside of the 'child' class of Ugly Font, and therefore doesn't inherit it's additional attributes of the ugly font?

Going back to the previous Billy Cyrus / Miley Cyrus example - there we added the show_info method into the Child class, which overrode the behavior that it inherited from the Parent class.

V

Vocabulary

Class - A blueprint or template which have information/instructions defined inside of them.

Think of a class like a recipe. It tells you how to cook something and what ingredients to use.

Instance - An instance is a portion of a class, that has its own specific behavior, but still follows the main guidelines of the Class.

Going back to the concept of the Class being the recipe, but when you create an Instance (Object) of that class - say "Pork Chops" - you use the recipe to instruct you how to make the meal, but you can control how many pork chops to make, how long to cook them, any additional ingredients you want to add or leave out, etc.

Constructor - When you create an instance of a class, the constructor is called - __init__ - it initializes all of the data associated with the instance, and loads it into memory for future use.

Self - The constructor uses the keyword Self, it's itself... so when the instance Toy_Story is created, the Self is Toy_Story.

Instance Variable - All of the variables associated with the specific instance. They are unique to an object, and can be called using Self keyword inside the class, and the instance name outside the class.

Instance Method - A function that is defined within a class, but is associated with an instance(object) and have the first argument as self.

C

Concepts

What is an "Object" in Object Oriented Programming?

An object can be either something that contains data (such as Lists or Arrays), or something that contains procedures (Methods, Functions, etc).

You generally give an object a meaningful name, for example "small_box" or "movie_name", usually something that describes it if you can.

In an abstract way of thinking, an object that is something or does something, and you make reference to the name you give that 'thing', to use in your program - but I'll try to explain that next:

• An Object as Data:

If an object is data, you can call the object "show_aired_date", which in that context would mean that object contains data about when a show aired on TV.

We don't actually have to know all of the data associated with that object, we just know what it does, it provides us with data when a show aired on TV, not every single date for every single show.

• An Object as a Procedure:

If an object is a Procedure (method), say "draw_triangle", you can call it "green_triangle_1" if you plan to use that method over and over to draw different types of triangles.

Now, "draw_triangle" itself is an object. It's a method that has code to tell Python how to draw a triangle. But we don't need to know the specifics, we just know that when we use "draw_triangle" - it does just that.

Also, "green_triangle_1" is kind of an object of an object if you think about it. It still will draw a triangle when we call it, but it'll be a green triangle because we'd specify additional attributes to turn the triangle green, and maybe it could be a small triangle if you wanted, etc.

• Benefits:

A big benefit of using objects is that a lot of methods have already been written as part of the Python Standard Library, so you can save yourself a lot of time and effort by reusing code that has already been written.

Also, with a bit of research and reading, you can probably find External Libraries that might do things that you need, again without having to code them, you just import the library and create an object to call the methods you want to use from the libraries that you imported.

How Does Object Oriented Programming Help With Large Projects?

In traditional programming, a program would be a series of logic written to execute line by line, and complex behavior was difficult to account for. The problem with this is it's not ideal for re-usability of code, repetitive tasks, debugging, and so on... because you are writing code that does something, based on conditions and logic.

With Object-Oriented Programming however, you can handle a lot of the drawbacks that you would encounter with traditional programming.

Re-usability:

Python has a large set of libraries, and classes within that do specific things. A programmer only needs to know a little bit about what those things do, and it saves them from having to write complex functions by hand to do things. These libraries, once imported, can be used multiple times in a program in multiple places - all without having to re-write code.

Looking back at the draw_turtle lesson, we created a function that drew a square, and were able to reuse that code by giving names to different objects/squares we would want to draw, and reused draw_square for each instance that we wanted to make.

Repetitive tasks:

The old way to program was inefficient because to do something more than once, you had to write out the code you had already written to run it again (like in our original "Draw a Square" example). With Python, repetitive tasks are made simple. You can make a function and include a loop, or you can call already-written code and specify a loop there.

Expandability:

When you write a function (or plan to use a class within a library), it has its own pre-defined behavior that you can modify. The benefit to this is you can spawn multiple instances (or Objects) of this behavior to use over and over, in the same program or in multiple programs.

In regards to classes, you can create an instance of that class, give it a name, and specify its own behavior... and then you can create another instance with a new name that does almost the same thing, but also can have its own unique attributes as well.

Organization:

Object Oriented Programming allows you to organize the behavior and functionality, as well as your data with meaningful names, which can help you set up and use your data in a way that makes sense, as well as being able to structure your programs behavior by assigning names to specific functionality – and calling in those behaviors by name.

In a past example, we structured our data for the Movie website by using a list (we could have also used an array), using this example: valid_ratings = ["G","PG","PG-13","R"]. Also going back to the draw_art example, I had broken down functionality into separate operations such as draw_square, draw_triangle, and draw_circle, and made them all work together using draw_art.

So a number of complex operations are made simple by defining some functionality, and then giving them names that make sense.

Top