Python Programming Guide Book
Python Programming Guide Book
How to Program
Computer Science Concepts
and Python Exercises
Course Guidebook
Dr. Keysers interests in physics, math, and computing led him to a career
in computer graphics, in which he has been able to combine all three
disciplines. He has published several articles in geometric modeling,
particularly looking at ways of quantifying and eliminating uncertainty in
geometric calculations. He has been a long-standing member of the solid
and physical modeling community, including previously serving on the
Solid Modeling Association executive committee. He has also published
several articles in physically based simulation for graphics, including
developing ways to simulate waves, fire, and large groups of rigid
objects. As a member of the Brain Networks Laboratory collaboration at
Texas A&M, he has worked on developing a new technique for rapidly
scanning vast amounts of biological data, reconstructing the geometric
structures in that data, and helping visualize the results in effective ways.
In addition, he has published papers on a variety of other graphics
topics, including rendering and modeling.
|
ii P ro f e s s o r B io gr a p hy
Since writing his first computer program more than 35 years ago, Dr.
Keyser has loved computer programming. He has particularly enjoyed
programming competitions, both as a student competitor and now as a
team coach. Of the many computer science classes he took, the most
important class turned out to be the one in which he met his wife. In his
free time, he enjoys traveling with her and their two daughters.
iii
Table of Contents
[ Introduction ]
Professor Biography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
Course Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Installing Python and PyCharm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
[ Lecture Guides ]
Lecture 1
What Is Programming? Why Python? . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Lecture 2
Variables: Operations and Input/Output . . . . . . . . . . . . . . . . . . . . . . . 16
Lecture 3
Conditionals and Boolean Expressions . . . . . . . . . . . . . . . . . . . . . . . 26
Lecture 4
Basic Program Development and Testing . . . . . . . . . . . . . . . . . . . . . 39
Lecture 5
Loops and Iterations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Lecture 6
Files and Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Lecture 7
Operations with Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
|
iv Tab l e o f C o n t en ts
Lecture 8
Top-Down Design of a Data Analysis Program . . . . . . . . . . . . . . . . . 79
Lecture 9
Functions and Abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Lecture 10
Parameter Passing, Scope, and Mutable Data . . . . . . . . . . . . . . . . 103
Lecture 11
Error Types, Systematic Debugging, Exceptions . . . . . . . . . . . . . . . 113
Lecture 12
Python Standard Library, Modules, Packages . . . . . . . . . . . . . . . . . . 124
Lecture 13
Game Design with Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Lecture 14
Bottom-Up Design, Turtle Graphics, Robotics . . . . . . . . . . . . . . . . . 146
Lecture 15
Event-Driven Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Lecture 16
Visualizing Data and Creating Simulations . . . . . . . . . . . . . . . . . . . . 170
Lecture 17
Classes and Object-Oriented Programming . . . . . . . . . . . . . . . . . . 182
Lecture 18
Objects with Inheritance and Polymorphism . . . . . . . . . . . . . . . . . . 193
Lecture 19
Data Structures: Stack, Queue, Dictionary, Set . . . . . . . . . . . . . . . . 205
| v
Lecture 20
Algorithms: Searching and Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Lecture 21
Recursion and Running Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Lecture 22
Graphs and Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
Lecture 23
Graph Search and a Word Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Lecture 24
Parallel Computing Is Here . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
[ Supplemental Material ]
Answers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Python Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
Python Modules and Packages Used . . . . . . . . . . . . . . . . . . . . . . . . . . 312
Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
vi |
1
How to Program
Computer Science Concepts
and Python Exercises
Using the language Python, you will learn programming, from the most
basic commands to the techniques used to develop larger pieces
of software. Starting with the first lecture, you will learn about how
computers operate and how to write programs to instruct them.
Throughout the first half of the course, the course covers all of the most
common programming operations.
With conditionals and Boolean expressions, you will learn how to make
the computer respond differently to different situations.
Loops will teach you how to get the computer to repeat the same task for
you again and again.
One of the common things you will want to do is process information that
might be stored somewhere else, so you next learn about how to work
with files.
|
2 C o u rs e Sco p e
The data you read in from files is often organized in long lists, so the
course discusses how to handle lists in Python. This is an area where
Python particularly excels, and you will be introduced to some of the
features that Python includes for handling lists.
The course will introduce one of the most powerful ideas in all of
computer scienceabstractionand show how functions help you put
abstraction into action. Functions let you separate different concepts into
different parts of a computer program, and the way these different parts
communicate is through parameter passing, so you will learn about this
process in detail.
One of the particular benefits of using Python is the ease with which we
can write powerful Python programs by making use of large collections
of code that other people have written. The way to do this is through
Python modules, and you will learn about modules as you reach the
halfway point in the course. You will learn how to write powerful Python
programs, sometimes with just a line or two of code, by calling functions
from these modules.
Throughout the course, you will learn how to put basic programming
tools together to form more complete programs. In Lecture 4, you will
learn how testing and iterative development help create and improve
programs. In Lecture 8, you will learn about the idea of top-down design
by building a basic data analysis programfor weather, in this case.
Lecture 11 focuses on the debugging process and how to identify and
deal with the various errors that people encounter when programming.
Lectures 13 and 14 show you how abstraction is important when
developing these larger programs. First, top-down design is used,
but this time with functions, to show how to create a game. Then, the
concept of bottom-up design is introduced, and you learn how it can be
used in graphics and robotics applications.
| 3
In the second half of the course, you will discover some more advanced
development skills. You will learn about event-driven programming
and how to can create graphical user interfaces. In addition, you will
learn how to use loops and modules to generate random numbers
and use plots and graphs to create simulations, such as a retirement
portfolio. Next, you will learn about the core ideas of object-oriented
programming, including encapsulation, inheritance, and polymorphism.
You will learn how to use object-oriented design to group your data
together and construct larger programs.
The last portion of the course turns to some slightly more advanced
topics. You will learn about how to organize data through data structures
and then how algorithms allow us to describe fundamental operations
on data. After learning about recursive algorithms, you will look in more
detail about a particularly useful data structure, graphs, and some of the
algorithms that can run on it.
T o write your own programs in Python, there are two main pieces
of software that you will need to know how to set up: Python itself,
and an environment for using Python called PyCharm.
First, you need to install Python on your computer. This means that you
will download a whole set of files that will let you write and run Python
programs on your computer. Once you have installed these files, you
will be able to execute .py files, run an interactive window, and execute
basically any Python command.
Python
Youll probably see two different options: one for Python version 2 and
one for Python version 3. Version 3 doesnt work quite the same way
as version 2. Many people who were already invested in version 2
because they had large amounts of software written in Python 2 or
were already familiar and comfortable with Python 2chose to keep
maintaining and developing software in version 2. As a result, two types
of Python continued to be used: the version 2 branch and the version
3 branch. The similarities between these are much greater than the
differences, but there are some differences. Python 3 is the more up-
to-date version, and for people who arent tied to any old code from
Python 2, Python 3 is better.
Follow the link to the Python 3 page. You dont want to get anything
called a development versionthats a version still being developed,
and not fully tested. Look for whatever stable version is right for your
operating system. If youre on a mac, youll probably want the Mac OS X
version. If youre on Windows, get the newest Windows version that your
operating system can handle. (For example, if you are running Windows
XP or older, you might need to download an older version of Python 3.)
Theres also a Linux version. Whichever system youre on, download the
newest stable release that your operating system can handle. For the
most part, this should involve clicking a single link.
The first thing you can do is bring up the interactive shell, called IDLE.
After your download, you should be able to find a program named IDLE
somewhere on your machine. If its not on the desktop or a start menu,
you might need to do a search.
Once youve found IDLE, run it. You should see a window pop up with
a name like Python 3.__ shell. There will be a prompt consisting of
several greater-than signs. Basically, any command you type into this
window will be interpreted as a Python command and executed.
IDLE is useful, especially for trying out something small. But for most
of the development in this course, it is recommended that you get a
full IDE. There are many Python IDEs to choose from, but PyCharm is
a great IDE. Its simple enough that most people can easily use it for
basic Python programming, and its powerful enough that high-end
programmers will still use it. The basic edition is free to download,
and it has all the tools you could want for this course, such as syntax
highlighting, error checking, auto-completion, and a full debugger.
PyCharm
First, go to the File menu in PyCharm. Click on the link New Project.
When you do that, it should prompt you for the name and location for
the new project, along with the interpreter to use. The interpreter should
default to the Python version that you just installed. For the project title,
it will probably default to one called untitled. Pick a new name for
the project, and feel free to pick a different directory. Finally, click the
Create link. You will probably need to choose whether the new project
is in a new window or not. If you pick a new window, you can start off a
project in a clean window.
In the main PyCharm window, you should see a mostly blank screen.
It might have a single line of Python code. You can ignore that line of
code; in fact, you can delete it entirely if you want, or just leave it in. It
wont make any difference to your code. That window, though, is where
you will type in your program.
Again, lets start with a Hello, World program. In that window, type in
the following: print (Hello, World). As youre typing, you might notice
that PyCharm will start filling in things for youfor example, when you
open the parentheses, it will automatically generate a close parenthesis,
and same with the quotation mark.
Notice that unlike the IDLE window, when you hit enter after this line
of code, you dont see the results of this code. To see the results, you
have to explicitly tell the computer to run the program. In the PyCharm
window, go up to the menu item where it says Run, and then select
Run from that menu. You will have to pick the name of the program
you just wrote. When you do, a new window will appear at the bottom of
|
8 I n stal li n g Py t ho n a nd PyCh a r m
the PyCharm environment. This is the window showing the output. You
should see the words Hello, World output there, along with something
saying Process finished with exit code 0. That last line just means that
the program completed without an error.
You might have noticed that there was a green arrow in front of Run.
After running for the first time, you can click the green arrow at the upper-
right corner of the PyCharm window or the green arrow down near the
output window to run your code again. The new output will replace the
old output, so you wont notice anything new if you rerun the same code.
But you can also make a modification to your codemaybe add another
print statement or change what this print statement says. Then, hit the
green arrow to see the results down in the output window.
Youve now created your own Python program and run it in the PyCharm
IDE. For most of this course, it is recommended that you do all of your
development in the PyCharm IDE. You can write your code, run it to see
how it works, go back and modify your code, and run it again. It makes it
very easy to make modifications and test them. Even if it feels awkward
at the moment, as you create more and more programs, it will become
very natural to create new projects and new Python files, and run them.
Lecture 1 01
What Is Programming? Why Python?
T he three main goals of this course are to teach you the basic tools
of programming, how those basic tools can be used to assemble
larger pieces of software, and how data structures and algorithms can
help us write programs that deal with deeper and more challenging
problems in computer science. In this lecture, you will learn about
programmingwhich is a form of communication, from the programmer
to both the computer and other people. Just as language helps us
organize and describe ideas for people, programming languages help
us organize and describe ideas for the computer.
[ Programming Languages ]
When we think of everything that computers can do, its easy to think of
them as incredibly smart and powerful machines that humans cant hope
to comprehend. At its heart, a computer is a machine that can do just a
few basic things extremely well.
For programmers, the code we write is a way of taking our ideas and
expressing them in a logical, ordered way. The programming languages
we use help us do thatto structure our ideas and instructions for the
computer in ways that we can understand. Then, there is a separate
program, called a compiler or interpreter, that will convert the program
into a series of ones and zeros that the computer will understand.
For this course, we will use a general-purpose language thats easy for
you to put on your computer, relatively easy to learn, and useful enough
that people use it regularly on real-world projects: Python. In addition to
these characteristics, it also doesnt require you to use any one particular
programming styleits a good language for several different styles.
[ Python Basics ]
The following code is an example of a Python program.
This is an instruction that we are giving the computer. This line of code
prints the words Hello, World to the screen. (The word print has
nothing to do with putting ink on paper.) For many people, a Hello,
World program is the first one they write.
OUTPUT:
Hello, World
Whats going on for the computer to be able to run this program? First, we
have our computer program that weve written. In this case, the program
is just a single line of code.
We could enter this program in a few basic ways. We could type it into an
interactive window, known as a shell window, which will give us results as
we type our code; or we could enter it within whats called a development
environment, in which we type several lines of codein other words, we
develop the programbefore we let the program work.
With either of these methods, we can save the program as a .py file,
indicating that its a Python program. We could even just create a .py file
on our own, in any text editor.
|
1 2 L e ct u r e 1W hat I s P r ogr a mming? W h y Py th on?
Regardless of how we type in and run the program, a computer will need
that code converted into a set of machine instructionsa bunch of ones
and zeros that the computer can understand. Then, its going to run, or
execute, those machine instructions. When the computer follows those
machine instructions, it does exactly what they tell it to. In this case, it
prints out the letters Hello, World to the screen.
One way will encounter each line of the program and immediately
convert it to machine instructions that are run. This is called interpreting.
Another method will take the whole program at once and try to figure out
the best machine instructions to do what that program meant to do. This
is called compiling.
#Greeting
print ("Howdy!")
#Invitation
print ("Shall we play a game?")
Now we have two lines of instructions for the computer, plus a few one-
word comments that are meant for humans. The comments are the lines
that begin with the pound sign, which people also call a hashtag, number
sign, or hatch mark. When an interpreter sees the pound sign, it ignores
everything else on that line.
Comments are text thats meant for people reading the code. Theyre
ignored by the computer, so their only real purpose is to tell someone
reading the code how to understand it. Comments can be critical for
helping someone understand the purpose of code and the way its
working. In this case, our comments give the purpose for each of the
commands that followa greeting followed by an invitation.
#Greeting
print ("Howdy!")
#Invitation
print ("Shall we play a game?")
|
1 4 L e ct u r e 1W hat I s P r ogr a mming? W h y Py th on?
OUTPUT:
Howdy!
Shall we play a game?
The first line of code prints our greeting: Howdy! The interpreter
will convert that line to machine instructions that print Howdy! to the
screen. The next line of code prints out a question: Shall we play a
game? Again, the interpreter will convert that to instructions that get the
computer to print out that text to the screen.
In addition, notice that we skipped over the comments and the blank
lines. Just like comments, blank lines are things we put in there to help
people understand the code. Blank lines help separate different parts
of the code visually so that its clear which things belong together.
Throughout your code, its a good idea to use comments and blank
spaces to help communicate what youre trying to do.
Because comments are for people, they can be free-form, but for
everything else, theres syntax, which is the particular way that you need
to write and structure your code in a languagePython, in our caseso
that the computer understands. Syntax is especially important because
computers are only going to understand exactly what theyre told, so we
need be very precise in how we talk to them.
Reading
Exercises
What would be the output from each of the following lines of code?
1 print(1+2+3+4+5)
3 print(3*(5+2))
4 #print(100)
5 print(1/2 + 1/2)
7 print (3985780149 % 2)
What would be the code you would write for each of the following?
02 Lecture 2
Variables: Operations and Input/Output
Some memory, called the registers, is built right into the CPU, and some
other memory, the cache, is in the same integrated circuit as the CPU.
This is all really short-term memory, and most programmers dont need
to worry about it.
The next level of memory is the main one programmers care about
called main memory, or sometimes primary memory. This is the main,
short-term memory of the computer. Its the working memory where we
keep all the things that are running, from the operating system that were
working inside of to the programs that were currently executing. This
memory is still close to the CPU.
can store massive amounts of datamuch more than you could have on
your computer. The idea of cloud storage is that the computer gets to
treat this tertiary memory more like secondary memory.
[ Variables ]
A good way of thinking about temporary, working memory is as a set of
boxes. Each box can contain some piece of information. If we want to use
these boxes, we need to be able to refer to them, so every box has a
name. Its these boxesthese regions of memory that have a name and
can hold a piece of informationthat we refer to as variables.
Each of these variables, the boxes with a unique name, can be used to
store information. To do this, well have to do a variable assignment. If we
have a variable x and we want that variable to hold the number 3, we can
write this with a line of code.
x = 3
The next character is an equal sign, but this equal sign does not mean
equals in the way youre used to thinking of it. Its actually what we call
an assignment operatorthat is, its indicating that were assigning a
value to a variable. The assignment operator always takes the thing on
the right side and assigns it to the box on the left side.
| 19
In this case, that thing on the right side is a number, the value 3. The
overall result of this line of code is that we take a box of memory, with the
name x, and put the value 3 into that box. This does not mean that x is
now forever equal to that thing on the right side, the 3.
x = 3
x = 3.14
x = "Make pizza"
Remember, though, that you need quotes to have a string. For example,
pizza in quotes is a string, but pizza without quotes could be a variable
name. If youre trying to refer to the actual letters of the text, you have to
enclose the text in quotes.
Every variable has a type, which is the way that the piece of information
inside that box should be understood. Integers, floating-point numbers,
and strings are all types. In some languages, you need to be very
particular about specifying the type of a variable. Python will figure out
what the right type is based on what is assigned, but you have to be
careful that you understand the type so that you dont make a mistake.
|
2 0 L e ct u r e 2 Va r ia b le s : Oper ations a nd Inpu t/ Ou tp u t
a = 2+5
b = a-4
c = a*b
MEMORY:
a: 7
b: 3
c: 21
We can assign the variable a the value 2+5, which the processor
evaluates, adding 2 and 5 together to get 7. So, when we assign a value
to variable b, we can use the value thats in a. If we assign a-4 to b,
then, because a has the value 7, a minus 4 is just 3, and this is the value
stored in b. Then, when variable c gets assigned the product of a and
b, the values sitting in boxes a and b are 7 and 3, so their multiplication
evaluates to 21.
a = 2+5
b = a-4
c = a*b
a = 42
MEMORY:
a: 42
b: 3
c: 21
| 21
Notice that if we change the value of one variable, it does not change
the value of any other variables, even if those variables were originally
defined from it. If we assign the value 42 to a, then the value stored in as
box in memory is changed, but the values stored in the boxes for b and
c are not.
Addition is also defined for strings. When we add two strings together,
the result is a new string with one added onto the end of the other. This
process, called concatenation, is represented by the addition sign.
However, there are no equivalent operations defined for subtraction,
multiplication, or division.
[ Input/Output Commands ]
The third critical aspect of a computer is the input and output, or I/O.
Computers can take input from a variety of sources and send output
several places.
Lets assume that our input and output uses a simple text window on
the screen. Any input will be from a person typing something into that
window via the keyboard, and any output will be text written out to
that window.
WINDOW:
Enter a value:
If we want to start a new line, we can print out a string with the code \n
inside. The \n is called the newline character and is interpreted as end
this line and go to the next one.
To get input from the user, we have a Python command named input.
This can be used to get information that a user types in. Notice that there
are parentheses after the word inputthis is going to be common
for most commands. The input command is put as the right side of an
assignment statement. It gets whatever value a user types in so that it
can be assigned to whatever variable is on the left side.
To display some text on screen to prompt the user for input, the input
command also lets us include a quoted string in the parentheses. This
will be printed out right before the user types in input.
We dont have to give the user a prompt to get input from the user. If
our program has an input command that doesnt include a string in the
parameters, the program just waits for the user to type something.
Input and output where we wont have any user prompt is common when
we turn to files for input and output. With files, we use commands called
open, read, and write, and we dont give files any prompts before
reading from them.
The idea of type becomes especially important with input. In the following
lines of code, the first two lines assign variables a and b by asking a user
to input a value for each one. Lets say that the user inputs 1 and 2. The
final line is an output statement. You might be surprised to learn that the
output is not the number 3, but rather 12.
| 23
MEMORY:
a: 1
b: 2
WINDOW:
Enter value one:1
Enter value two:2
The sum is 12
Why did this happen? When we read data with the input command, the
data we read always comes in as a stringeven if the data is numerical.
a = "1"
b = "3.14159"
c = int(a)
d = float(b)
|
2 4 L e ct u r e 2 Va r ia b le s : Oper ations a nd Inpu t/ Ou tp u t
Reading
Exercises
For each of the following short programs, what would be the output of the
segment of code?
1 a = 10
b = 15
a += b
print(a)
2 a = 10
b = 15
a = b
b = 1
print(a)
3 a = 10
b = 15
a = a*a+b
print(a)
4 a = 10
b = 15
a *= a+b
print(a)
5 a = 10
b = float(a)
print(b)
| 25
6 a = "10"
b = int(a)
print(b)
7 a = "Welcome"
b = "Home"
print(a,b)
8 a="Welcome"
b="Home"
print(a+b)
9 a = "10"
b = "15"
c = a+b
d = int(c)
print(d)
11 Given a price for a loaf of bread, bread_price, and a price for a block of
cheese, cheese_price, calculate the cost to buy 2 loaves of bread and 3
blocks of cheese.
13 Write a program to form the name of a knight by asking the user for the
knights name and a personality characteristic. The final name should be
printed as Sir <name> the <characteristic>. For example, if the user enters
Robin and Brave, you would print Sir Robin the Brave.
26
03 Lecture 3
Conditionals and Boolean Expressions
Lets start with a simple example that is easily expressed with conditionals.
Suppose that a computer-controlled thermostat needs to decide whether
or not to turn on the heat. It will have some criteria, usually whether the
temperature is above or below some minimum. If its below, itll turn on
the heater, and if not, it wont.
Lets start with this short piece of code using a conditional statement: the
if statement.
if True:
print("Turning on the heater.")
We start the statement with the word if, and this tells us that well be
having a condition. Right after the if, we have the condition itself. The
condition is something thats either true or false. For now, we are going
to use the words True and False for our conditions. But the condition
is usually written as a more complex expression that evaluates to be true
or false.
In this example, well have a condition thats just plain true, so we just use
the word True. Note that the T is capitalized. If it were lowercased, it
would just be a variable with the name true. In Python, its common for
a constant value to be capitalized. Because True is a constant value
meaning true, of courseits capitalized.
are writing your own code, you should be consistent about always using
four spaces.
Indents are part of Pythons syntax, a visual way for humans to see the
structure of the program in a way that is also, simultaneously, how the
computer reads the code; that is, when you indent a line of code in
Python, you are telling the computer where a new block of code begins.
OUTPUT:
Turning on the heater.
But what if the condition is false? In this case, we wont execute the code
thats indented. So, there is no output.
if False:
print("Turning on the heater.")
OUTPUT:
Lets say that we want to print more than one line if the condition is true.
We can indent a second line of codein this case, one that will output It
was too cold. When we execute this code, we have a true condition, so
we execute both lines of code, and we get two lines of output: Turning
on the heater and It was too cold.
if True:
print("Turning on the heater.")
print("It was too cold.")
| 29
OUTPUT:
Turning on the heater.
It was too cold.
if False:
print("Turning on the heater.")
print("It was too cold.")
OUTPUT:
Lets say that we didnt have the second print statement indentedthat
theres no tab, and it is just lined up with the if. In this case, where the
condition is true, we still get both lines output.
if True:
print("Turning on the heater.")
print("It was too cold.")
OUTPUT:
Turning on the heater.
It was too cold.
But if the condition were false, the print statement It was too cold is not
indented, so its not part of the if statement.
if False:
print("Turning on the heater.")
print("It was too cold.")
The computer comes along, sees the if statement, checks the condition
which is falseand skips the indented lines. Then, it goes on to the next
line of codewhich is not indentedand executes it. So, the statement
printing It was too cold is executed.
|
30 L e ct u r e 3 C o n d it io na l s a nd Bool ea n Ex pr essions
if False:
print("Turning on the heater.")
print("It was too cold.")
OUTPUT:
It was too cold.
For this, we want an if-then-else statement, which has two sets of code
that can be executed: one set if the condition is true and another set if
its false.
if True:
print("Turning on the heater.")
else:
print("Temperature is fine.")
OUTPUT:
Turning on the heater.
The syntax looks just like the if-then statement, but now we have another
line, else, right afterward. Notice the colon, too. We again have indented
code right after that, saying what to do if the condition is false.
If the condition is true, only the first set of indented code is followed. So,
in this case, we have a true condition, so we see the output Turning on
the heater.
| 31
On the other hand, if the condition is false, we would follow the second
set of indented codethe part after the else. So, in this case, we see
the output Temperature is fine.
if False:
print("Turning on the heater.")
else:
print("Temperature is fine.")
OUTPUT:
Temperature is fine.
[ Nesting ]
When we have indented code, it is just like the other code we have. The
computer executes the commands that are indented just like it executes
the first commands. That means that within the indented code, we can have
another if-then-else statement. We call this nesting because one statement
is entirely within the bounds of the other. You can think of the first if statement
as being the nest and the other if statement as being inside the nest.
if True:
print("It is cold.")
if True:
print("The heater is already on.")
else:
print("Turning the Heater on.")
else:
print("It is warm enough.")
OUTPUT:
It is cold.
The heater is already on.
Lets assume that the first condition was false. In this case, wed skip the
entire indented first section, including the nested if statement. We would
go straight to the else portion of the outer statement. It wouldnt matter
whether the inner if statements condition was true or false, because that
line of code is never reached.
if False:
print("It is cold.")
if True:
print("The heater is already on.")
else:
print("Turning the Heater on.")
else:
print("It is warm enough.")
OUTPUT:
It is warm enough.
Up until now, weve written our conditions as either True or False. But
that assumes what we want the computer to find. If we know at the time
were writing the code whether its true or false, we dont even need
a condition.
| 33
So, instead of writing True or False for the condition, we need a way
for our condition to be something that can have the value either True or
False. This value that can be either true or false is called a Boolean; a
Boolean variable is a type of variable that can be either true or false.
temp_is_low = True
if temp_is_low:
print("Turning on the heater.")
else:
print("Temperature is fine.")
OUTPUT:
Turning on the heater.
On the other hand, if the value is false for temp_is_low, then our output
would be Temperature is fine.
temp_is_low = False
if temp_is_low:
print("Turning on the heater.")
else:
print("Temperature is fine.")
OUTPUT:
Temperature is fine.
Any false conditional, however nonsensical, will have the same effect:
Output would be Temperature is fine.
|
34 L e ct u r e 3 C o n d it io na l s a nd Bool ea n Ex pr essions
[ Comparisons ]
Conditionals are really only valuable when we dont know ahead of time
whether the condition will be true or false. This means that we need to be
able to take an expression and determine whether that expression is true
or false. The most common way we do this is with comparisons, which let
us compare two values. We can choose how we want to compare them.
Lets define three variablesa, b, and cand assign a the value 1 and
both b and c the value 2. Then, lets do some comparisons.
a = 1
b = 2
c = 2
a > b #False
a < b #True
a >= b #False
a <= b #True
If we check whether a is greater than b, well find that this is false because
1 is not greater than 2, while if we compare whether a is less than b, its
true. The same thing happens if we use greater than or equal to and less
than or equal to as our comparisons: 1 is not greater than or equal to 2,
but 1 is less than or equal to 2. Notice the syntax: The greater-than sign
always comes first, and then the equal sign. The less-than sign comes
first, and then the equal sign.
What happens if we compare b and c? They are equal; they both have
the value 2. So, both b > c and b < c are false expressions. But when
we also check for b >= c or b <= c, theyre true.
| 35
a = 1
b = 2
c = 2
b > c #False
b < c #False
b >= c #True
b <= c #True
Lets look at two more comparisons: equal and not equal. Each of these
has a special notation you need to use thats probably different than
nonprogrammers would expect.
To compare for equality, we use a double equal sign. If you use only a
single equal sign, that is assignment. If you want to compare whether or
not a and b are equal, thats just an expression that can be true or false,
but assigning b to a is a command.
a = 1
b = 2
c = 2
a == b #False
a != b #True
b == c #True
b != c #False
a = b #This assigns b to a
|
36 L e ct u r e 3 C o n d it io na l s a nd Bool ea n Ex pr essions
We can compare a and b, which have the values 1 and 2, for equality and get
a False, or we can compare whether they are not equal to each other and
get true. If we compare b and c, which both have the value 2, then the equal
comparison is true, and the not-equal comparison is false.
Readings
Exercises
a = 1
b = 2
c = 2
d = "One"
e = "Two"
f = "Three"
g = "one"
1 a > b
2 a == b
3 a != b
4 b == c
5 d < e
6 e < f
7 d < g
8 g < e
| 37
9 not (a == b)
10 b < c or b > c
11 (a+1) == b and not b < c
12 ((a <= b) and (b <= c)) or ((a >= b) and (b >= c))
What would be the output for each of the following segments of code?
13 total_cost = 100.00
days = 3
cost_per_day = total_cost / days
if cost_per_day > 40:
print("Too expensive")
elif cost_per_day > 30:
print("Reasonable cost")
elif cost_per_day > 20:
print("Excellent cost")
else:
print("Incredible bargain")
14 age = 67
income = 10000
if (age > 70):
if (income < 15000):
print("Eligible for benefits")
else:
if (income < 20000):
print("Eligible for reduced benefits")
else:
print("Not eligible for benefits")
else:
if (income < 20000):
print("Eligible for reduced benefits")
else:
print("Not eligible for benefits")
|
38 L e ct u r e 3 C o n d it io na l s a nd Bool ea n Ex pr essions
17 Generally, every fourth year is a leap year, but there are exceptions. If the
year is divisible by 100, then it is not a leap year, unless the year is also
divisible by 400, in which case it is still a leap year. So, 2000 (divisible by
400) is a leap year, 2100 (divisible by 100 but not 400) is not, 2004 (divisible
by 4 but not 100) is a leap year, and 2001 (not divisible by 4) is not. Write code
that examines a variable and year and prints out Leap year or Not a leap
year for that value. Try writing the code in the following three different ways.
Lecture 4 04
Basic Program Development and Testing
I n this lecture, you will learn about some of the big-picture aspects
of the programming process. You will learn whats really involved
in creating programs, especially as programs become larger and more
complex. This is software analysis and developmentalso called
software engineeringwhich is a critical aspect of computer science.
This lecture will focus on one program and the process of developing it.
Along the way, you will learn three general principles that are important
in practical programming: Plan ahead, keep testing, and develop
iteratively.
[ Creating a Program ]
Were going to create a program to help you save money toward a goal
maybe a new appliance, a vacation, a car, or a house. Then, lets assume
that you have a certain amount of money you can set aside each week or
month. You want a program that will help you understand how many times
you will need to set aside that amount to save up enough money to meet
the goal. Mathematically, this is really simplebasically just a division.
It can be really tempting to just jump in and start writing code, but the
first thing we should do when we start programming is to stop and think
about our program.
Generally, you can stop planning at the point when its obvious how to
turn your plans to code. Then, it can be a good idea to start by writing
comments to ensure that we know what needs to be done in each part
of the program that well write. In this case, we can write a comment for
each of the three main steps.
Next, well fill in the actual code for each of these steps. Well start at
the beginning, requesting and reading in some data from the user. We
need two pieces of data from each user: the amount to save for and the
amount regularly set aside each period of time.
For this simple version of the program, we wont ask for how long each
period of time will be. Instead, well just calculate a total number of
payments. So, well need to ask the user for two pieces of information and
then store each of those in a variable. Lets use the variable balance to
store the total balance and the variable payment to store the amount
set aside each period.
The next thing we should do is test the code we wrote to make sure it
works. Regular testing is extremely important in software development,
and its only by testing along the way that youre likely to catch errors that
might otherwise slip through. Test each logical section of your code; you
should never write large amounts of code without stopping to check to
make sure it works.
| 41
We want to see if we really did write code that will read in the values we
wanted and store them correctly. One of the simplest ways we can test
code is to run it and print out the values of variables. So, well type in a
print statement and run our code.
When we run the code and enter a few values for balance and payment,
such as 100 and 10, we do get the values printed back out. So, it looks
like the code we have written is working.
To test this, well want to print out that variable to make sure we got
the right answer. And that happens to be the last of our three tasks:
presenting the information to the user. So, we have a program, and we
need to test it.
num_remaining_payments = balance/payment
#Present information to user
print("You must make", num_remaining_payments, "more payments")
Lets run the code and say that we want to save 100 dollars, and can save
10 dollars, per week. The computer tells us that there is an error in line 9:
unsupported operand type(s) for /: str and str. This means that we tried
to do a divisionthats the slash operandbetween two stringsthats
what the str means. The computer thinks that were trying to divide a
string by another string, which cant be done.
Our division is between the variables balance and payment, so that must
mean the computer thinks that both balance and payment are strings. To
fix this, we will add float around each of the input lines to make sure were
getting floats rather than strings. Our input is coming in as strings, so we
need to convert it to integers or floats if we want numbers.
We type in 100 and 10, and we get the right answer. But just because this
passed one test doesnt mean its actually doing what we want it to do.
Testing means testing many cases. For example, if we enter 325 and 60,
we get an answer that looks right. But when we type in 100 and 0, we get
another errorthis time a division by 0.
There are a few options. Maybe we want to ask them to put in a new
value. Or, maybe we want to say, Thats no goodyou cant reach your
goal if you set no money aside and close the program. Or, maybe we
want to say, Zero in is never going to let you save money. What about
if we use some small value, such as 1? We need to think about which
functionality we want if someone enters bad information, especially bad
information that could crash our program.
So, lets run this to test it. If we enter in 100 and 0, now we get the
message, and we get a chance to enter some other value, such as 10.
Thats a lot better than what we had before.
|
44 L e ct u r e 4 B as i c P r o g r a m Development a nd Testing
After some testing, it seems like regular values are working, and we can
handle the cases where we enter 0 because there is a warning. What
if we enter a negative number, such as a payment of -10? We get a
negative answer.
The math is correct, but it doesnt make much sense. We dont need to
be talking about saving up negative amounts, and saving up negative
amounts will never get us to our value. So, we probably should put in
another check to make sure we handle the negative cases.
For the balance, if the user enters a negative number, there could be
several reasons. Maybe its an error on the users part, or maybe it means
that there is already enough money savedthe amount the user still
needs is less than zero.
Assuming its the second case, we want to treat the balance as zero so
that we compute that no more payments are needed. So, in this case,
well write a statement telling the user what were doing and just set the
balance to zero.
balance = 0
payment = 1
else:
payment = float(input("Enter how much you will save each
period: "))
if (payment <= 0):
payment = float(input("Savings must be positive. Please
enter a positive value:"))
#Calculate number of payments that will be needed
num_remaining_payments = balance/payment
#Present information to user
print("You must make", num_remaining_payments, "more payments.")
Now, if we run this and enter a negative balance, it goes right to the
end. And if we run it again with reasonable values, we still get the right
messages when we have a positive balance. Everything seems to be
working.
[ Iterative Development ]
You might look at this program and see additional areas that could be
improved. For example, the program could interact more with the user.
Maybe we want to ask for the users name and use that in the output, or
ask what the user is saving for and use that. Maybe we want to compute
the number of payments differently, such that we want to round up to the
next-highest integer. We could make all kinds of further modifications to
the program to improve it.
You always want to make sure that the code you have is stable, working
code before you try to add on something else. Repeated testing will help
ensure that you always have a stable base where you can return, without
needing to reexamine everything.
Readings
Exercises
Try making additional iterations to improve the code developed in the lecture.
The following are a few possible improvements you might want to make.
1 Assume that the user has already saved up some amount of money. Ask the
user for an amount already saved. (Note that the balance will be the cost
minus the amount already saved.)
2 Ask the user for the period (week, month, etc.) for how often they will
regularly save money. Use this to make the input and output for the user
more meaningful.
48
05 Lecture 5
Loops and Iterations
[ While Loops ]
With loops, we take our first big step toward avoiding repetition. The
most basic loop structure is the while loop, which is the same thing as an
if-then statement that is repeated over and over. In fact, when we write a
while loop, we write it just like an if-then statement. All we do is replace
the word if with the word while.
An if statement starts with if, then has a condition that can be true or
false, then a colon, and then a body that is indented. The indented part of
the code is the stuff that happens if the condition is true.
This code checks whether some value is zero or negative, and if it is, it
asks a person to input a positive value instead.
if value <= 0:
value = int(input ("Enter a Positive value!"))
What happens if we run this, though, and the person again enters a
number thats not positive? The code just goes on, accepting that wrong
entry. We would have to add another check after the first one, and then
check that one, and so on. If someone were stubborn enough, he or she
could keep entering negative values, and wed eventually run out of if
statements.
| 49
But theres a really easy way to fix all this: with our while loop. With the
while loop, we have the exact same structure we had before, but now
we change the word if to be the word while. The rest of the statement
is the same. We have a condition, followed by a colon, followed by the
code we want to execute, indented.
The main difference is that with the while statement, were going to
keep doing the stuff in the body, as long as the condition is true, without
needing to type in all those if statements. While sets up a loop that is like
an indefinitely long string of ifs.
When we come to this while statement, well first check the condition, just
like we did before with the if statement. If the condition is true, then we
do the stuff in the body, and if not, we skip it.
What if we have a negative value, such as -1? When our code reaches
this line, we find that the value is indeed less than or equal to zero, so
the condition is true. So, we run the indented line, where we ask for a
positive value and get a new value from the user.
MEMORY:
value: -1
WINDOW:
Enter a Positive value!
But lets say that the user is stubborn, or ignoring the command, and
enters another negative number, such as -5. The new number entered
updates the value in memory.
|
5 0 L e ct u r e 5 Lo o p s a n d Iter ations
MEMORY:
value: -5
WINDOW:
Enter a Positive value!-5
With the while statement, once we finish the body of the code, we
go back and check the condition again. In this case, the value is still
negative, so we run the body of the loop again. The user couldyet
againnot enter a positive value.
MEMORY:
value: 0
WINDOW:
Enter a Positive value!-5
Enter a Positive value!0
When we check the condition, its still true, so we do the loop again.
Our program can continue this indefinitely, until the user finally enters a
positive value. Now when we check our condition, its false, so we skip
the loop and go on to the code after the loop.
MEMORY:
value: 1
| 51
WINDOW:
Enter a Positive value!-5
Enter a Positive value!0
Enter a Positive value!1
Lets look at a slightly modified version of this loop. Just like with the if
statement, we can have multiple lines of code in the body of the loop, the
indented part. In this case, lets say that we have two lines: an input and
a print.
We come along to the while statement and have a negative value, so the
condition is true. Because of that, we ask the user for input.
MEMORY:
value: -1
WINDOW:
Lets say that the user enters the value 3, a positive value. The condition
for the loop is no longer true, but we still have one line in the body. So,
we go on to the next line of the body, which prints the value that we put
in just now.
MEMORY:
value: 3
WINDOW:
Enter a Positive value! 3
You entered 3
|
52 L e ct u r e 5 Lo o p s a n d Iter ations
Notice that we only check the condition again after we have gone
through the whole body. Once weve started running the body of the
loop, we dont check our condition again until the body of the loop is
finished. It doesnt matter if the condition of the loop becomes false
somewhere in the middle of the loopwe only care about whether its
true or false at the end.
In the following loop, we set the value to zero and output the data.
WINDOW:
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
The value is: 0
...
Notice that, in this case, the condition is always going to be true. Inside
the loop, we set the value to zero, so every time we check the condition,
value <= 0, its going to be true. If we run this code, assuming that the
value wasnt positive to begin with, its going to just write The value is
zero repeatedly. We can never get out of the loop. We refer to this as
an infinite loop, and its something that we usually want to avoid. But its
actually a pretty common bug.
| 53
Because of this, you want to make sure that you know how to stop
a program thats in an infinite loop. If youre running your code in the
PyCharm or another integrated development environment, youll
probably have some sort of stop button, usually designated by a red
square. Pressing this will stop the loop. Other times, youll need to stop
the program another way, such as by closing the window its running in or
entering some command that will cause the program to quit.
[ For Loops ]
Lets say that we want to find the ages of all the people in a group. We can
count the number of people in the group, and then we can go through a
loop that same number of times.
In the first line, we find out how many people there are in the group by
asking the user. The next lines set up our counter for the number of
people, i (because were iterating, or counting through, an index), and the
sum of the ages in the group. Were going to add all the ages and then
divide by the number of people.
The next line we encounter begins a loop. We go through the loop one
time per person. Inside the loop, were going to ask for the age of the
person and add that age to the total.
|
5 4 L e ct u r e 5 Lo o p s a n d Iter ations
Notice that the message we print out contains the person number. In
the input statement, notice that were asking for person i+1, because
otherwise wed be asking for person 0 first, which would be confusing.
Finally, after the loop is over, we compute the average age by dividing
the total by the number of people, and then output it.
If we run this program, enter in 3 people and the ages 10, 30, and 20, we
find that the average age is 20.
In terms of how the commands are written, the for loop is just a simpler
way of writing a particular version of a while loop. However, in practice,
these two types of loops are used very differently. A while loop is used
when were not sure how many times well need to iterate through the
loop; a for loop is used when we have a precise set of things to loop
through, or know exactly how many times to iterate.
Weve just been looking at loops like the following one. We have some
variable, which we can call an iterator, that gets initialized to some
starting value, gets incremented every time through the loop, and gets
checked until it reaches some maximum value. In this example, the
iterator i is initialized to 0, and then will take on the values 0 through n-1
as we go through the loop.
i = 0
while i < n:
...
i = i+1
We can write this same loop using a for loop, as follows. These two loops
do the exact same thing. The iterator i is still going to take on the values
0 through n-1 as we go through the loop.
| 55
i = 0
while i < n:
...
i = i+1
for i in range(n):
...
We start with the word for. We then give the iterator variable we want
to use, which is i in this case. Then, we have the word in. And then we
have the range, followed by a colon.
for i in range(4):
print(i)
OUTPUT
0
1
2
3
The range command actually gives us a little more control. Lets look
back at our while statement. There are three things that we can vary in
the statement: the starting value, which is 0 in this case; the number that
we are comparing to, which is n; and the amount we increment by every
step, which in this case is 1.
We have control over all three of these things in the range statement.
We can specify the starting value, the value we dont want to meet or
exceed, and the amount we increase by on each iteration.
For loops can get more complicated, and you can see some of the
maybe unexpected behavior if you try putting in negative numbers. For
example, the following case counts down from 5 and stops once were
no longer greater than 1. Notice that Python automatically tells that youre
counting down when it sees the negative value in the third spot of the
range.
OUTPUT
5
4
3
2
This for loop is the same as the following one. A key feature is that it
repeats based on a greater-than comparison, instead of a less-than
comparison.
i = 5
while i > 1
print (i)
i = i - 1
Readings
Exercises
1 i = 10
while i > 1:
print (i)
i /= 2
2 i = 0
value = 0
while value < 20:
value += i
i += 1
print(value)
3 for i in range(4):
print (i)
4 for i in range(3,5):
print (i)
8 Get a number from the user, and then count from 1 to that number. Try writing
it using both a while loop and a for loop.
i = 2
while(i<7):
print(i)
i = i + 3
10 Write a short program that defines a number from 1 to 10, and then keeps
asking the user to guess that number until the correct number is guessed.
59
Lecture 6 06
Files and Strings
I n this lecture, you will learn more about the process of how a
program can interact with files sitting in storage. Whether the data
file is something that already exists before you run the program or its
something you will create from within the program, you need to form
a link between the program and the file sitting in storage. Files are
closely tied with strings, because the typical file format that you will
write to and read from will essentially be one long string. The locations
of those files are also given by strings, so its important to know how to
work with strings.
Finally, once were done, well close the file: The program has
to say that were done with the file. This is going to break the
connection that we made when we opened the file. Closing the
file makes sure that everything in the file is left in a nice condition
so that nothing is corrupted. Also, it prevents us from accidentally
opening too many files at the same time.
|
60 L e ct u r e 6 F ile s a n d Str ings
To open a file, we can use the command Python helpfully calls open.
The following is what an open command looks like.
First, we need a name for the file. This is the name that were going to
use for the file inside our program. Its the name that were going to use
to refer to whatever that file is when we write our code, and its a variable
that we can name like any other.
The name we use inside our program doesnt have to have any relation
to the name of the file in the computer itself. Its just our internal way of
thinking about the file. Our internal name for a file is a variable whose
name makes sense within the context of our program. When we open
a file, well make a particular connection between that internal variable
we use in our program and the specific actual file stored outside
the program.
In this case, the name of the variable well use is myfile. We then need
to assign an actual value to that variable, so we have the assignment
operator, the equal sign.
Next comes our open statement. Notice that it has parentheses right
after it. Inside the parentheses are two strings.
The first of these strings is the name of the file in the computer system.
This is the name of the file that you would see if you looked at a file
explorer or directory on your computer. If you create a file and save it,
this is the name you used to save it. In this case, the name of the file is
Filename.
The final part of the if statement is a second string, and this string tells the
computer how were planning to use the file. If the string has the letter r in
it, it means that we are going to read from the filebasically, its going to
be giving us input.
| 61
If it has the letter w in it, it means that we are going to write to the file
basically, its where we can put our output. And if it has the letter a in it,
it means that we are going to append to the file. That means that we are
going to write to it, but were not writing from scratch; we are going to just
add on more stuff to the end of an existing file.
If you try to open a file for reading and the file doesnt already exist, youre
going to get an error. Obviously, it cant form a link to read in something
from a file that doesnt exist. If you open a file for writing or appending,
itll create the file for you. If you open a file for writing and the file already
exists, you will write over the old version. So, be careful when you write
to files.
myfile.close()
You start out with the name for the file variable. This is your internal name
that youve been usingin the example, its myfile. Then, you add a
period, the word close, and two parentheses.
When we deal with files, we have some code like the following. First, we
open the file, then we do some something, and then we close the file.
But theres another way we can write this code in Python. It makes it
easier to know when you have the file open and when you dont, and it
helps ensure that your file always gets closed.
|
62 L e ct u r e 6 F ile s a n d Str ings
########## OPTION 1
myfile = open("Filename", "w")
#Do something here
myfile.close()
########## OPTION 2
with open("Filename", "w") as myfile:
#Do something here
These two sets of code do the same thing. In the second one, we have
a command, the one starting with with. This opens the file, just like
in the first line of the first set of code. Then, the stuff you want to do
with the code is indented, just like with conditionals and loops. For all
that indented code, we can use the opened file, named myfile in this
case. When we leave the indented portion, the file will be closed for us
automatically.
Either way you do this is okay, but an advantage to this second version
is that you wont ever forget to close your file, because its done for you
automatically. However, the file is only open for the section of code that
is indented.
Writing works like the print statement that weve been using. The
following is an example.
We start out with the name of the file were writing to. This is the internal
name we gave to the file. In this case, its myfile. Next, we have a period,
followed by the keyword write, followed by parentheses. Notice that
this is like the close command. Finally, inside the parentheses, we have
a string.
This is like the print command, but there are some important differences.
The write command can only write strings. It cannot output numbers,
but it can convert numbers if needed. Also, the write command can only
write one string; you can't put in separate strings spaced by commas, the
way you could with a print statement.
Finally, the write command does not put in a newline character at the
end of each line you write. With print, every time we print something out,
it comes out on a new line. With write, thats not the case, and if we want
there to be a new line, we need to explicitly write out a newline character.
First, notice that we opened the file for reading at the beginning. When
we read from a file, were generally going to read in one line at a time.
That line is going to come to us as a string; we will get a string thats
one line from the file. In other words, when we have the readline
command, we read an entire line from the file as one single string. That
string will include the newline character at the very end of the line as
part of the string.
|
64 L e ct u r e 6 F ile s a n d Str ings
We know how to open a file by specifying the files name inside the
parentheses, but its not just the name of the file that can be specified
there. We can actually specify a string that contains both a path and
the filename. The path gives the directory in which the file resides,
also called the folder that the file belongs in. If no path is given, its
assumed that the file is in the same directory as the file for Python itself.
The directory structure is the way all the files on the computer are
organized, in a large hierarchy. Some of these details will vary depending
on which operating system youre using. On Windows machines, the
base directory is designated by some letter followed by a colon. C: is
the most common base directory. To specify a position, you specify each
subdirectory using a backslash character. On Macs, running the OS X
operating system, a forward slash is used to separate the directories.
You can also specify file locations relative to the current files directory. To
specify a relative path, the key thing to remember is that the .. directory
is the directory one level higher in the hierarchy. So, a path such as
..\..\Programming would mean going up two levels in the hierarchy and
then down into the Programming directory.
| 65
Reading
Exercises
2 Open a file named data.txt in the directory above the current one for
writing.
4 Given an open file infile, read and print each line in the file.
5 Ask a user for a filename, and then write the numbers 1 to 10, one per line,
to that file.
6 Assume that you have a data file named data.txt that consists of integers,
one per line. Find and print the average of those numbers. (Hint: You will
want to keep a running total of the sum of numbers and how many numbers
youve read in.)
66
07 Lecture 7
Operations with Lists
In memory, you can think of this as 7 boxes being created, each with a
different value contained. Lets say that we want to actually look at one
of those values. Each of those boxes in the list is going to have a whole
number associated with itcalled its index.
The boxes are going to be numbered consecutively. But the first box
is numbered 0, not 1. In pretty much all of computer science, we start
numbering with 0. So, the first box is 0, the second box is 1, the third is 2,
and so on. For the temperatures for the week, we would have the 7 items
of the list numbered 0 through 6.
daily_high_temps: 0: 83
1: 80 4: 79
2: 73 5: 83
3: 75 6: 86
Each element (or value) in the list is going to have some index. If we want
to get the value in one of those boxesbasically, get an element out
we need a way to refer to it. We can refer to an element of a list by putting
the index in brackets right after the variable name.
variable_name[index]
In the temperature list, printing out the fifth value, daily_high_temps [4],
would print out 79.
OUTPUT:
79
OUTPUT:
80
We can also assign values to the list elements, just like any other variable.
So, in the following case, writing daily_high_temps[3] = 100 assigns the
value 100 to element 3.
OUTPUT:
daily_high_temps: 0: 83
1: 80 4: 79
2: 73 5: 83
3: 100 6: 86
What if we wanted to print out all the elements of a listfor example, just
this list of 7 temperatures? How might we write code to do that?
What about using a loop? How would we create a loop to print every
element? We can loop through all the different index values and print out
the value of each element of the list. The following is one way we might
write such a loop.
OUTPUT:
83
80
73
75
79
83
86
We pick our indexin this case, iand we let it range from 0 to 6. The
command range(7) means that it will take values starting from 0 on up,
as long as it is less than 7. So, in this case, it takes the values 0 through
6. So, for this code, it is going to print out the element for each of those
indices, which will print the whole thing.
Lets say that we didnt know how long the list was. There are a few ways
we could figure this out. One way is to first find out the length of the list.
Fortunately, theres a command to do this: the len command, short for
length. You put the variable in parentheses, and the command returns
the number of elements in the list. So, in this case, the length comes out
to 7.
OUTPUT:
daily_high_temps: 0: 83
1: 80 5: 83
2: 73 6: 86
3: 75
4: 79 x: 7
OUTPUT:
83
80
73
75
79
83
86
OUTPUT:
83
80
73
75
79
83
86
| 71
When we set up a for loop like this, the variable is going to take on the
values of the individual elements of the list. So, when we start the loop,
i will get the value of the first element of the list, which is 83 in this case.
When we print i, we get 83. On the next iteration, i gets the value 80, and
thats what we print. This will continue until i takes on every value in the list.
Note that i is getting assigned the value from the list. So, if we modify the
value of i, it does not change the value in the list.
OUTPUT:
daily_high_temps: 0: 83
1: 80 5: 83
2: 73 6: 86
3: 75
4: 79 i: 0
OUTPUT:
daily_high_temps: 0: 0
1: 0 4: 0
2: 0 5: 0
3: 0 6: 0
|
72 L e ct u r e 7 Ope r at io ns with Lists
[ Appending ]
In addition to creating lists by separating items with commas, we can also
merge lists together by using the addition operation. This creates a new
list by taking all the elements in the first list and then appending on those
from the second list. In this case, we create list 3 by adding lists 1 and 2
together.
OUTPUT:
list1: 0: 3.1 list3: 0: 3.1
1: 1.2 1: 1.2
2: 5.9 2: 5.9
3: 3.0
list2: 0: 3.0 4: 2.5
1: 2.5
We can also increase a list by using the += operation. This will let us
append additional items onto the end of an existing list. The following is
an example, where weve appended list 2 onto the end of list 1.
OUTPUT:
list1: 0: 3.1 list2: 0: 3.0
1: 1.2 1: 2.5
2: 5.9
3: 3.0
4: 2.5
| 73
If we dont have a list, but rather just one item, there is an append
command that lets us add one element onto the end of an existing list. In
the following example, we have the name of the list, then a period, then
the word append, and then in parentheses the thing we want to append
on. In this case, we take list1 and we add on the value 3.9 to the end of it.
OUTPUT:
list1: 0: 3.1
1: 1.2
2: 5.9
3: 3.9
Appending can be really useful for building up lists. There are many times
when we want to keep adding things to a list, but we dont know at first
how long its going to be.
[ Indexing ]
In addition to different ways to build up a list, there are also different ways
to index into a list. The basic way we index into a list is by putting the
element number of the list in brackets. But what happens if we put in an
index number thats too large? The array has 7 elements, so they will be
numbered 0 through 6. In the following, were trying to access element 7.
OUTPUT:
IndexError: list index out of range
|
74 L e ct u r e 7 Ope r at io ns with Lists
In this case, we get an error, saying that were out of range. Thats actually
a good thingthe computer is not letting us access something past the
end of the list.
What will happen if we put in -1 for the index? Instead of getting an error,
which is what you might expect, in Python, putting in -1 gives us the value
of the last element of the list, 86. This is a convenient tool in Python to let
us pull items out of the end of the list.
OUTPUT:
86
[ Slicing ]
Python also lets us pull out a portion of a list with an operation called
slicing. With a slice, we pick where to start and where to stop. So, we
dont have just one index value; we have a colon separating two values.
You can think of these values as similar to the range examples when
working with loops.
The first value, which is a in the following code, is the starting point. The
second value, which is b in this example, is the number that you want to
stop before.
varname[a:b]
Remembering that the index of the first element in the list is 0, if you
want the first three elements, you would enter 0:3, saying that you want
elements 0, 1, and 2. If you want elements 4 and 5, you could enter 4:6.
If you leave off the number before or after the colon, it means that you
want to start from the beginning or go to the end. If you just put a colon, it
means from the beginning to end, which is another way of saying that you
are getting a copy of the whole list.
| 75
Strings are actually a slight variation of a list. That means that slicing on
strings works basically like slicing on lists. The one big difference is that
we cant assign to a string the same way we can with lists. That is, we
cant delete parts of strings, or insert strings in the middle, as we can with
regular lists. You need a separate set of commands to manipulate strings.
[ Lists of Lists ]
We can make a list out of anything. We can have lists of integers, lists of
floats, lists of strings, and even lists of lists. The following is a list of three
lists, each of which has three elements. What will happen if we print out
the first element of this list?
OUTPUT
[4, 5, 6]
In this case, it prints out element 1 of the list of lists, which is the list [4, 5, 6].
Python lets you create lists in which you have a combination of different
types. Most languages require an array to be all stuff of the same type,
but in Python, its okay to have a list of items with an integer, a float, a
string, and another list, for example. This is useful when you want to
group unlike things together.
|
76 L e ct u r e 7 Ope r at io ns with Lists
[ Tuples ]
Another thing that is very similar to a list that Python supports is the tuple,
which is like a list whose values can never change and, unlike a list, often
will contain different types of data. Tuples can be specified by giving a list
of values separated by commas.
OUTPUT:
Buick
Century
23498
('Century', 23498)
We could have done this with lists, but thats not the best use of lists. We
can also access parts of the tuple using indexes or slices, just like with
lists. However, we cannot change the value of a tuple once its created.
| 77
Readings
Exercises
16 Given a list of integers named ages, form a new list named minor_ages
consisting of all those ages from the ages list that are less than 18.
17 Create a list containing two lists: one with names of 3 people and one with
ages of 3 people (you can choose the names/ages).
79
Lecture 8 08
Top-Down Design of a
Data Analysis Program
[ Top-Down Design ]
Top-down design is one of the most commonly applied methods for
developing computer programs. With top-down design, you begin by
looking at the big picturethe topwhich is often too much to consider all
at once in fine detail. You then break that overall task into a series of more
manageable tasksthat is, you work your way down from the top.
1/1/2000,79,37,0
1/2/2000,79,68,0
1/3/2000,73,50,0
1/4/2000,51,26,0
1/5/2000,57,19,0
1/6/2000,59,46,0.08
1/7/2000,53,48,1.61
1/8/2000,53,44,0.76
1/9/2000,72,43,0.01
1/10/2000,75,35,0
1/11/2000,77,42,0
1/12/2000,79,64,0
1/13/2000,72,57,0
1/14/2000,66,43,0
1/15/2000,73,39,0
1/16/2000,78,55,0
1/17/2000,77,51,0.01
1/18/2000,81,57,0
1/19/2000,78,48,0
1/20/2000,64,43,0
| 81
To get weather data like this, you can go to any one of several weather
sites, some of which will let you download it into a spreadsheet, but you
can also cut and paste data into a spreadsheet yourself. Once you have it
in the spreadsheet, you can save it as a CSV file.
Lets assume that we have this file of weather data and that its sitting in our
directory where were writing our Python program to make it easy to find.
Our Python program might be called Your Special Day, and well set it
up to answer some queries about past temperatures on that calendar
day, such as the average daily high and low.
Usually, in design, we want to do a bit more than just sketch out one level
before we write code. But notice how some of this can already translate
to code.
#Read in Data
#Analyze Data
#Present Results
The code that we have here is just three comments. Were not actually
writing commands yet, because we havent really designed any of the
low-level details. All we can do at this point is give the outline of each of
the main sections, and were designating those with comments.
|
82 L e ct u r e 8 To p - D ow n Design of a Data Ana lysis P r og ram
If we go one level down from our top-level design, our first task is to read
the data in. To do this, at a high level, the normal pattern will be to open
the file for reading, to actually read in the lines of the file, and then to
close the file.
Lets first look at what we have so far in terms of how it would appear in
comments. We are basically taking each level of the design and putting
in a comment describing what it does. All of these comments can get a
little messy, so sometimes it helps to put in some additional characters
to help visually separate code, such as the following sets of 10 hashtags.
Again, we have some pretty high-level ideas, so lets break those down
a bit more. Well start with the reading-in-data part. To open the file, we
need to know the filename, which well assume the user is going to give
us. Then, we need to actually give the open command. At this point in
our design, were basically at the level of individual lines of code. Each
of these ideas corresponds to one or maybe a few lines of code. So, we
can actually start writing the code now.
Well start by asking the user to input a filename, and then well call the
open command for that file, noting that we want to read the file. Well
write our open command, calling the file that we open infile and listing
the filename and the letter r in the parentheses.
| 83
Once weve done some tests, next we should think more about our
design. As with any design process, we need to think about our plan
before jumping forward and implementing something. Weve already
completed the file opening, and our next task is to read in the data from
the file. Well be reading in line after line, doing basically the same thing.
So, this is going to mean looping through all the lines of the file.
Well develop this code in stages. First, we need our loop. Well loop
through the lines of the file with a simple for loop. The following for loop
is going to read each line from a file, and it will be stored in the variable
line. Notice that we also set up an empty list, datalist, that we can use
to hold the data that we collect.
We need to extract the individual elements from a line that weve read
in. We have a string that has all this weather data in it. We want to pull
out the first part (date), the second part (high temperature), the third part
(low temperature), and the fourth part (rainfall). These are all separated by
commas. So, wed like to have some command that would let us take the
string and get back each of the parts thats separated by a comma.
We can do all of this with a built-in Python command that lets us pull out
parts of a string that way. The command is called split. We use it by taking
the string name, which in this case is line, followed by a period, and then
in parentheses, we put the character that tells us how to separate the
parts (a comma in this case). The split command returns a set of values
that we can assign to several variablesa, b, and c in this case.
a, b, c = line.split(',')
The thing it returns is a tuple, and well be able to assign the tuple to
several values.
| 85
In our design, we need to split the line up into its parts, and then we need
to store each value in the appropriate placea variable of the right type.
The line.split(,) will give us back the four elements of the line we care
about: the date, high temperature, low temperature, and rainfall. We
probably want temperature and rainfall values to be numbers, and the
split command is going to return strings. So, we can convert these values
using the type converter. Lets convert each of the temperature strings to
integers. For rainfall, lets convert the string to a float.
We still need to put this data for this line into a list so that we can retrieve
it later in the analysis phase. So, we well create a list with all the data we
just found for one date: the day, month, year, low temp, high temp, and
rainfall. Well append that list into the datalist variable that we created.
So, datalist is going to be a list of lists.
Lets also reverse the order of the low and high temperature in our own
list versus what was in the original data file, and lets reverse the order of
month and day.
hightemp = int(h)
rainfall = float(r)
m, d, y = date.split('/')
month = int(m)
day = int(d)
year = int(y)
#Put data into list
datalist.append([day, month, year, lowtemp, hightemp,
rainfall])
#Close File
########## Analyze Data ##########
########## Present Results ##########
The following is what the resulting datalist will look like for the first several
items in the input file.
1/1/2000,79,37,0
1/2/2000,79,68,0
1/3/2000,73,50,0
1/4/2000,51,26,0
1/5/2000,57,19,0
...
datalist:
[ [1, 1, 2000, 37, 79, 0.0], [2, 1, 2000, 68, 79, 0.0], [3, 1,
2000, 50, 73, 0.0], [4, 1, 2000, 26, 51, 0.0], [5, 1, 2000, 19,
57, 0.0], ... ]
#Close File
infile.close()
| 87
Remember that our goal with this program is to find out historical data
about a particular date. So, well need one additional piece of input: the
date were dealing with. Then, well need to get the relevant historical
data for that date. Finally, well need to analyze that historical data to find
the information we care about.
To get the date from the user, well need to ask the user for the month
and the day. There are many ways to do this, but to keep it simple, well
ask first for the month and then for the day. There will be just one line of
code for each piece of the design. We just have a few input commands,
each time converting the input to an integer.
Next, we want to pull out all the historical data for the date we care about.
We have read the original data file into a datalist, which is a list of lists.
Each of the lists inside has the day, month, year, low temp, high temp, and
rainfall. Now, we have a particular month and day that we care about, and
we want to find all of the historic data for those dates.
|
88 L e ct u r e 8 To p - D ow n Design of a Data Ana lysis P r og ram
We want to go through the datalist and for each of the lists in there, check
whether the day and month match the ones we care about. If they do
match, well store that information in a different list.
We first create an empty list called gooddata, which will hold the data for
the dates that match our target date. We then loop through all the datalist.
Notice that in the for loop, we will refer to each element of datalist as
singleday, because it is the data for one single date.
After we get through this code, we should have a list of the information
corresponding to the date we care about. Next, we need to analyze it.
Lets figure out some key information about the highest and lowest
temperature and the average high and low. To do this, well loop over all
the dates and keep track of information as we look at each record.
| 89
First, well count the number of dates so that we can get an average.
Next, well keep track of the highest and lowest temperatures weve seen
so far. Finally, well add up all the maximum and minimum temperatures
so that at the end of the loop we can calculate an average high and low.
Then, we loop through all the data, and we update each of those values
along the way. For every date, we increase numgooddates. We add the
maximum to sumofmax and the minimum to sumofmin. We then check
to see if the minimum is lower than the minimum weve seen so far, and if
so, we update that. We also check to see if the maximum is larger than the
maximum seen so far, and if so, we update that.
#Perform analysis
minsofar = 120
maxsofar = -100
numgooddates = 0
sumofmin=0
sumofmax=0
for singleday in gooddata:
numgooddates += 1
sumofmin += singleday[1]
sumofmax += singleday[2]
if singleday[1] < minsofar:
print(minsofar, singleday[1])
minsofar = singleday[1]
if singleday[2] > maxsofar:
maxsofar = singleday[2]
avglow = sumofmin / numgooddates
avghigh = sumofmax / numgooddates
Reading
Exercise
Modify the program developed in this lecture to also keep track of the chance
that there will be rain on the particular day. Below are a few hints if you
need them.
09 Lecture 9
Functions and Abstraction
The term function is not universal. Other names for it include routine,
subroutine, and procedure. Youll also hear method, although thats
usually only in the context of object-oriented programming. Sometimes
people use the term function with a more specific meaning, in which a
function always returns a value.
Other functions, such as the input function, not only perform some action,
such as printing to the screen, but they also return a value. For the input
function, the function returns a string that is whatever the person typed in.
This return value can get assigned to a variable or whatever else is needed.
As you write the details of your code, youll find yourself writing functions.
Youll also find that within those functions, youll make use of more
function calls, which are when you initiate some action that has been
defined with a function. Calling print means that were using the print
function, and calling input means that were using the input function.
And you can treat these calls as black boxes of their own.
Writing functions to work in Python has two different stages: defining the
function and then calling the function to put it into use.
We define a function by starting with the key term def, short for define.
Next, we have the name of our functionthe command we want to use to
call the function. Following that are parentheses. If our function is going
to take in some form of input, that information is going to be specified
inside the parentheses. Then, we have a colon, just like weve seen in
conditionals and loops. Finally, we have the commands that the function
should do. These are indented, again just like we saw with conditionals
and loops.
def functionname(...):
#details
The term we use for the first line defining the function is called its header.
The actual commands it should dothe part thats indentedis called
the body.
|
94 L e ct u r e 9 F un ctio n s a nd Ab str act ion
def warn():
print("Warning! Use program at your own risk.")
Notice that we start with the word def, short for define. We then have
a name for the functionin this case, warn. Theres nothing inside the
parentheses, because this function doesnt take any input. After the
colon, we indent the commands that we want. In this case, theres just
one command, a print statement that displays a warning message.
We can call this function when writing code. We may have some code and,
in the middle of it, decide that we need to print out a warning message.
We can do so by calling warn, just like we would any other command.
def warn():
print("Warning! Use program at your own risk.")
a = 3
print (a)
warn()
print("Welcome!")
Notice that we need to have parentheses after the name of the function.
When we execute this code, it works like you were expecting it to. The
code before the function call works just like always, in this case printing
the value 3. Then, when we get to the function call, it executes the
function, in this case printing the warning message. After that, it executes
the rest of the code, just like always.
def warn():
print("Warning! Use program at your own risk.")
a = 3
print (a)
warn()
print("Welcome!")
| 95
OUTPUT:
3
Warning! Use program at your own risk.
Welcome!
We could have just output that warning message directly; the code here
works exactly the same way, and we didnt have to create a function to
do it. But there are several reasons that wed want a function to do this.
Lets say that you have some code where youre asking users for
sensitive information and you want to give them plenty of warning that
theyre about to do something dangerous.
a = 3
print (a)
print("Warning! Use program at your own risk.")
print("Welcome!")
OUTPUT:
3
Warning! Use program at your own risk.
Welcome!
You might print a warning before each time you ask for information. This
is a lot of repetition of the exact same thing, and when youre repeating
the same commands over and over, this is often a good time to use
a function.
def warn():
print("Warning! Use program at your own risk.")
warn()
name = input("Enter your name:")
warn()
address = input("Enter your address:")
warn()
ccn = ("Type in your credit card number:")
warn()
expiration = ("Enter the expiration date for your card:")
Notice that this looks much cleaner. It also makes it easier to make
changes that need to happen everywhere. If you want to change the
warning message, we only have to make one change, instead of hunting
down every place we had the message and changing it in each of those
locations. We can also easily expand our warning.
You should aim to use functions any time you find that youre doing
the same task in different areas of the code. The warn function is an
example. Using a function not only means less typing, but it also means
less of a chance that youll have a bug, because any errors are going to
appear every time, instead of just once, and once you fix the bug, its
fixed everywhere that program is used. In addition, any time you have a
concept that you can consider as a single unit, a discrete idea, its a good
idea to use a function to encapsulate it.
| 97
Remember that functions are able to return values. For example, the
input function returns whatever the person typed in. To return a value
for a function, we just include a line that says return something. The
following function gets a persons name as a string. It gets the users first
name, then last name, and then combines them with a space in between.
It returns that combined name. When we call this function, we can assign
the value it returns to a variable, as follows.
def getName():
first = input("Enter your first name:")
last = input("Enter your last name:")
full_name = first + ' ' + last
return full_name
name = getName()
Lets try a variation on this. Starting from the code we had, lets make a
small modification so that it returns in a last name, first name format.
def getName():
first = input("Enter your first name:")
last = input("Enter your last name:")
full_name = last + ', ' + first
return full_name
name = getName()
We just changed the way we formed the string so that it was last plus
, plus first.
|
9 8 L e ct u r e 9 F un ctio n s a nd Ab str act ion
We could have even just returned that string right as we made it. Notice
that the following returns the combined name directly, without putting it
in another variable.
def getName():
first = input("Enter your first name:")
last = input("Enter your last name:")
return last + ', ' + first
name = getName()
def getName():
first = input("Enter your first name:")
last = input("Enter your last name:")
return first, last
userfirst, userlast = getName()
Sometimes, the side effect is something that the person writing the
function thought would be harmless, and thus doesnt even advertise.
Sometimes, this is a bugsome action the programmer never meant the
function to perform. Usually, these side effects go unnoticed, at least for
a while. But, eventually, they can cause serious problems when someone
doesnt realize theyre going to be there.
[ Docstrings ]
Its helpful to provide documentation for functions right up near
where they are first defined, and there is a standard way to do it. The
documentation should say, concisely and specifically, what that function
does. There is even a special syntax used to provide these comments for
a function, and its called a docstring.
A docstring begins and ends with a triple set of quotation marks. Triple
quotation marks are how we create a string that can include new lines,
thus spanning multiple lines. This string should come right after the
function header.
In the following case, we have a function named greet that will take in
a name as a parameter and will print Hello, name for whatever name
is passed in. So, we can create a docstring afterward, with three double
quotation marks, saying Print a greeting: Hello, name.
def greet(name):
"""Print a greeting: Hello, name. """
print("Hello, "+name)
help(greet)
OUTPUT:
Help on function greet in module __main__:
greet(name)
Print a greeting: Hello, name
|
1 00 L e ct u r e 9 F un ctio n s a nd Ab str act ion
Docstrings are a lot like comments, in that they dont actually compute
anything. But they have a second advantage. If you use the command
help for any function, passing in the function name as a parameter, you
will get information about the function, including its docstring.
Readings
Exercises
3 def something3(a):
return a-1, a+1
a, b = something3(5)
print(a, b)
| 10 1
4 def something4(a):
sum = 0
for b in a:
if b < 0:
sum -= b
else:
sum += b
return sum
print(something4([2, -4, 3, -1, 7, -4]))
5 def something5(a):
sum1 = 0
sum2 = 0
for i in range(len(a)):
if i%2 == 0:
sum1 += a[i]
else:
sum2 += a[i]
return sum1, sum2
x, y = something5([1, 2, 3, 4, 5, 6])
print(x,y)
6 A function that takes in a number and a string and prints the string that many
times.
7 A function that takes in two lists of the same length and returns a new list of
that length, containing the smaller of the elements at that index value from
the two lists.
8 A function that takes in three numbers and returns the one in the middle.
|
1 02 L e ct u r e 9 F un ctio n s a nd Ab str act ion
Lecture 10 10
Parameter Passing, Scope,
and Mutable Data
[ Scope of Variables ]
When we say that a variable is in scope, it means that at that point in the
program, it is defined and usable. Scope is what helps keep straight what
we are referring to when we use a name for something. It is an important
way that we make use of abstraction. The bigger our programs become,
the more important scope becomes.
There are two sides every time we make use of a function: the function
definition, where our code describes what the function will do, and the
function call, where a function is used.
|
1 04 L e ct u r e 10 Pa r a m e t e r Passing, Scope, a nd Mu ta bl e Data
But lets write our own max function because that will provide a good
way to illustrate the tricky aspects of functions that are hidden from view
in the preloaded functions.
A function is defined by the header and the body. The header starts with
the keyword def, followed by the function name, followed by a list of
parameters in parentheses and a colon. Then, we indent the body, and
we can exit the body by returning some value.
When we run our program, the computer comes along and sees the
function definition. It doesnt do anything with it at that point, other than
remember that its a function with this name and these parameters, and
then later if that function is called, its going to remember that definition
and use it.
Next, we have whats called the main program. All of our code together
is called our program, but well call the main program the stuff that
actually gets executed firstthat isnt part of a function definition. As our
program gets processed, the first line that gets executed is a line from
the main program.
| 10 5
To understand how functions are really working, lets open the black
box and look at how memory is getting handled. The computer skips
over the function at first, just remembering that it was previously defined,
and executes the first line of the main program. The line a = 1 creates a
variable, named a, and assigns it the value 1.
In the next line, we have a function call. We call the function max, and
we pass in two parameters: a and b. When we call the function, there is
a whole new area of memory set aside for that function to work in. This is
called the function activation record.
In that area of memory, we will first have variables created for the
parameters. In this case, we have val1 and val2 variables created in the
function activation record. As far as that area of memory is concerned,
these variables are named according to the parameters in the function,
not according to the names they originally had. These parameters inside
the function are sometimes named the local parameters, to distinguish
them from the parameters on the callers sidethat is, the ones that are
listed in the main program.
So, when the line c = maxdemo (a,b) is called, the values of the
parameters on the callers side are copied into the new memory
locations, the local parameters. In this case, the value of a, which is 1, is
copied into val1, and the value of b, which is 2, is copied into val2.
The function then runs in its own little memory area, and when something
is finally returned, that is sent back out to the main program memory area.
After that, the memory for that function is all freed up. The activation
record is destroyed, leaving that memory free to use again in the future.
And our program goes on to the next line of code from the main part of
the program.
|
1 06 L e ct u r e 10 Pa r a m e t e r Passing, Scope, a nd Mu ta bl e Data
The testscore function takes in two parameters: the number correct and
the total number. Lets say that everyone did well on a pretest, so youre
giving everyone credit on the test for 5 extra questions. So, the function
first adds 5 to the number of correct answers, then it divides by the total,
and returns that answer, multiplied by 100.
From the main program, we have variables a and b, and then when the
function call is reached, we get a function activation record, where we
have the parameter values copied into the parameters. So, a is copied
into the local variable numcorrect and b is copied into the local
variable total.
What happens when we get to the next line, where the numcorrect
value is increased by 5? The variable numcorrect in the function
activation record is increased by 5, so in this case, it becomes 17.
| 10 7
Notice that we do not change the value of the variable a. The value of
a was copied into the memory location for numcorrect, and when we
change numcorrect, we are changing only the new memory location.
As far as the computer is concerned, it no longer cares that numcorrect
got its value from a; that value could have come from anywhere. Its
now just numcorrect, so whatever happens there doesnt affect the
input parameters.
The next line of code in the function creates a new variable, temp_value,
which gets the ratio of numcorrect and total. This is a new variable, so it
has memory set aside for it. The memory that is set aside is set aside within
the function activation record. We create a variable named temp_value
there, and we assign the value to that variable.
testscore
def testscore(numcorrect, total):
numcorrect += 5; numcorrect: 17
temp_value = numcorrect / total total: 20
return (temp_value * 100) temp value: 0.85
a = 12
b = 20 a: 12
c = testscore(a,b) b: 20
Then, because were done with the function call, the function activation
record is destroyed. We go on to the next line of code.
What should happen if we tried to pull one of the values out of the
function? If we tried to access numcorrect, one of the parameters from
the function call, or maybe the temp_value variable, neither of these is
allowed. Theres no more function activation record; all that memory was
freed up. Theres no way to get those values, even if we wanted to. As
far as the main part of the program is concerned, those things inside the
function never existedthe main program has no way of using them.
In Python, we always pass by value, although there are ways to affect the
parameters outside. Global variables are one way. Another way to get an
effect that seems a lot like pass by reference is to use mutable variables.
Mutable variables bring us to the surprising fact that there are times that
we can modify the value on the calling side, through a function.
However, there are also mutable data types, and one mutable type is a
list. Because lists are mutable, when we pass them as a parameter, the
value in the list can actually change.
|
1 1 0 L e ct u r e 10 Pa r a m e t e r Passing, Scope, a nd Mu ta bl e Data
[ Default Parameters ]
We dont always have to specify each of our parameters. Basically, in the
parameter list, we give a default value for the parameter that can be used
if the caller doesnt specify it.
OUTPUT:
150.0
200.0
In the first case, we can call it just like always, where we specify both
parameters. In this case, we have 10 gallons and are getting 15 miles per
gallon, so we have 150 miles. Theres no difference in the way we call the
function in that case, and it didnt matter that we had the default value.
However, we also have the option to leave off the parameter that has a
default value. In the second case, we specify just one parameter, which
will be the first one, gallons. The other parameter, mpg, is determined
from the default value, which in this case is 20. So, we get 200 miles total.
Default values can get tricky if you use them for mutable data or define
them to equal a variable. Its recommended that you only use default
values if the default value will be a fixed, immutable value.
Readings
Exercises
1 def something1(a):
a = 0
b=3
something1(b)
print(b)
2 def something2(a):
a[0] = 0
b=[1,2,3]
something2(b)
print(b)
|
1 1 2 L e ct u r e 10 Pa r a m e t e r Passing, Scope, a nd Mu ta bl e Data
4 def something4():
a = 3
a = 2
something4()
print(a)
5 def something5():
global a
a = 3
a = 2
something5()
print(a)
6 def something6():
a[0] = 0
a = [1, 2, 3]
something6()
print(a)
Lecture 11 11
Error Types, Systematic Debugging,
Exceptions
Even the very best programmers have bugs, although its true that the
number of bugs you create will decrease a little as you gain experience.
But what makes some of these programmers great is that when they do
have bugs, they can find and eliminate them quickly.
The easiest bugs to find are usually syntax errors, which happen when
you have mistyped something in the program. Most syntax errors are
going to be relatively easy to find, because the interpreter or compiler
|
1 1 4 L e ct u r e 11 E r r o r T y pes, Systematic Debu gging, Exce pt i on s
for any high-level language isnt going to let you go forward. Pythons
interpreter-style compiler will give you a syntax error message and stop
the program from executing if it finds a problem like these.
There are other errors you might not find until the program is actually
running. These are called runtime errors. Many runtime errors will involve
someone giving input incompatible with whats required. Maybe you are
asking for a month, expecting the person to enter a number, and he or
she types in January. This would cause your conversion from a string to
an integer to fail.
A third category of errors, and the ones that are most difficult to find,
are logic errors, in which the computer will run the statements just
fine, but the output will be incorrect. Remember that computers just
do what theyre toldthey follow the instructions exactlyand dont
know that they didnt do what was wanted. These can take many
different forms.
Some logic errors are like syntax or runtime errors, except that the
interpreter lets them through. You might forget the way something was
spelled or how you capitalized, but the computer does not catch the
mistake. Instead, the new spelling can end up creating a new variable
with a different name. The interpreter doesnt know what you meant to
say, only what you actually did say, so you have to be careful. Youll have
written code that is perfectly valid; its just not doing what you wanted it
to. That can make it difficult when reading over the code to notice the
mistake youve made.
Even more problematic, though, is when you have a logic error in your
thinking and you actually meant to have a line like the one you wrote
but shouldnt have. In other words, you really thought you wanted that
less-than sign, for example, but it wasnt the right way to solve the
problem. These are some of the most difficult errors to track down,
because eventually you have to realize that the problem is in your
thinking, not in the code.
| 115
The general approach for debugging has three stages. First, we need
to thoroughly test our code. Testing can tell us that theres an error
somewhere. Second, we need to isolate the error. We want to find the
particular conditions that cause the bug or error to occur and then hone
in on the particular place where the error is occurring. Finally, once weve
found the bug, we need to fix it, which might be a complex task on its
own, depending on the bug.
Lets look at how this will work on a previous example that contained
weather data. Remember that the first step in debugging is to thoroughly
test. Well run the program, enter a datefor example, April 6and get
some result.
|
1 1 6 L e ct u r e 11 E r r o r T y pes, Systematic Debu gging, Exce pt i on s
The most important tests to run are the extreme caseswhat are called
edge cases or corner cases. In our program, we need to think about the
extreme dates. The first and last day of the year are extreme. It wouldnt
hurt to check the first and last days of some other months.
Finally, we need to check any special cases. For dates, theres an obvious
special case: leap day (February 29).
So, our test set should have, at a minimum, January 1, December 31, some
day in the middle, and February 29. And, to be on the safe side, we
should check a few other dates, too.
If we run on this set of test data, we run across the same bug we found
earlier. January 1 comes out okay, but testing December 31 gives us the
crash we found before.
Our next step is to isolate the bug. We have to look for clues as to what
is causing the problem, eliminate things that we check are working
correctly, and gradually focus on the one problem. One of the oldest
methods for debuggingone thats still used in many circumstancesis
just to insert a lot of print statements in the code.
#Perform analysis
minsofar = 120
maxsofar = -100
numgooddates = 0
sumofmin=0
sumofmax=0
raindays = 0
|
1 1 8 L e ct u r e 11 E r r o r T y pes, Systematic Debu gging, Exce pt i on s
After youve run the print statements, you can leave them in the program
and comment them out, by putting a hashtag in front of them or enclosing
them in triple quotation marks. Commenting them out will keep them
around in case you want to run them again later.
There are several debuggers out there, and each of them works a little
bit differently. But they all provide the same basic functionality. There is a
debugger integrated into PyCharm. One of the great things about using
an integrated development environment, such as PyCharm, is that the
debugger is right there waiting for you to use it.
Once weve isolated the bugwe know exactly what the problem iswe
turn to fixing the bug. The thing you should not do is just change this line
of code to get rid of the bug and go on. Instead, you need to think about
where this bug originated. Was it in this line, where we compare day and
month to the wrong elements, or was it when we first built the list and
decided to put day first and month second?
Maybe there are other places in our code where we assumed that month
came before day. Before we make any change to the code, we need to
think about how the change we made is possibly going to affect other
parts of the program.
In this case, this issue is relatively isolated. We dont use the datalist for
anything else, and theres not another check like this one. So, we could
just modify this one line within the for loop. We just swap around day and
month in the if statement, and that should fix the error.
Or, we could change the way the datalist is constructed to begin with.
We would just build up datalist with month first and then day.
Either way, the next thing we should do is determine if we actually fixed the
bug by rerunning all of our tests. We want to make sure that our fix didnt
break something that was already working and that it fixed the problem
it was supposed to. So, in this case, well run the code for our test suite:
January 1, December 31, February 29, and some other day in the middle.
And now it seems to work, telling us that we did seem to fix the error.
| 121
[ Exceptions ]
Runtime errors come up when a program is running, typically due to
an unexpected input of some kind. Python, like many other languages,
has a special way that it can deal with runtime errors. This is through
exceptions, which are a way of handling the special error conditions that
can occur when a program is running.
For example, trying to open a file that doesnt exist, converting a string
to an integer when the string turns out to be a word instead of a number,
or dividing by zero will usually cause a program to crash, printing out an
error. Exceptions are a way of taking these problems and handling them
in some way so that the program doesnt have to crash. For example, if
opening a file to read it in fails, we can ask the user for a new filename.
try:
#Commands to try out
except <name of exception>:
#how to handle that exception
Avoid using exceptions for cases that can and should be handled at the
point the problem is detected. Conversely, use exceptions only when the
problem cant be handled at the point of detection.
Readings
Exercises
1 Imagine that you have written a piece of code that is supposed to return
a ticket price given an age. Those under age 3 are free, other children
from 3 to 12 are $5, and all others are considered adults and cost $10.
When you test your code, what are the ages that would be good to use in
your tests?
2 Assume that you have written the following code to find the middle element
from a 3-element list.
def findmiddle(a):
if ((a[0] >= a[1]) and (a[1] >= a[2])) or ((a[0] <= a[1])
and (a[1] <= a[2])):
return a[1]
elif ((a[0] >= a[2]) and (a[2] >= a[1])) or ((a[0] <=
a[2]) and (a[2] <= a[1])):
return a[2]
else:
return a[0]
| 123
Notice that if a list is passed in that is not of length at least 3, the code will
give an error.
a) Modify the function so that it will raise an exception if the list is not valid.
b) Then, show how you could call the function, printing a message if there was
an exception.
124
12 Lecture 12
Python Standard Library,
Modules, Packages
There are a many different types of modules with all kinds of different
uses. Some of them aid us in basic programming functionality. For
example, a math library, such as the math module from the standard
library, or the downloadable NumPy module, gives us access to many
other mathematical functions. We get access to all of these functions by
the command import math, or import numpy, and then can reference
things like pi and the sine function.
import math
print(math.sin(math.pi/2))
import webbrowser
webbrowser.open("http://www.thegreatcourses.com")
import turtle
turtle.forward(100)
Other modules might help you get input from a computer. Tkinter
provides a link to the commonly used Tcl/Tk library thats used for making
graphical user interfaces.
|
1 2 6 L e ct u r e 12 Py t ho n Sta nda r d Libr a ry, Mod u l es, Pack ag e s
Other modules, such as shutil, will let us do things on the computer itself,
such as copy a file. The following code copies file1.txt to file2.txt.
import shutil
shutil.copy("File1.txt", "File2.txt")
And thats just the beginning. There are hundreds of modules distributed
with every installation of Python as part of the Python standard library.
And there are thousands more available for download from PyPl or
independent websites. If you want to see how many are already installed,
try typing help(modules).
The typical use for a module is not to hold code to run right away when it
is imported in. Instead, a module will usually define a set of functions we
can use later on in our program. A module can also define variables. And
it can define classes.
So, this is how modules work: You essentially have other files that contain
a bunch of function definitions. Then, you have an import command
that, in effect, loads that file into your program.
For example, the standard librarys module called math has a list of
about 45 functions that are included. There is a square root function, and
there are functions for computing greatest common divisor or cosine. In
addition, some values are defined, such as the value of pi.
To write a program using these commands, wed just import the math
module. Then, we can use math.cos to compute the cosine and
math.pi to get pi.
|
1 28 L e ct u r e 12 Py t ho n Sta nda r d Libr a ry, Mod u l es, Pack ag e s
The Python standard library is quite useful on its own, and its modules
are probably the most important and widely used setthats why theyre
part of the standard. But theres a much bigger source of modules out
there; these are the third-party modules.
[ Packages ]
One thing that helps you find what you want is that modules themselves
are often bundled together into what Python calls packages. A package
is a collection of modules.
A package can bring in many modules, and we can access those modules
by adding an extra period and then the name of the subpackage or
module. The rest of the import works just like before.
There are many popular packages out there. NumPy and SciPy are
packages used for a lot of mathematical and scientific computing.
Matplotlib provides graphing and plotting capabilities. ZeroMQ is a
| 129
To find a good Python module for something we want to do, there are a
few options. One would be to browse through the Python Package Index
(PyPI): https://pypi.python.org/pypi. The PyPI is an official index of Python
modules that other people have released. These are not automatically
included with your Python installation, and the Python modules that are
collected there are not necessarily good, or reviewed, or rated.
Almost anyone can create a module and upload it there for others to use.
There are several tens of thousands of modules uploaded to the PyPI,
and the purpose of the index is to make sure that theres a central place
people can go for the modules they want.
A second option is to just do an internet search for the topic you want a
module for and then add Python module to the end.
Python also has a recommended tool for installing packages, called pip.
Assuming that you installed a recent version of Python, pip will have been
installed automatically for you, so it will already be on your computer.
To use pip, you will need to go to the command line in your computer.
The command line is an interface in your operating system that you might
not be that familiar with from standard usage. In Windows, you can get to
the command line by running the program CMD. On a Mac, you want to
run the Terminal program. If you dont know where it is, you should be
able to find it in the Applications and then Utilities folder.
|
1 30 L e ct u r e 12 Py t ho n Sta nda r d Libr a ry, Mod u l es, Pack ag e s
The command line will let you type in commands to the operating system
directly. If you installed Python so that it could be run from anywhere, you
can type the next command anywhere. Otherwise, youll need to go to
the directory that contains Python.
You can install a Python package using pip by typing the line python
m pip install <package name>. That should find and install the package
you specify, along with any packages it needs. Once that is done, youll
be able to access that package from Python, just like any of the standard
library packages.
For most packages, you can get a list of commands that a package
provides from within Python. After importing the module, you can use the
dir command, passing in the module name.
import math
print(dir(math))
This list will show you the names of the functions provided. Its not
necessarily a whole lot of help to see the function names without
knowing what parameters they take or what they do, but its a start, and
it can be useful to verify that you didnt accidentally overwrite a function
name or something.
If the developers of the module were good about using docstrings, you
should be able to write help(), with the function name in parentheses, to
find out more about the function, too. More helpful, however, is to look at
the online documentation for that package to see how to use it.
| 131
Readings
Exercises
1 From the Python standard library, find the module you could import to do
each of the following.
a) Read zipped files and compress and uncompress files in a zip format.
b) Work with numbers as fractions.
c) Send email. Note: Email is sent using the SMTP protocol.
d) Work with URLs (the addresses of web pages).
2 What would be the code you would write to make a new directory named
DataDir off of the current one? Note: You can do this using the os module,
which is part of the Python standard library. You will probably need to look at
the os module documentation to find the appropriate command.
132
13 Lecture 13
Game Design with Functions
I n this lecture, you will learn how to develop a game that is similar
to many popular computer games. You will learn how functions
directly support a top-down design approach, and you will use stub
functions to help you rough in the structure of the program along the
way. The game will have the guts for a grid-based matching game,
in which you have a bunch of objects arranged in a grid and you try
to move things around to match up similar items, at which point the
matched-up items disappear.
When a move is made, some objects are removed from the grid
according to patterns that are made. In our case, well remove any cases
with three or more of the same object adjacent in the same row or
same column. Usually, this is what the player gets points for. Then, the
remaining objects rearrange, typically by falling down to fill in the gaps
just removed.
| 133
Well want to fill in gaps at the top with random new objects. The game
continues like this until the user meets a goal. For this game, the goal will
be to get a predefined number of points.
This is a somewhat complex piece of software; its certainly not the kind
of thing we want to just sit down and start writing.
In code, we can, and should, take each of these steps and put in a
comment describing what needs to be done and in what order. In this
case, we have three comments: one for the initialization, one for the loop,
and one for taking a turn.
#Initialize
#While game not over
#Do a round of the game
Each of these general tasks is something that can, and usually should, be
encapsulated into a function. To illustrate, every time we have one of these
tasks, well turn it into a function call. This is the main idea of procedural
programming, where functions are created to handle all the main tasks.
The following is what the current code looks like if we introduce the
functions. Notice that each of the original lines has turned into one
function call. Those actual functions are defined, but dont do anything,
because we havent gotten to that level yet.
|
1 34 L e ct u r e 13 Ga m e D e s ign with Fu nct ions
def Initialize():
#Initialize game
def ContinueGame():
#Return false if game should end, true if game is not over
def DoRound():
#Perform one round of the game
#Initialize game
Initialize()
#While game not over
while ContinueGame():
#Do a round of the game
DoRound()
The term we use to refer to the little functions that are placeholders
for something that should be much bigger is a stub, which is a function
that doesnt really do what its intended to do but is just enough that
everything around it runs.
Because weve written some code, the next thing we should do is test it.
To test in this case, we want to see if everything is getting called in order.
The goal is to have something stable that has been tested.
Lets look at one of the functions that hasnt been defined yet: initialization.
In initialization, we need to set up the grid itselfthat is, we need to get
all the pieces placed into their starting positions on the grid. We also
have to set the users score to zero because were just starting the game.
And we probably want to initialize other variables, such as one that will
help us keep track of which round of the game we are on.
| 135
For one round of the game, we have three basic steps: We have to get
the move from the user, update the game based on that move, and then
display the new grid to the user.
This brings us to our continue the game check. It turns out that this is
going to be a pretty simple check for our gamewe just want to see if the
user has reached the goal score yet, or not. So, well have a conditional
that checks whether the score exceeds some maximum, or not, and
return true or false for that routine.
We have an if statement that compares current score and goal score and
a return of either true or false.
In the main part of the code, we will make a change to the call to
ContinueGame. We set up the score and the goal, and we call
ContinueGame with that score and goal passed in as parameters.
We need to decide how the grid itself will be represented. This is what
we would refer to as a data structure. For this game, we can have a pretty
straightforward data structure. Basically, we want our grid to have a set of
rows and columns, and in each of those rows and columns, we have one
object. In this case, an object is just a letter.
Lists let us store rows and columns nicely. In fact, a grid representation is
a list of lists.
|
1 36 L e ct u r e 13 Ga m e D e s ign with Fu nct ions
Lets say that our board is an 8-by-8 grid, like a checkerboard. We will
thus need a list of 8 rows, each with 8 elements. We will actually set these
elements to what is needed for the game in the initialization routine, but
to begin with, we will make a list of all these elements.
board = [
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]
def InitializeGrid(board):
#Initialize Grid by reading in from file
print("Initializing grid")
def Initialize(board):
#Initialize game
#Initialize grid
InitializeGrid(board)
#Initialize score
global score
score = 0
#Initialize turn number
global turn
turn = 1
| 137
Notice that because this is a list, it is mutable, and thus we are passing it
as a parameter to initialize it. There are different ways we could initialize,
but lets assign random objects to each grid cell.
Thats the initialization stage. We can now turn our design to the game
round itself. There are four basic parts to a turn: presenting the state of
the game, then getting the users move, then determining the result of
that move, and finally incrementing the turn number.
The top-down approach means that we can create a separate function for
each of these main steps. We just call these in order from our DoRound
routine.
|
1 38 L e ct u r e 13 Ga m e D e s ign with Fu nct ions
def DrawBoard(board):
#Display the board to the screen
print("Drawing Board")
def GetMove():
#Get the move from the user
print("Getting move")
return "b1u"
def Update(board, move):
#Update hte board according to move
print("Updating board")
def DoRound(board):
#Perform one round of the game
#Display current board
DrawBoard(board)
#Get move
move = GetMove()
#Update board
Update(board, move)
#Update turn number
global turn
turn += 1
The next unfinished portion of our routine is presenting the board. Were
just printing to the screen at this point, so we just need to output each
of the grid values, in an orderly format. Well draw horizontal and vertical
lines to separate the individual elements.
def DrawBoard(board):
#Display the board to the screen
linetodraw=""
#Draw some blank lines first
print("\n\n\n")
print(" ---------------------------------")
#Now draw rows from 8 down to 1
| 139
for i in range(7,-1,-1):
#Draw each row
linetodraw=""
for j in range(8):
linetodraw += " | " + board[i][j]
linetodraw+= " |"
print(linetodraw)
print(" ---------------------------------")
Thats our display routine. Lets now address our get move routine. For
now, well just ask the user for a move and return that move. Notice that
we havent said what form a move should take, so for now, the move is
just a string.
def GetMove():
#Get the move from the user
move = input("Enter move: ")
return move
Next, well turn to the actual turn mechanics, which is embodied in the
update routine. The turn mechanics are the main thing that define
the game. They embody the rules about how the game progresses
according to a move. In this case, there are a few parts, each of which will
require us to do something.
First, well need to update the board according to our move. In this case,
that means swapping one object with an adjacent one. Then, well need
to repeatedly eliminate pieces and update the board until theres nothing
more to be eliminated. Well have to go through and remove any pieces
that are three in a row or three in a column. This will leave some empty
spaces, and everything else will need to fall down. Finally, any blank
spaces at the top will get filled in with new random objects.
Our move must also say what direction we are swapping. To do that, well
put a single letter after the space to say whether it is swapping up, down,
left, or right (u, d, l, and r, respectively). Also, there are some invalid moves.
| 14 1
def ConvertLetterToCol(Col):
if Col == 'a':
return 0
elif Col == 'b':
return 1
elif Col == 'c':
return 2
elif Col == 'd':
return 3
elif Col == 'e':
return 4
elif Col == 'f':
return 5
elif Col == 'g':
return 6
elif Col == 'h':
return 7
else:
#not a valid column!
return -1
def SwapPieces(board, move):
#Swap pieces on board according to move
#Get original position
origrow = int(move[1])-1
origcol = ConvertLetterToCol(move[0])
#Get adjacent position
if move[2] == 'u':
newrow = origrow + 1
newcol = origcol
elif move[2] == 'd':
newrow = origrow - 1
newcol = origcol
elif move[2] == 'l':
newrow = origrow
newcol = origcol - 1
|
1 42 L e ct u r e 13 Ga m e D e s ign with Fu nct ions
Well create a new 8-by-8 board that we will use to keep track of whether
a piece should be removed or not. Well then update this board by looking
at the rows and columns to find three of the same object and mark those
spaces if they need to be removed. After weve found all the pieces to be
removed, well go back and remove them. As were removing them, well
increase the score. Finally, well return True or False, depending on
whether the pieces were removed or not.
def RemovePieces(board):
#Remove 3-in-a-row and 3-in-a-column pieces
#Create board to store remove-or-not
remove = [
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]
#Go through rows
for i in range(8):
for j in range(6):
if (board[i][j] == board[i][j+1]) and (board[i][j] ==
board[i][j+2]):
#three in a row are the same!
remove[i][j] = 1;
remove[i][j+1] = 1;
remove[i][j+2] = 1;
#Go through columns
| 14 3
for j in range(8):
for i in range(6):
if (board[i][j] == board[i+1][j]) and (board[i][j] ==
board[i+2][j]):
#three in a row are the same!
remove[i][j] = 1;
remove[i+1][j] = 1;
remove[i+2][j] = 1;
#Eliminate those marked
global score
removed_any = False
for i in range(8):
for j in range(8):
if remove[i][j] == 1
board[i][j] = 0
score += 1
removed_any = True
return removed_any
The next stub routine to fill in is for dropping pieces. To do this, well go
to each column and make a list of remaining pieces from bottom to top.
Well then fill in the column with those pieces, putting zeros in at the top.
def DropPieces(board):
#Drop pieces to fill in blanks
for j in range(8):
#make list of pieces in the column
listofpieces = []
for i in range(8):
if board[i][j] != 0:
listofpieces.append(board[i][j])
#copy that list into colulmn
for i in range(len(listofpieces)) :
board[i][j] = listofpieces[i]
#fill in remainder of column with 0s
for i in range(len(listofpieces), 8):
board[i][j] = 0
|
1 4 4 L e ct u r e 13 Ga m e D e s ign with Fu nct ions
We have just one more stub function to fill in: filling in any blank spaces
with new pieces. In this case, well just run through all spaces, and if
theres a zero, well replace it with a random new piece.
def FillBlanks(board):
#Fill blanks with random pieces
for i in range(8):
for j in range(8):
if (board[i][j] == 0):
board[i][j] = choice(['Q', 'R', 'S', 'T', 'U'])
We finally have the whole program finished. We can play it now. And
when we do, we probably see some things that we could improve. We
can use an iterative improvement process to gradually add on these
additional features.
Reading
Exercises
Imagine that you wanted to create a tic-tac-toe game on the computer. Assume
that the board spaces are numbered 1 through 9, with the top row numbered
1, 2, 3; the middle row numbered 4, 5, 6; and the bottom row numbered 7, 8, 9.
Show the code you would write for the following pieces of the program.
3 A function that takes in a board and examines the first row. If all elements
are X, or all are O, then that character is returned. Otherwise, . is
returned.
146
14 Lecture 14
Bottom-Up Design, Turtle Graphics,
Robotics
[ Turtle Graphics ]
To illustrate bottom-up programming for things like robots, were going
to use a simple module that will let us simulate a robot motion. The
name of this is the turtle module, and its one of the modules installed
automatically with the Python standard library. The turtle module lets us
create what are called turtle graphics, which are relatively simple line
drawings but can be lots of fun on their own.
A simple turtle graphics program in Python looks like the following. Its
a program to create a square spiral. From the turtle module, we import
the forward and left commands, and then for every i within a given
range is movement forward and then a left turn. The little shape that
moves around the screen and traces out a path as it moves is called
a turtle.
| 14 7
Even though this isnt a real robot moving around, we can treat the turtle
like a robot. Itll have some of the same basic commands that a real robot
would have.
For our example, the turtle will have only six basic commands: move
forward or backward, turn left or right, and raise or lower a pen it carries.
When the pen is down, wherever it moves is traced out as a graphic on
the screen. When the pen is up, it moves without tracing an image.
These commands, and many others, are all part of the turtle package. The
forward and backward commands take in a parameter that says how far
to move in pixels, where a pixel is just one dot on the screen. Images that
you see are made up of a bunch of pixels arranged in a large grid.
The commands left and right cause the turtle to turn in place, either
to the left or to the right, with the number of degrees to turn passed in
as a parameter. The two controls for the pen are simply pendown and
penup.
The turtle will start in the center of the screen, facing to the right, with the
pen down.
These are the most basic commands. For a real robot, youll often have
something similara few basic motion commandsthat you will have to
put together to do something more complicated.
For example, lets say that we want to draw a square. We can imagine
what we need the turtle to do: go forward for a while, turn 90 degrees
(counterclockwise), go forward the same amount, and so on, until the
square is completed.
|
1 4 8 L e ct u r e 14 B otto m - Up Design, Tu rtl e Gr a ph ics, Roboti cs
First, notice that we can use a from turtle import * command to get all
six commands from the turtle module. Well make the square 100 units
long on each side. So, drawing the square means that we move forward
100 units, turn 90 degrees, etc., until weve drawn all four sides. We are
going counterclockwise, so we turn left.
When we run the program, the turtle goes around and draws all four
sides, creating a square. The turtle (the triangle-looking thing) is back
at the center of the screen, although now its facing down instead of to
the right.
left(90)
forward(100)
drawSquare()
input()
There are several ways we can improve the square program. And we
can also create other shapes, such as a triangle or rectangle. And if you
explore some of the other turtle commands listed in the library, you can
get other features.
[ Robot Program ]
One great, if perhaps surprising, way to think about the turtle library is
as a good proxy for robot motion. So, were going to look at how we
could control a robot to have it explore a roomthe same way a robot
vacuum cleaner might, for example. Well assume that we have the basic
turtle commandsforward, backward, turn left, etc.and will build from
the bottom up from those basic commands to define the robots paths to
cover a whole room.
Our turtle is obviously not a real robot, and it does not have sensors.
However, we can just define a sensor function that will act like a sensor
for our on-screen turtle.
Our code will start by importing two modules. First, were going to be
using turtle pretty heavily, so well import all the turtle functions, indicated
with an asterisk. Second, were going to want some random functions, so
well import the random module, too. Next, well set up variables to say
that the room were operating in is a simple square, going from -250 to
250 in both x and y. And well assume that we should say were too close
to the edge if were within a proximity of 10.
The sensor function itself will use the position command from the turtle
library to compute how far away the turtle is from each of the walls. If
the turtle is within proximity of any of the four walls, the sensor function
returns True, indicating that the sensor had triggered. Otherwise, it
returns False.
Robot vacuums typically have just a few basic types of motion. It can
travel in an ever-increasing spiral, and in fact it usually starts out in a
spiral. It travels in a straight line, in some seemingly random direction. It
also moves parallel to a wall that it is close to.
Were going to try to build up these patterns for our turtle. For all of them,
we only want to continue the pattern until the sensor triggers a proximity
warning, at which point we have to do something else.
Lets start by thinking about how wed build the easiest of these
traveling in a straight line in a random direction. How would we use our
forward and our left or right commands to pick a random direction,
and then head in that direction, until the sensor triggered? Remember
that we have the random module available to us, too.
def straightline():
'''Move in a random direction until sensor is triggered'''
#Pick a random direction
left(random.randrange(0,360))
#Keep going forward until a wall is hit
while not sensor():
forward(1)
|
1 5 2 L e ct u r e 14 B otto m - Up Design, Tu rtl e Gr a ph ics, Roboti cs
The turtle will turn left by some random amount between 0 and 360
degrees. We use randrange from the random module to pick the
number of degrees and pass this to the left function. The second part
of the function just continues in a straight line until the sensor returns
True. Notice that we only move forward one unit at a time before we
check the sensor again.
If we run this code, we see that the turtle heads off in some random
direction, until it hits the edge of the square room that its in.
def straightline():
'''Move in a random direction until sensor is triggered'''
#Pick a random direction
left(random.randrange(0,360))
#Keep going forward until a wall is hit
while not sensor():
forward(1)
straightline()
Next, lets define a spiral function. We could use the spiral that we defined
earlier, but thats a square spiralit would be better to have something
more circular. Mathematically, this is going to be trickier.
If we run this code, we see that the turtle indeed is going to spiral out.
How might we build a pattern for wall-following? In this case, well want
to find which of the four walls is closest and then set our direction to be
parallel to that wall. Note that well want to use the turtle function named
setheading, which allows us to give a direction: 0 is to the right, 90 is
up, 180 is left, and 270 is down.
|
1 54 L e ct u r e 14 B otto m - Up Design, Tu rtl e Gr a ph ics, Roboti cs
def followwall():
'''Move turtle parallel to nearest wall for amount
distance'''
#find nearest wall and turn parallel to it
min = xmax - position()[0]
setheading(90)
if position()[0] - xmin < min:
min = position()[0] - xmin
setheading(270)
if ymax - position()[1] < min:
min = ymax - position()[1]
setheading(180)
if position()[1] - ymin < min:
setheading(0)
#Keep going until hitting another wall
while not sensor():
forward (1)
speed(0)
#Start with a spiral
spiral(40)
while (True):
#First back up so no longer colliding
backward(1)
#Pick one of the three behaviors at random
which_function = random.choice(['a', 'b', 'c'])
if which_function == 'a':
straightline()
if which_function == 'b':
backupspiral(random.randrange(100,200), random.
randrange(10,50))
if which_function == 'c':
followwall(random.randrange(100,500))
If we run this code, we start out in a spiral. One weve spiraled all the way
out to where we come in proximity with a wall, we start taking random
motions, according to one of our three patterns.
speed(0)
#Start with a spiral
spiral(40)
while (True):
#First back up so no longer colliding
backward(1)
#Pick one of the three behaviors at random
which_function = random.choice(['a', 'b', 'c'])
if which_function == 'a':
straightline()
|
1 5 6 L e ct u r e 14 B otto m - Up Design, Tu rtl e Gr a ph ics, Roboti cs
if which_function == 'b':
backupspiral(random.randrange(100,200), random.
randrange(10,50))
if which_function == 'c':
followwall(random.randrange(100,500))
Reading
Exercise
Hint: Make the sides of the A at a 60-degree angle to the horizontal. This will
make the shape of the A an equilateral triangle, which may be easier to draw.
Suggestion: Make the turtle finish in its original orientation, shifted over slightly
from the last point on the A.
157
Lecture 15 15
Event-Driven Programming
[ Pyglet ]
Pyglet is a Python package created to help support development of
games and other audiovisual environments. It provides functions that
let you create windows, display images and graphics, play videos and
music, get input from the mouse, etc.
If youre using pip to install modules such as pyglet, you should be able
to install pretty easily. Remember that you can go to the command line, to
the directory where Python is installed, and type python m pip install
pyglet and it should install for you.
|
1 58 L e ct u r e 15 Ev en t- D r iven P r ogr a mming
Once you have pyglet downloaded, be sure to run the command: import
pyglet. Only if you dont have pyglet installed will you see a response, so
any error message probably means that pyglet hasnt yet installed properly.
[ Event-Driven Programming ]
One form of graphics that is familiar to everyone on a computer these
days, in practice if not by name, is the graphical user interface (GUI, or
gooey). The way a GUI works is entirely based on what is called event-
driven programming.
Theres more than one way to monitor events. Sometimes the event
monitor uses what are called interrupts. Basically, it just sits there until
something interrupts it with an event. Other times, the event monitor
actively polls the various devicesthat is, it actively checks to see
whether there is a keyboard event, a mouse event, etc. But you dont
have to worry about whether polling or interrupts are monitoring events
in your own programsjust know that the event monitor is indeed going
to be getting the events.
The event handlers main job is to take in events and then call what is
known as a callback function corresponding to that event. The callback
is just a function to execute in response to an event, at which time we go
back to the event handler to get the next event.
For the main body of the program, theres usually some initialization work
thats done, just like any sequential program. As part of this initialization,
callbacks need to be registered. Registering a callback function is how
we say what function will be called for each event that we want to respond
to. So, we have to write functions for each possible event. The final step of
the sequential part of the program is to start up the event monitor.
import pyglet
window = pyglet.window.Window(width=400, height=300,
caption="TestWindow")
pyglet.app.run()
When we run this, we get a window of size 400 pixels by 300 pixels, and
the name on the window is TestWindow.
for i in range(8):
for j in range(8):
if remove[i][j] == 1:
board[i][j] = 0
score += 1
removed_any = True
return removed_any
def DropPieces(board):
#Drop pieces to fill in blanks
for j in range(8):
#make list of pieces in the column
listofpieces = []
for i in range(8):
if board[i][j] != 0:
listofpieces.append(board[i][j])
#copy that list into colulmn
for i in range(len(listofpieces)):
board[i][j] = listofpieces[i]
#fill in remainder of column with 0s
for i in range(len(listofpieces), 8):
board[i][j] = 0
def FillBlanks(board):
#Fill blanks with random pieces
for i in range(8):
for j in range(8):
if (board[i][j] == 0):
board[i][j] = choice(['A', 'B', 'C', 'D', 'E'])
def Update(board, move):
#Update the board according to move
SwapPieces(board, move)
pieces_eliminated = True
while pieces_eliminated:
pieces_eliminated = RemovePieces(board)
DropPieces(board)
FillBlanks(board)
|
1 64 L e ct u r e 15 Ev en t- D r iven P r ogr a mming
@window.event
def on_draw():
window.clear()
for i in range(7,-1,-1):
#Draw each row
y = 50+50*i
for j in range(8):
#draw each piece, first getting position
x = 50*j
if board[i][j] == 'A':
Im1.blit(x,y)
elif board[i][j] == 'B':
Im2.blit(x,y)
elif board[i][j] == 'C':
Im3.blit(x,y)
elif board[i][j] == 'D':
Im4.blit(x,y)
elif board[i][j] == 'E':
Im5.blit(x,y)
label = pyglet.text.Label('Turn: '+str(turn)+' Score:
'+str(score), font_name='Arial', font_size=18, x=20,
y = 10)
label.draw()
@window.event
def on_mouse_press(x, y, button, modifiers):
#Get the starting cell
global startx
global starty
startx = x
starty = y
@window.event
def on_mouse_release(x, y, button, modifiers):
#Get starting and ending cell and see if they are adjacent
startcol = startx//50
startrow = (starty-50)//50
endcol = x//50
endrow = (y-50)//50
| 165
The new code, using event-driven programming, is shorter than the code
for the text-based game. Creating fancier interfaces doesnt have to be a
huge amount of code; you can create a lot of flexible functionality easily if
you have the right libraryin this case, pyglet.
[ Tkinter ]
Tkinter, a module that is part of the Python standard library, is useful for
creating GUIs with buttons, boxes, and sliders. TK is a cross-platform
toolkit that has been ported to many programming languages. Tkinter
provides a binding between Python and the overall TK toolkit. The
fact that its cross-platform means that its available for and works on
most platforms.
Although TK itself is not part of Python, its released with Python, and the
Tkinter module is built on top of it, providing an interface between Python
and TK. This lets Python programmers have relatively easy access to a
powerful cross-platform GUI library.
import tkinter
class Application(tkinter.Frame):
def __init__(self, master=None):
tkinter.Frame.__init__(self, master)
self.pack()
self.increase_button = tkinter.Button(self)
self.increase_button["text"] = "Increase"
self.increase_button["command"] = self.increase_value
self.increase_button.pack(side="right")
self.increase_button = tkinter.Button(self)
self.increase_button["text"] = "Decrease"
| 167
self.increase_button["command"] = self.decrease_value
self.increase_button.pack(side="left")
def increase_value(self):
global mainval
mainval *= 2
print (mainval)
def decrease_value(self):
global mainval
mainval /= 2
print (mainval)
mainval = 1.0
root = tkinter.Tk()
app = Application(master=root)
app.mainloop()
That routine to create the widgets creates two widgets in this case: the
two buttons. Each is created by four lines of code. The first button is the
increase button. The first line of code for that section just tells TK that
were creating a button, which we refer to by the local variable increase_
button. The button is an object, and objects will contain data called
attributes and functions called methods.
|
1 6 8 L e ct u r e 15 Ev en t- D r iven P r ogr a mming
The second line of code for this button says that the button should
display the text increase, and it does this by setting the text attribute
of increase_buttton.
The third line registers our callback by setting the command attribute of
increase_button. It says that when the button is pressed, we should call
the increase value function.
The fourth line says to place the button at the right of the window. It does
this by calling the pack() method thats part of the increase_button.
The increase value function will take a value named mainval (a global
variable, in this case), multiply it by two, and print the new value to the
output window.
A second button is also created. The format is the same, but this one is
labeled Decrease, will call the decrease_value function, and is at the
left of the screen. The decrease_value function is just like the increase_
value one, but it divides by two instead of multiplying by two.
The last part of the code, in the main part of the code, will start the event
handler. Specifically, there are lines to set up TK. Note that the class we
just created is going to be the one defining our window and then starting
the event managerthats the final line in the code. If we run this code,
we see a window come up with two buttons, doing just what we said.
Reading
Exercises
1 Using the pyglet library, write a program that draws a window and draws an
image wherever a mouse is clicked.
2 Using Tkinter, create a window with a single button. Each time the button is
pressed, some phrasesuch as Hello!should be printed several times,
once more than the previous one. So, the first press should print Hello!
once, the second press should print Hello! twice, etc.
170
16 Lecture 16
Visualizing Data and Creating Simulations
[ Data Visualizations ]
One of the best packages to create visualizations of data is matplotlib.
It has a very wide range of capabilities and is probably the most well-
known and popular Python package for creating plots, graphs, and
charts from data.
The first step is to install matplotlib. One option is to use pip. If pip is
installed, you should be able to go to your Python directory in the
command line and type python m pip install matplotlib. If you arent
using pip, you can find out the details of how to install matplotlib at the
matplotlib.org website. Installing matplotlib will require installing several
other libraries, too.
With matplotlib installed, lets start by making a basic display. Well first
import pyplot from matplotlib. Then, well use the pyplot.axes function,
which basically says that were going to have a data plot that uses axes.
We could provide parameters to specify the appearance and range
of these axes, but we dont provide any parameterswe can just use
the defaults.
| 171
Finally, once weve created a data plot, well need to show it, so we
call show.
0.8
If we run this, we see that matplotlib
has created a plot with axes in the 0.6
range of 0 to 1 in both x and y. And it 0.4
includes some graphical tools at the
bottom that let you interact with the 0.2
chart by zooming in, moving around, 0.0
or saving it. 0.0 0.2 0.4 0.6 0.8 1.0
For matplotlib, you can see the key plotting commands if you follow
the link at the top of the page to pyplot. That gives you a whole list of
commands provided in pyplot, and if you click on each of them, it will give
you a more detailed description of what the command does and what
parameters it takes in.
The plot command can take in few lists. The first one gives all the
x-values. The second one gives all the y-values. In this case, were using
the x-values from 0 to 5, and then for the y-values, were using the square
of the x-values.
The following is a more compact version that makes the same plot. Notice
that instead of manually making the lists, we made a list of x-values using
the range command. Then, we built a list of y-values by going through
the x-values and appending the square of that x-value onto the list.
Also, notice that were just importing functions from pyplot to make things
simpler. Instead of having to write pyplot in front of each, we can write
plot, axis, or show directly. And if we run this, we get the same results
we had with the previous case.
There are many ways to improve and change graphs. There are many
options in the mathplotlib modulenot only for how to do line plots, but
also for numerous other types of charts and graphs. Its relatively easy
to show many different graphical representations of data, and once you
create the representation, its also easy to incorporate graphical output
into any larger program.
| 173
[ Simulations ]
Beyond visualizing the data we have, theres also data we would like
to have but dont. This brings us to simulations. When we talk about
simulations, we normally mix two ideas that are very related but distinct.
The first idea is a model, which tells us what the laws, rules, or processes
that we are trying to compute should follow. The actual simulation takes
the model and some set of conditions and uses it to determine how the
situation develops, usually over time.
The model is the most important thing about the whole simulation
process. If the model is incorrect, it doesnt matter how good the
computer is at performing the simulationit wont get the correct
answer. Its also very important to have the correct initial conditions.
Sometimes even tiny errors in initial conditions can have large
effects later.
Were also given what is called a time step (h). The idea is that were going
to take steps forward in time by that amount. That will let us determine a
new state at that new time. Well call this sequence of states Si and the
sequence of times ti. This will continue until weve reached the total time
we want to simulate, T.
Lets say that we want to see how an account accumulates interest over
time. Suppose that we use $1000 to buy a 10-year certificate of deposit
that earns 3% per year. We want to see how that grows over time.
|
1 74 L e ct u r e 16 V is ua li z i n g Data a nd Cr eating Simu l ation s
The code is as follows. Well use the variable time to keep track of time
(t) and the variable balance to keep track of our state, and well initialize
each of these to our starting conditionstime 0 and $1000 balance.
Each of these is going to be stored in a listtimelist or balancelistso
that we can keep track of growth over time.
We then have our simulation loop. In the loop, we increase the balance
by 3% and the time by 1. We store these in the time and balance lists.
And this continues until weve done this for 10 years. At the end, we print
everything out.
| 175
20000
15000
10000
5000
0
0 20 40 60 80 100
|
1 76 L e ct u r e 16 V is ua li z i n g Data a nd Cr eating Simu l ation s
Were going to start by changing the way we compute the change per
year. Were going to follow the principle of abstraction to make a separate
function that will calculate how the investment will increase (or decrease)
in any particular year.
We could modify our code to pick a random rate of return each year
for that investment. We import the random module. We then use the
uniform command to pick a random rate in between some maximum and
minimum. Lets say that our rate of return will be between 0% and 6%.
import random
from matplotlib.pyplot import plot, show
def ChangeInBalance(initial_balance):
rate = random.uniform(0.0, 0.06)
return initial_balance*rate
#Set initial conditions
time = 0
balance = 1000
#Set list to store data
timelist=[time]
balancelist=[balance]
while (time < 10):
#Increase balance and time
balance += ChangeInBalance(balance)
time += 1
#Store time and balance in lists
timelist.append(time)
balancelist.append(balance)
#Output the simulation results
for i in range(len(timelist)):
print("Year:", timelist[i], " Balance:", balancelist[i])
plot(timelist, balancelist)
show()
| 179
We can run this code and see what the results would be. Every time we
run the code, we get a different result. Over the 10 years, the results tend to
come out pretty similarly, because the variations will tend to average out.
Were going to get rid of the lists of the balances year by year and only
store the final balances in a list. Well also generalize things so that the
number of years in the simulation and the total number of simulations are
single variables that are easy to change.
We still start with our function that computes the change in balance. Well
then have a loop for however many simulations we need. In each of them,
well start at time 0, and with a balance of $1000, and simulate for a few
years, just like before. Well store the final balance into the final balances
array. After this loop, we can print out all the final balances we found.
import random
def ChangeInBalance(initial_balance):
rate = random.uniform(0.0, 0.06)
return initial_balance*rate
number_years = 10
number_sims = 100
final_balances = []
for i in range(number_sims):
#Set initial conditions
time = 0
balance = 1000
while (time < number_years):
#Increase balance and time
balance += ChangeInBalance(balance)
time += 1
|
1 80 L e ct u r e 16 V is ua li z i n g Data a nd Cr eating Simu l ation s
final_balances.append(balance)
#Output the simulation results
for i in range(number_sims):
print("Final Balance:", final_balances[i])
Given the results of all those runs, we can replace a simple printout of
the values with a histogram to plot the results. The hist command in
matplotlib will take in a list of resultsthe final balances, in this caseand
plot a histogram. We set the number of bins in this case equal to 20, and
well run 10,000 experiments.
import random
from matplotlib.pyplot import hist, show
def ChangeInBalance(initial_balance):
rate = random.uniform(0.0, 0.06)
return initial_balance*rate
number_years = 10
number_sims = 10000
final_balances = []
for i in range(number_sims):
#Set initial conditions
time = 0
balance = 1000
while (time < number_years):
#Increase balance and time
balance += ChangeInBalance(balance)
time += 1
final_balances.append(balance)
#Output the simulation results
hist(final_balances, bins=20)
show()
| 181
800
To understand overall
performance, we can 600
Readings
Exercise
Imagine that you are rolling 3 dice and are interested in the sum of those dice.
Use a Monte Carlo simulation to simulate 10,000 rolls of 3 dice. Use matplotlib
to plot a histogram of the results.
182
17 Lecture 17
Classes and Object-Oriented Programming
[ Object-Oriented Design ]
An object-oriented approach differs from top-down and bottom-up
approaches, which are both task-oriented designs. They tend to focus
on the task and how to accomplish the task, either by decomposing the
task into more basic tasks or building up from existing tasks we already
know how to perform. In computer science terms, both approaches are
focused on operations and bundling those operations into more and
more sophisticated functions.
[ Creating Classes ]
Lets say that we want to write some software that deals with a bank
account. First, we want to think about the types of things we need to know
about the bank account and the types of things wed like to do with it.
The main thing you need for a bank account is the balancehow much
money is in the account. You might want to deposit money, or withdraw
money, or maybe just check on how much is in the account.
When were defining a class, we start with the word class, followed by
the name of the class that we want to use. In this case, were defining
BankAccounts, so well name the class BankAccount, which is followed
by a colon. Then, everything in the class definition will be indented from
there. The indentation shows that this is the stuff that belongs to that
class. Our bank account needs to keep track of the current balance, so
that is a data item we have. Indenting in, we call this data item balance,
and we start out by setting it to 0.
class BankAccount:
balance = 0.0
Lets see how we use these classes that we define. First, we can create
an instance of the class. Each instance of a class is known as an object.
We create an object thats an instance of a class by writing the class
|
1 84 L e ct u r e 17 C las s e s and Obj ect- Or iented Pr ogr a mmi n g
class BankAccount:
balance = 0.0
my_account = BankAccount()
print(my_account.balance)
OUTPUT
0.0
In Python, some attributes can be set to apply equally across all members
of a class, so that every object has that attribute, while other attributes
can be defined individually for only some objects. In the previous
example, balance was an attribute across the entire classin fact, it
was the only attribute of the class.
| 185
Lets look at our code again. First, we can assign values to the members
of an object. After creating my_account, we can set my_account.balance
to 100. Then, if we print out my_account.balance, it is 100.
class BankAccount:
balance = 0.0
my_account = BankAccount()
my_account.balance = 100.0
print(my_account.balance)
OUTPUT
100.0
class BankAccount:
balance = 0.0
my_account = BankAccount()
your_account = BankAccount()
my_account.balance = 100.0
print(your_account.balance)
OUTPUT
0.0
|
1 86 L e ct u r e 17 C las s e s and Obj ect- Or iented Pr ogr a mmi n g
[ Mutable Data ]
Lets say that we want to keep track of the deposits that were made
to the bank account. So, in a bank account, we want to have a list of
deposits made. Well add a new attribute to the BankAccount class,
called deposits, and initialize it to be an empty list. So, BankAccounts
now has two attributes: balance and deposits.
class BankAccount:
balance = 0.0
deposits = []
class BankAccount:
balance = 0.0
deposits = []
checking_account = BankAccount()
checking_account.deposits.append(100.0)
print(checking_account.deposits)
OUTPUT
[100.0]
class BankAccount:
balance = 0.0
deposits = []
checking_account = BankAccount()
savings_account = BankAccount()
checking_account.deposits.append(100.0)
print(savings_account.deposits)
OUTPUT:
[100.0]
One way around this is to reset the value of the attribute for a particular
object. In the following, we set the value of checking_acount.deposits
to be an empty list. This creates a new empty list and sets the value of
deposits in the checking account to that list. The deposits attribute
of the savings_account still points to the original empty list, and if
we had other instances of BankAccount, they would, too. But now
checking_account has its own deposits list to work with, separate from
the rest. So, when we add 100 to the checking_account.deposits list, the
savings_account list is unchanged.
|
1 88 L e ct u r e 17 C las s e s and Obj ect- Or iented Pr ogr a mmi n g
class BankAccount:
balance = 0.0
deposits = []
checking_account = BankAccount()
savings_account = BankAccount()
checking_account.deposits = []
checking_account.deposits.append(100.0)
print(savings_account.deposits)
OUTPUT:
[]
This works, but we could avoid this problem if we could just create a list
to begin with that was separate for each object. In fact, there is a better
way to approach the issue of attributes.
[ Methods ]
In a Python class, we can have two types of attributes: class variables
and instance variables. Everything weve seen so far is a class variable
that is, its one variable defined for the class. When we create an object
that is, when we create an instance of the classthat instance will get its
own versions of the class variables.
But any initial values set in the class are going to be shared across
all instances, which leads to problems with mutable data types. The
alternative is to create instance variables, which are created separately
for each instance of the class. With instance variables, we never need
to worry about the changes to one object inadvertently affecting the
attributes of a different object.
The init method gets defined much like a regular function, but its inside
the class definition. The init function also has syntax that differs in a few
ways from other functions: It starts with a double underscore, then init,
and then another double underscore. It will take one parameter. Then,
you have the colon, and the function definition is indented from there. In
this case, well have one line in the init function: self.deposits = [].
class BankAccount:
balance = 0.0
def __init__(self):
self.deposits = []
This first parameter, self, in the init command is also a special one; its not
one you pass in, but its automatically filled in. The self parameter refers
to the current instance of the object. Because of the self parameter, we
have a way of clearly referring to things within this particular instance of
an object. So, if we write self.balance, we mean the balance in this one
instance.
class BankAccount:
balance = 0.0
def __init__(self):
self.deposits = []
checking_account = BankAccount()
savings_account = BankAccount()
checking_account.deposits.append(100.0)
print(savings_account.deposits)
OUTPUT:
[]
Readings
Exercises
For exercises 1 through 3, assume that you have the following class to keep
track of inventory.
class Inventory:
item = ""
barcode = 0
quantity = 0
price = 0.00
sales = 0.00
def __init__(self, product, bar, pr):
self.item = product
self.barcode = bar
self.price = pr
def changeprice(self, newprice):
self.price = newprice
def sell(self, n):
self.quantity -= n
self.sales += self.price*n
def restock(self, n):
self.quantity += n
3 Write a method, print, that will print out information about all the information
about the inventory. For example, calling widget.print() would print out all
the information about name, bar code, etc.
Imagine that you wanted a class to keep track of movies youve watched. You
will want to keep track of the name of the movie, the genre, and a numerical
rating of how much you liked it.
4 Define a class with attributes for these three characteristics, with some
default values.
5 Write a constructor method that takes in values for all three attributes as
parameters.
6 Write code that constructs a list of movies by asking a user for the appropriate
information, until the user enters a movie with rating less than 0.
193
Lecture 18 18
Objects with Inheritance and Polymorphism
[ Inheritance ]
Imagine that we have a program that were going to use to keep track of
statistics for different players on a sports team. Often, players with different
positions will have different statistics that are relevant to them. In football,
players on offense will have different statistics than those who play defense.
For the position of running back, we would want the players name and
team, as well as the number of rushes and rushing yards gained.
With just these two positions, one possibility would be to create two
classes. We could create a Quarterback class (which would have
attributes for name, team, pass attempts, completions, and passing yards)
and a RunningBack class (which would have attributes for name, team,
rushes, and rushing yards).
|
1 94 L e ct u r e 18 Ob je cts with Inh er ita nce a nd P oly morphi sm
Notice that both quarterback and running back share some attributes.
They both have the players name and the players team. In fact, this
would be true for all the various positions we might want to define.
So, lets imagine a different organization. Lets say that we have some
base type, which well call FootballPlayer, which will have all the
attributes common across the various types of players. In this case, thats
the players name and the team. We can then define a Quarterback as a
type of FootballPlayer, with some additional attributes: passes attempted,
completions, and passing yards. Likewise, a RunningBack is also a type of
FootballPlayer with some additional attributes: rushes and rushing yards.
What weve just seen is called inheritance. You can think of the football
player as the parent. Then, the quarterback and the running back are
children. The children inherit the characteristics of the parent. In this
case, the children inherit the name and team attributes defined for the
football player.
class FootballPlayer:
name = "John Doe"
team = "None"
class Quarterback(FootballPlayer):
pass_attempts = 0
completions = 0
pass_yards = 0
class RunningBack(FootballPlayer):
rushes = 0
rush_yards = 0
So, we have our FootballPlayer class with a name and team defined, and
we set the values to be John Doe for the name and None for the team.
| 195
With those classes defined, we can then create an instance of a class. Lets
say that we create a player, called player1, that is a Quarterback. We can
print the players name. Becuase we didnt set the name, it uses whatever
the default name was for all football players, which we said would be John
Doethat is, the quarterback has a name attribute because its parent
class, FootballPlayer, had a name attribute. We can also print out the
pass_yards attribute, which is 0, just like we initialized it to be.
player1 = Quarterback()
print(player1.name)
print(player1.pass_yards)
OUTPUT:
John Doe
0
Lets say that instead of a quarterback, we had said that player1 was a
RunningBack. Wed still have the name attribute, because a running back
is a child of the FootballPlayer class, and FootballPlayer has a name
attribute. However, if we tried to print off the pass_yards, wed get an
error. Pass yards were defined only for the Quarterback class.
player1 = RunningBack()
print(player1.name)
print(player1.pass_yards)
OUTPUT:
John Doe
AttributeError: 'RunningBack' object has no attribute 'pass_
yards'
|
1 96 L e ct u r e 18 Ob je cts with Inh er ita nce a nd P oly morphi sm
player1 = Quarterback()
player1.name = "John"
player1.team = "Cowboys"
player1.pass_attempts = 10
player1.completions = 6
player1.pass_yards = 57
player2 = RunningBack()
player2.name = "Joe"
player2.team = "Eagles"
player2.rushes = 12
player2.rush_yards = 73
Its not just attributes that can be inherited. We can also inherit methods.
In the following, weve augmented our classes to include some methods.
For the FootballPlayer class, well define a method, printPlayer, that
prints out the name and team of the player. Well also add some methods
for the Quarterback and RunningBack classes to compute some statistics
specific to those positions. For a quarterback, that will be the completion
rate and yards per attempt, and for the running back, that will be the
yards per rush.
class FootballPlayer:
name = "John Doe"
team = "None"
years_in_league = 0
def printPlayer(self):
print(self.name+" playing for the "+self.team+":")
class Quarterback(FootballPlayer):
pass_attempts = 0
completions = 0
pass_yards = 0
| 197
def completionRate(self):
return self.completions/self.pass_attempts
def yardsPerAttempt(self):
return self.pass_yards/self.pass_attempts
class RunningBack(FootballPlayer):
rushes = 0
rush_yards = 0
def yardsPerRush(self):
return self.rush_yards/self.rushes
We can go back to our two players that we defined earlier, and then we
can call the methods for these players. Notice that we can call printPlayer
for both player1 and player2. Because the method is defined in the
parent, its automatically inherited by the children. We can also call those
statistics methods that are specific to each of the children classes.
class FootballPlayer:
name = "John Doe"
team = "None"
years_in_league = 0
def printPlayer(self):
print(self.name+" playing for the "+self.team+":")
class Quarterback(FootballPlayer):
pass_attempts = 0
completions = 0
pass_yards = 0
def completionRate(self):
return self.completions/self.pass_attempts
def yardsPerAttempt(self):
return self.pass_yards/self.pass_attempts
class RunningBack(FootballPlayer):
rushes = 0
rush_yards = 0
def yardsPerRush(self):
return self.rush_yards/self.rushes
|
1 9 8 L e ct u r e 18 Ob je cts with Inh er ita nce a nd P oly morphi sm
When people discuss inheritance, there are different terms used for the
different classes. Sometimes, we call the parent class the base class,
and we call the children derived classes. Other times, we call the
parent a superclass and the children subclasses. All of these terms
refer to the same thing.
Just like in biology, where you can inherit traits from more than just one
parent, classes can inherit properties from multiple parents. But for the
most part, you should stay away from multiple inheritance. It can make
your code more confusing to follow. Plus, its very rare that multiple
inheritance is actually the right solution to your problem.
[ Polymorphism ]
The third main feature of object-oriented programming is polymorphism,
which means that a function, or method, can take on many different forms,
depending on the context.
Lets return to our example with the football players. Lets imagine that
we want to assess whether each player is good or not, according to
some measure we devise. Clearly, the way we determine whether a
quarterback is good at throwing is different from the way we determine
whether a running back is good at running.
So, lets say that wed like to have a method called isGood that
returns True or False, depending on whether a player is good or not,
according to whatever method we devise. We can augment our earlier
definitions to include this function.
Lets put a function isGood in the FootballPlayer class. Its not possible
to determine whether a generic football player is good or not, given that
all we have is a name and team. So, in this case, well print out some sort
of error message, saying that we called a function that wasnt defined.
| 199
class FootballPlayer:
name = "John Doe"
team = "None"
years_in_league = 0
def printPlayer(self):
print(self.name+" playing for the "+self.team+":")
def isGood(self):
print("Error! isGood is not defined!")
return False
Lets assume we have two players that weve created, just like before.
Were now going to create a playerlist, and well add both player1 and
player2 into that list. Then, well go through each of the players in the
list, using a for statement. For each player, well print out the player
information using the printPlayer method, and then well call isGood and
print out whether the player is a good player or not a good player.
player1 = Quarterback()
player1.name = "John"
player1.team = "Cowboys"
player1.pass_attempts = 10
player1.completions = 6
player1.pass_yards = 57
player2 = RunningBack()
player2.name = "Joe"
player2.team = "Eagles"
player2.rushes = 12
player2.rush_yards = 73
playerlist = []
playerlist.append(player1)
playerlist.append(player2)
for player in playerlist:
player.printPlayer()
if (player.isGood()):
print(" is a GOOD player")
else:
print(" is NOT a good player")
|
200 L e ct u r e 18 Ob je cts with Inh er ita nce a nd P oly morphi sm
OUTPUT:
John playing for the Cowboys:
Error! isGood is not defined!
is NOT a good player
Joe playing for the Eagles:
Error! isGood is not defined!
is NOT a good player
When we run this, we get error messages printed. Thats what wed
expect.
class FootballPlayer:
name = "John Doe"
team = "None"
years_in_league = 0
def printPlayer(self):
print(self.name+" playing for the "+self.team+":")
def isGood(self):
print("Error! isGood is not defined!")
return False
class Quarterback(FootballPlayer):
pass_attempts = 0
completions = 0
pass_yards = 0
def completionRate(self):
return self.completions/self.pass_attempts
def yardsPerAttempt(self):
return self.pass_yards/self.pass_attempts
def isGood(self):
return (self.yardsPerAttempt() > 7)
| 20 1
class RunningBack(FootballPlayer):
rushes = 0
rush_yards = 0
def yardsPerRush(self):
return self.rush_yards/self.rushes
def isGood(self):
return (self.yardsPerRush() > 4)
Using the exact code as we had before, if we run it now, we get an output
without the error messages.
player1 = Quarterback()
player1.name = "John"
player1.team = "Cowboys"
player1.pass_attempts = 10
player1.completions = 6
player1.pass_yards = 57
player2 = RunningBack()
player2.name = "Joe"
player2.team = "Eagles"
player2.rushes = 12
player2.rush_yards = 73
playerlist = []
playerlist.append(player1)
playerlist.append(player2)
for player in playerlist:
player.printPlayer()
if (player.isGood()):
print(" is a GOOD player")
else:
print(" is NOT a good player")
OUTPUT:
John playing for the Cowboys:
is NOT a good player
Joe playing for the Eagles:
is a GOOD player
|
202 L e ct u r e 18 Ob je cts with Inh er ita nce a nd P oly morphi sm
When the Python compiler sees the call to isGood, it first looks at the
definition of isGood in the child class. If theres not a definition of that
method there, it will look at the parent to see if the method is defined there.
Pickle is a module that lets you read and write data other than strings more
easily. Pickle lets us read and write data from a file in binary format (which
is how most files you encounter every day are stored, from images to
word-processing files). And it works for even more data types than JSON.
Readings
Exercises
class Game:
name = ""
numplayers = 0
a) A video game class Videogame that has the same attributes as a Game
and also keeps track of the platform that it is.
b) A board game class Boardgame that has the same attributes as a Game
and also has a number of pieces and a size (stored as a list of two numbers:
a length and a width).
|
2 04 L e ct u r e 18 Ob je cts with Inh er ita nce a nd P oly morphi sm
def print(self):
print(self.name)
print("Up to ", self.numplayers, "players")
3 Write code to create a video game, and then print its information out.
4 How would you use the pickle module to save the video game from exercise
3 into a file, Game.dat?
Lecture 19 19
Data Structures: Stack, Queue,
Dictionary, Set
[ Data Structures ]
Classes and objects are great at tying together different types of data,
but object-oriented design is focused more on bundling different types
of data together. Data structures are focused on how to organize large
amounts of the same type of data.
One of the simplest data structures is what Python calls a list, and other
languages call an array, which has an orderis linearand lets us string
many distinct things together in sequence (so its sequential).
But stringing things together is not the only way we could organize them.
We could lay them out in a grid, for example. Either a list thats sorted, or
a heap, would make it much easier to get the largest (or, alternatively, the
smallest) value.
Data structures can also be nonlinear and nonsequential. Maybe the data
would be better organized around memberships, or geographic location,
or a bunch of special-purpose keys associated with each object.
|
206 L e ct u r e 19 Data St r u ct u r es: Stack, Qu eu e, Dict ionary, Se t
There are many methods for organizing large amounts of data. For an
army, organizing into a hierarchical structure might be great for helping
make sure orders get followed. But that might not be a great way of
organizing if the goal were to come up with creative ideas. In other
words, organization affects operations.
[ Stacks ]
Imagine that we have a stack of books. Lets assume that they are heavy
books, such that we can only hold one at a time. If we have a stack of
these books, there are basically just two things we can do: add a book to
the stack or take the top book off of the stack.
The stack data structure is basically just this, only with data instead of
books. If we add something new onto the stack, well call the operation a
push; if we remove the top item from the stack, well call it a pop.
Lets see how this would work with a list and some of the commands
already available for lists. First, just to extend the book analogy, lets
assume that weve organized our book data into a book class, where we
store a title and author per book. We also create three specific books: a
long book, medium book, and short book.
class book:
title = ""
author = ""
long_book = Book()
long_book.title = "War and Peace"
long_book.author = "Tolstoy"
medium_book = Book()
medium_book.title = "Book of Armaments"
medium_book.author = "Maynard"
short_book = Book()
short_book.title = "Vegetables I Like"
short_book.author = "John Keyser"
| 20 7
Our stack of books is going to be represented using a list. The first book
in the list is the book on the bottom of the stack, and the last book in the
list is the top book on the stack. So, to push a book onto the book stack,
we would just use the append command on the stack of books.
We start out with an empty list, which means that we have an empty
stack. We then stack the books on top of each other. Lets say that we
want to put the medium book down first. Well append the medium book
to the list. Next, we might want to stack the short book, so we append it.
Finally, we stack on the long book.
book_stack = []
book_stack.append(medium_book)
book_stack.append(short_book)
book_stack.append(long_book)
In memory, this set of books is treated as a list, with the medium, short,
and long books listed in order. But we are supposed to think of it
conceptually as a stack, with the medium book at the bottom, then the
short, and then the long book.
Now lets say that we want to pop the top book off of the stack. Lists have
a built-in method named pop, which will remove the last item from a list
and return it. In this example, we assign the result of the pop to a variable
next_book, which now refers to the long book, because that was the
first one on the stack. If we were to print the title and author of next_book,
we would see the title and author of the long book. If we were to pop
another book off the stack, the next one would be the short book.
book_stack = []
book_stack.append(medium_book)
book_stack.append(short_book)
book_stack.append(long_book)
next_book = book_stack.pop()
print(next_book.title+" by "+next_book.author)
|
208 L e ct u r e 19 Data St r u ct u r es: Stack, Qu eu e, Dict ionary, Se t
OUTPUT:
War and Peace by Tolstoy
Stacks give us whats referred to as last in, first outthat is, the last thing
pushed is the first thing popped.
[ Queue ]
What if we want a first in, first out process? This is what you encounter
when people queue up to stand in linethe first one in line is the first
one handled.
Python makes this really easy. The pop command can take a parameter,
indicating which element gets taken out of the list. If no parameter is
given, it defaults to the final element, as we saw with stacks. But for
the first element in the list, we just pass in a 0, and the first element
is removed.
Lets look at some code to get the idea. Instead of a stack of books
that we are piling up, we have a queue of books. Maybe we buy books
one at a time and want to read them in the order we bought them,
for example.
| 20 9
We can build our queue like we built the stack. We start with an empty list
we call book_queue. Then, we add books to book_queue by calling
the append methods. This creates the exact same list as we had in the
earlier case. The only difference is in how we think about itas a queue,
versus a stack.
Just like we could pull one item off of the stack, we can also pull one item
off of a queue, by calling the pop method on book_queue. Notice the
parameter 0 when we call pop. So, this call to pop would pull off the next
book in the queue. When we finished that book, we could call pop with
parameter 0 again to get the next book in the queue.
book_queue = []
book_queue.append(medium_book)
book_queue.append(short_book)
book_queue.append(long_book)
next_book = book_queue.pop(0)
[ Hash Tables ]
Another really useful data structure is called a hash table, which works
by mapping large data values into a smaller set of indices.
To see how helpful it can be to map like this, imagine that you have a set
of friends and they all have phone numbers, but all of them have blocked
caller ID. So, when they call, you dont know whose phone number
belongs to whom. Youd like to be able to enter a phone number and find
who it is.
It would not be a good idea to create a giant list, where the list element
number corresponded to the phone number. So, if Sue Smith had phone
number (135) 246-0987, then element number 1352460987 would have
the value Sue Smith. The problem is that most of the list is going to
be empty.
|
21 0 L e ct u r e 19 Data St r u ct u r es: Stack, Qu eu e, Dict ionary, Se t
0000000000
0000000001
0000000002
...
1352460987 Sue Smith
...
8647531234 John James
...
9999999999
Heres where a hash table comes in. Instead of a list of 10 billion elements,
lets instead take a list of just 100 elements. Well store each person in a
slot that corresponds to just the last two digits of the phone number. So,
Sue Smith, because her phone number ends in 87, would go into the list
at index 87.
00
01
02
...
34 John James (8647531234)
...
87 Sue Smith (1352460987)
...
99
| 211
This is a much more compact representation than the first list. But what if
two people have the same last two digits to their phone number?
Imagine that Bill Brown comes along with the phone number (808) 424-
1287. Hell end up in the same position as Sue Smith. To resolve this, we
can use chainingjust making a list of everyone in that slot. So, when we
get to a slot, we cant just pull out the name; instead, we have to look at
everyone in that list. But its still much more practical than the giant list.
00
01
02
...
34 John James (8647531234)
...
Sue Smith (1352460987),
87
Bill Brown (8084241287)
...
99
Hash tables can get much more complicated than this, but fortunately,
theres a tool in Python that basically implements hash tables for us, and
we dont even have to come up with our own hash function. In Python,
this tool is called dictionary, and the Python command to create a new
dictionary is called dict.
|
2 1 2 L e ct u r e 19 Data St r u ct u r es: Stack, Qu eu e, Dict ionary, Se t
[ Sets ]
A different way that hash tables can be used is accessible with another
built-in Python data structure: the set. A set is just a collection of items,
but it will be stored using a hash table instead of a list so that it does
mathematical set operations and checks set membership very quickly.
OUTPUT:
Yes!
Note that we could get the exact same effect by using the set command,
as follows, instead of the curly braces. The set command takes in a list as
a parameter, and all the elements of the list get put into the set. One big
advantage of the set command over curly braces is that the set command
lets us create an empty set. If we just have curly braces, with nothing
inside, that will create an empty dictionary, not an empty set. So, if we
want to start with an empty set and gradually add things to it, we have to
use the set command.
| 213
OUTPUT:
Yes!
Sets have some additional operations defined on them that can be very
useful. First, sets have an add method defined. To add a new element to
a set, just call add as a method on that set and pass in the new element
as a parameter. In the following example, we have a set of friends from
work, and we add Kathy to that list. You can see from the output that
Kathy is added to the set. Likewise, there is a remove method defined.
In the example, we remove Fred from the list.
OUTPUT:
{'Fred', 'Eric', 'Sue'}
{'Fred', 'Eric', 'Sue', 'Kathy'}
{'Eric', 'Sue', 'Kathy'}
Readings
Exercises
1 Assume that the Stack class is defined as in the lecture. What would be the
output of the following code?
namestack = Stack()
namestack.push("John")
namestack.push("James")
namestack.push("Joseph")
person = namestack.pop()
print(person)
person = namestack.pop()
print(person)
person = namestack.pop()
print(person)
2 Assume that the Queue class is defined as in the lecture. What would be
the output of the following code?
namequeue = Queue()
namequeue.enqueue("John")
namequeue.enqueue("James")
namequeue.enqueue("Joseph")
person = namequeue.dequeue()
print(person)
person = namequeue.dequeue()
print(person)
person = namequeue.dequeue()
print(person)
| 215
cas
t = {"Cardinal Ximenez" : "Michael Palin", "Cardinal
Biggles" : "Terry Jones", "Cardinal Fang" : "Terry
Gilliam"}
cast["customer"] = "John Cleese"
cast["shopkeeper"] = "Michael Palin"
print(cast["shopkeeper"])
print(cast["Cardinal Ximenez"])
print(cast["Cardinal Fang"])
primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}
teens = set([13, 14, 15, 16, 17, 18, 19])
print(primes - teens)
print(primes & teens)
print(primes | teens)
print(primes ^ teens)
216
20 Lecture 20
Algorithms: Searching and Sorting
[ Algorithms ]
Writing a program is essentially the same thing as writing an algorithm.
The difference is that a program is a specific, concrete implementation
in a particular programming language. An algorithm can be thought of
as a more general description thats not necessarily tied directly to a
particular programming language.
1. Step 1
2. Step 2
3. If (condition)
a. Step 3
b. Go to Step 1
4. Else
a. Step 4
[ Searching ]
To illustrate how algorithms work and how theyre implemented, were
going to start with one of the simplest general algorithms: a search. Well
assume that we have some list and want to find whether a particular
value is in the list or not. Keeping with the way algorithms are usually
developed, we wont worry about exactly what were searching for.
Lets assume that we know nothing about this listits just a collection
of values. We have a value that were looking for, and we want to return
either True or False in this case. Lets see how we might write some
pseudocode for this algorithm.
There are two pieces of data we need to run our algorithm. First, we
need the list itself, which well designate as L. Next, well need the value
were looking for, which well designate as v. The output, the result of
our algorithm, is going to be a Boolean value: either True if v is in L or
False if its not. If were writing pseudocode, we want to be clear, at the
beginning, about whats needed for input to the algorithm and what the
resulting output will be.
|
21 8 L e ct u r e 2 0 A lg o r it h ms: Sea r ch ing a nd Sorting
Next, we need to outline the steps to be taken. In this case, the idea
is simplewe are just going to go through each element of the list,
checking to see if theres a match. This is called a linear search: Were just
going down the line, looking at each item, one at a time.
If we find a match, we return True, but if we get to the end of the list and
havent found anything, we return False. We can write the pseudocode
step by step. We first set a value, i, to be the first index in the list. Then, we
go through a loop, as long as i is still within the range of the list. In each
iteration, we compare the ith element with our value we are looking for, v.
If it matches, we return True and are done. If not, we increment i. If we
eventually reach the end of the loop, that means we never encountered
the value v in the list, so we return False.
Input:
List of values: L
Value to find: v
Output:
True if v is in L, False otherwise
Its pretty straightforward to put this algorithm into code. We can create
a function that implements this algorithm almost exactly. We take in a list
and a value as input. We initialize the index i to 0 and have a loop until
i is no longer less than the length of the list. We have an if statement to
compare element i of the list with our value, returning True if they match
or incrementing i if they dont.
The following example shows how we could use this. We have a list,
favorite_foods, and we call the function we just created on two values.
When we look for a value that is in the list, we get a True back, and
when we look for one not in the list, we get a False.
| 219
OUTPUT:
True
False
There is a built-in function within Python that implements this algorithm for
us: the in command. So, when we ask whether some value is in some
list, Python is doing exactly what we just showed in the backgroundits
just looping over all the elements to see if the one we want is there.
Lets assume that instead of having a list of values in any order, our list
was sorted, from smallest to largest. A command to sort the list would be
to call a sort method on the list. But for purposes of writing an algorithm,
we can just assume that the list has been sorted.
There is a better way to write this routinea way to make use of the fact
that our input is sorted to search for the value more efficiently.
How might we write the pseudocode for this? The input and output is
the same as what we had before. We take in a list of values and a value
to find and return True or False. The only difference is that the list of
values is given in sorted order.
Now we need to describe the steps of our algorithm very precisely. Our
approach will be to gradually narrow down the range of options until we
find the one were looking for. So, at any point, we will have a maximum
and a minimum index of where the value might be. At the very beginning,
our maximum and minimum indices will be from 0 to the list size minus 1.
Well check those values to make sure its not matching them.
Input:
List of values IN SORTED ORDER: L
Value to find: v
Output:
True if v is in L, False otherwise
At this point, we know that the value we are looking for is somewhere
between item low in the list and item high in the list. We are going
to gradually narrow down low and high until either we find the point or
theres nothing left between low and high.
Input:
List of values IN SORTED ORDER: L
Value to find: v
Output:
True if v is in L, False otherwise
So, we are going to have a loop that continues as long as low is less than
high minus 1. Notice that we want to continue only as long as the high
and low indices are at least 2 apart so that theres some potential value
in between. Once the high and low values are next to each other, we
can quit the search, because there is no possibility of another value in
between them.
Notice that at each iteration of the loop, we still have that same
condition: The value were looking for is either between element low
and element high, or its not in the list. This conditionthis thing thats
the same every time we go through a loopis called a loop invariant.
When were designing an algorithm, it often is helpful if we can identify
such a loop invariant.
Now we need to decide whats done in each iteration of the loop itself.
We will compute a midpoint thats halfway between the high and low
point and check to see if it matches the value. Notice that when we
calculate the midpoint between low and high, we can do so by finding
the difference between low and high, dividing it by 2, and adding it to
the low. Notice that because were dividing by 2, we could end up with
a fractional value, which doesnt work for indices. So, we need to make
sure that we are doing an integer divisionkeeping only the quotient but
ignoring the remainder.
Input:
List of values IN SORTED ORDER: L
Value to find: v
Output:
True if v is in L, False otherwise
Notice that because we know the high and low values are at least 2 apart
from each other, high minus low divided by 2 is at least 1. So, the midpoint
is guaranteed not to be the same as low or the same as highit will be a
new index somewhere between low and high.
If the midpoint turns out not to be the actual value, we can at least use the
midpoint to narrow our range. Were faced with one of two possibilities:
either go forward with the range from low to midpoint or the range from
midpoint to high. We can decide which of these is the right sub-range
to continue with by comparing the value at the midpoint to the value
were searching for. If the value at the midpoint is less than v, it means
that we need to use the upper sub-range. So, we can set low to be the
midpoint. Going forward, well be looking between that midpoint and the
high index. On the other hand, if the value at the midpoint is greater than
v, it means that we should use the lower sub-range. So, we can set high
to be the midpoint.
Input:
List of values IN SORTED ORDER: L
Value to find: v
Output:
True if v is in L, False otherwise
Notice that our loop invariant is maintained. The value were looking for is
either between low and high or its not in the list at all.
Finally, if we finish the loop, it means that we narrowed in, and the value
we were searching for was not found. In this case, well return False.
| 223
Input:
List of values IN SORTED ORDER: L
Value to find: v
Output:
True if v is in L, False otherwise
OUTPUT:
True
False
When we run this code with a sorted list, we find that we correctly identify
when an item is in the list or not. Realistically, we want to make sure that
we tested this in a variety of situations.
[ Sorting ]
Linear and binary search are two of the simplest and most fundamental
algorithms. Some slightly more complex algorithms are sorts. There are
many different ways to sort, and different methods will work better or
worse in different circumstances.
Lets compare two basic sorts: selection sort and insertion sort.
thing we want is the smallest item, we look through all of our values and
find the smallest one. We put that into the first place. We then repeat that
process to find the next-smallest item and put that into the second place.
Each time, we have to look at all of our remaining items so that we can
select the one that is the smallest-remaining item. Thats where we get
the name selection sort.
If you directly sort the elements of the list, thats called an in-place sort.
In contrast, an out-of-place sort means that you create a new list thats
a copy of the original one. In this case, the original list stays unchanged,
while we also have a new, sorted, list to work with.
In the selection sort, we can sort in place by making sure that every
time we place a new element into its final position, we swap it with an
existing element.
Selection sort works, but it spends a lot of time going through the entire
unsorted list on every iteration. Insertion sort is a different approach that
can be much faster and simpler. For insertion sort, at iteration n, weve
sorted the first n elements. So, the only thing to do on each iteration is
add one more element into the right spot.
With insertion sort, we start with the first itemand only the first item.
When we take the second item, all we do is compare it with the first item
and insert it in whichever position is correct. We continue making iterations
in this way, where each time we take one more value and insert it into the
list of sorted items. Thats where we get the name insertion sort.
|
22 6 L e ct u r e 2 0 A lg o r it h ms: Sea r ch ing a nd Sorting
Readings
Exercises
We will show how to build another sorting routine: the bubble sort.
1 Write a function that takes in a list and an element number, i, and swaps
element i with element i+1.
one_bubble_pass:
Input:
List lst
Output:
Modified list, True if a swap was made, False if not
1. returnval = False
2. Loop over all elements except last one in lst
3. I
f lst[i] > lst[i+1] then swap elments i and i+1 and set
returnval = True
4. Return returnval
| 227
3 The bubble sort just keeps calling one_bubble_pass until no more swaps
can be made. Write a routine, bubblesort, that implements the following
pseudocode.
bubblesort:
Input:
unsorted List
Output:
sorted List
21 Lecture 21
Recursion and Running Times
def countdown(n):
print (n)
if n > 0:
countdown(n-1)
countdown (5)
| 229
OUTPUT:
5
4
3
2
1
0
Of course, there are other, better, ways to count down from n to 0thats
what loops are made for. But this idea of recursion is going to let us do
a few things that dont have such a nice non-recursive version, and it will
help us organize some of our programming so that even if we can find a
non-recursive solution, well have a tool for thinking about problems.
One of the main approaches that can rely on recursion is whats called
divide and conquer. The idea is that its easier to deal with two smaller
problems rather than one big one. But theres a more particular meaning
to the term divide and conquer in computing. When we use the term,
we mean that we are taking a large data set and dividing it into subsets
that we handle independently.
First, we have merge sort. Lets assume that were given some completely
unordered set of numbers. Were going to do three steps to get these
sorted. First, well divide the set of numbers in twousing the first half to
form one list and the second half to form the other list.
|
230 L e ct u r e 2 1 R e c ur s io n a nd Ru nning Times
The second step is to sort each of those lists. We can use the merge sort
routine to sort the lists, and the sorting process is an example of divide
and conquer. Were taking one large sorting problem and reducing it to
two small sorting problems that we solve recursively. Finally, theres a
merge stage, where well merge those two sorted lists into one bigger
sorted list. To merge, well work through both lists, pulling out the smallest
one left from whichever list.
Lets put all of that into pseudocode, which is a great intermediate step
for writing algorithms because it lets us specify the key ideas of the
steps without also needing to specify all the syntax at the same time.
In fact, less detail in pseudocode is sometimes better, because that
leaves the programmer more flexibility to determine how to implement
an item.
1. If n <= 1
a. Return L #a list of length 1 is already sorted
2. L1 = L(0:n/2-1) L2 = (n/2:n-1)
3. MergeSort(L1) MergeSort(L2)
4. L = Merge(L1, L2) #Merge will be defined separately
Like other sorts, well be taking in an unsorted list and returning a sorted
one. The actual routine will start out with a special case, though. Well
first check to see if we have a list of length 1, and if so, we just return that
listbecause if we have a list of length 1, its already sorted. Also, if our
list has only one element, we cant divide it into two lists, so the rest of
the routine isnt going to work.
We refer to this sort of special case check as a base case when we are
discussing recursion. A recursive routine that keeps calling itself has to
stop at some point, or it will go on forever. The point where it stops is the
base case.
| 231
If we have more than the base casethat is, if we have a list of 2 or more
elementswell go through our three steps. First, well form two lists, L1 and
L2, made from half of the original list. Well then sort each of those lists by
a recursive call to this very routine. Finally, well merge those lists together.
In the actual code, we define our function, mergeSort, and take the list
in as the parameter, L. Well store the length of L in a variable, n. First, we
handle the base case: If n is less than or equal to 1, we just return, because
the list is already sorted. Otherwise, well form our two shorter lists.
def mergeSort(L):
n = len(L)
if n <= 1:
return
L1 = L[:n//2]
L2 = L[n//2:]
mergeSort(L1)
mergeSort(L2)
merge(L, L1, L2)
return
Notice two things about the transition from pseudocode to Python syntax
in the next lines of code. First, were using the slicing operation to take a
subset of the lists, and were using n/2 as the splitting point. So, we can
write :n/2 for the first sublist and n/2: for the second sublist.
Second, in order for our code to specify that splitting point, n/2, we had
to use integer division, where we drop the remainder, to make sure that
we have an integer result for the index. That integer division is the double
slash, as opposed to the single slash for regular division.
Finally, we have a call to a merge routine, which takes two lists and
merges them into a third list.
[ Recursion: Quicksort ]
Another example of a recursive sorting routine is called quicksort,
because it works quickly on typical cases. In quicksort, the idea is
still divide and conquer, but the division is done differently than in
merge sort.
| 233
When we write pseudocode for quicksort, the input and output are just
like weve had before. Well take in an unordered list, and our output will
be a sorted list. For a recursive routine, we want to have a base case.
The base case will be just like in merge sortwe want to return if we
have a list of length 0 or 1.
Next, we pick the first element of the list, the pivot. Then, we form two lists, L1
and L2one with elements below the pivot and one with elements above.
We then recursively sort the two lists. This is our recursive call, where we
call the quicksort function from within the quicksort function. Once the lists
are sorted, we form our final list, by joining the two lists and the pivot.
1. If n <= 1
a. Return L #a list of length 1 is already sorted
2. pivot = L[0]
3. F
orm lists L1 and L2 of remaining elements less/greater than
pivot
a. Set empty lists L1 and L2
b. Loop through elements of L from 1 onward
1. I
f the element is less than the pivot, add it to L1,
otherwise add it to L2
4. QuickSort(L1) QuickSort(L2)
5. L = Join(L1, pivot, L2)
a. Clear L
b. Loop through L1, appending elements on to L
c. Append pivot to L
d. Loop through L2, appending elements on to L
|
234 L e ct u r e 2 1 R e c ur s io n a nd Ru nning Times
Lets use the pseudocode to write the code. The following is one way to
implement quicksort.
def quickSort(L):
#handle base case
if len(L) <= 1:
return
#pick pivot
pivot = L[0]
#form lists less/greater than pivot
L1 = []
L2 = []
for element in L[1:]:
if element < pivot:
L1.append(element)
else:
L2.append(element)
#sort sublists
quickSort(L1)
quickSort(L2)
#join the sublists and pivot
L[:] = []
for element in L1:
L.append(element)
L.append(pivot)
for element in L2:
L.append(element)
return
In the case of sort routines, Python has a built-in sort routine, which uses
a combination of a merge sort and an insertion sort. That built-in Python
sort is really efficient. In fact, that should usually be the version you use.
Most other languages will have some similar built-in sort function.
We also have to think about what part of the running time we care
about. Do we care about the best case, the worst case, or the average
case? Most of the time, computer scientists will analyze the worst case,
because they want to make sure that things dont behave too badly.
However, depending on the problem, we sometimes care about the best
case or average case.
The algorithm used in Pythons built-in sort() function has been cleverly
designed to do even better than each of the four algorithms weve
been using, at least most of the time. Pythons current algorithm uses a
combination of merge sort and insertion sort.
|
2 36 L e ct u r e 2 1 R e c ur s io n a nd Ru nning Times
Even though merge sort works better on large problems, insertion sort
works faster on smaller problems. The Python sort routine basically uses
merge sort for the overall problem, but once its dealing with sufficiently
small problems, it switches to insertion sort.
Readings
Exercises
def dosomething(lst):
if len(lst) == 0:
return 1
else:
return lst[0]*dosomething(lst[1:])
| 237
a) swap
b) one_bubble_pass
c) bubblesort
22 Lecture 22
Graphs and Trees
[ Graphs ]
Imagine that you live in a kingdom with five main cities: Rivertown,
Hillsview, Brookside, Lakeside, and Forrest City. Three of these cities
are connected to each other directly with roads. Lakeside is connected
only to Forrest City, and the only other road from Forrest City connects
it to Hillsview.
edge
Brookside Lakeside
In terms of writing code, graphs can have more than one representation.
For example, there is one that is more global and one that focuses on
adjacent neighbors. The more global option is to represent a graph as
two lists: one list of nodes and one list of edges.
Each node will contain information about itself. So, each of our city nodes
might contain just the name of a city, or each node could also contain
other information, such as city population, GPS coordinates for the city,
and so on.
An edge would have the names of the two nodesin this case, the two
cities that it connects. If its a weighted graph, the edge would also store
a weight, which in this case might be the length of that road, in miles or
kilometers.
Lets try to write code to support this. Well want two classes, one for
the nodes and one for the edges. Remember that a class helps us
encapsulate the stuff that goes together, so the node class should
incorporate the stuff in a node, and the edge class should incorporate
the stuff in an edge.
The node class will have a name for the city and a population. Well set
these with our initialization routine, which sets the instance attributes
_name and _pop. This is why we have the self reference; each
node can have a different name and population. We make sure that we
can access the name and population variables through two accessor
functions, getName and getPopulation.
|
2 4 0 L e ct u r e 2 2 G r a p hs a nd Tr ees
Likewise, our edge class would have the names of two cities, along with
the distance along the road. These are set with the init initialization
routine, which sets local instance attributes for city1, city2, and
distance. Then, we have a set of accessor functions to get each city
name, or the pair of city names, and the distance.
class node:
def __init__(self, name, population=0):
self._name = name
self._pop = population
def getName(self):
return self._name
def getPopulation(self):
return self._pop
class edge:
def __init__(self, name1, name2, weight=0):
self._city1 = name1
self._city2 = name2
self._distance = weight
def getName1(self):
return self._city1
def getName2(self):
return self._city2
def getNames(self):
return (self._city1, self._city2)
def getWeight(self):
return self._distance
The following is how we might set up our node list and edge list for the
city example. Well create our five cities, each with some population, and
add those to the city list. For example, we create a node for Rivertown
with a population of 100 and append it to the cities list.
cities = []
roads = []
city = node('Rivertown', 1000)
cities.append(city)
| 24 1
Well also create our roadsfive of them, in this caseand add them
to the road list. For example, we form an edge between Rivertown and
Brookside of length 100 and then append that edge to the roads list.
It would be simple to add another road between two cities, or another
cityjust create a new edge or node and append it on.
The global list of all the edges is probably most useful if you find yourself
regularly needing to look at all of the edges. Thats the approach
commonly used to represent geometric models, like you would have in
three-dimensional graphics.
The second approachthe adjacency list, where you keep a list of the
edges within each nodeis useful in most typical graph operations, such
|
24 2 L e ct u r e 2 2 G r a p hs a nd Tr ees
For any given graph, we can define a variety of graph algorithms. Some
of these algorithms are simple. For example, returning a list of all the cities
adjacent to one city is a very basic algorithm. The representation used to
store the graph will determine exactly how the algorithm will perform.
Graph algorithms let us analyze all kinds of things about the structure of
graphs. For example, the breadth-first search algorithm lets us analyze
how many degrees of separation there are between two people in a
social network.
With roads, we can usually travel the road in either directionwe just say
that those nodes are connected. Graphs like this are called undirected
graphs. A graph where people are friends is undirected: If person A is
friends with person B, then person B is also friends with person A.
However, we can also have cases where two things are linked, but its
not an equal connection between the two sides. For example, imagine
cities connected by airline routes. Not all airline routes fly round-trips.
Sometimes, a plane will fly from city 1, then to city 2, then to city 3, and
then back to city 1. In this case, the edges go from one city to another, but
not necessarily the other way around.
| 24 3
So, there should be an edge from city 1s node to city 2s node, and one
from city 2s node to city 3s node, and one from city 3s node back to
city 1s node. These are called directed edges, and the resulting graph,
made up of directed edges, is called a directed graph, or digraph.
Web pages and the links between them form a directed graph. If web
page A has a link to web page B, theres not necessarily one from web
page B back to web page A.
[ Trees ]
A connected, undirected graph that does not contain a cycle is called a
tree. Trees are such a useful structure that a whole set of algorithms have
been developed just for trees.
Figure 22.a
|
24 4 L e ct u r e 2 2 G r a p hs a nd Tr ees
Usually, when we talk about trees, well designate one node as the root,
which can be thought of as the starting point, or the central point. Its
the top level of a hierarchy. The rest of the tree can then be arranged
in terms of levels from the root, where the root is at level 0, and each
subsequent level is formed based on how many edges must be followed
to get to the root.
All the nodes connected to the root are considered its children and form
the first level. The nodes they are connected to form the second level,
and so on. For any node, the node it is connected to at the previous
level is called its parent, and the nodes below it are called its children.
A typical drawing of a tree will put the root at the top and the children in
levels below.
Because trees have this particular structure, they often have a particular
way they are stored in code. Trees almost always use an adjacency list,
where the edges are stored inside each node. And they usually store the
parent and the children separately. So, any one node will store its own
information, along with an index (or some other notation, such as a name)
for the parent node, along with a list of its children.
In code, well keep a variable to give the parent, and well keep a list of
variables that are the children. We can add additional children whenever
we need to.
class node:
def __init__(self, name, parent=-1):
self._name = name
self._parent = parent
self._children = []
def getName(self):
return self._name
def getParent(self):
return self._parent
def getChildren(self):
return self._children
| 24 5
A very common type of tree is called a binary tree. In this case, every
node has no more than two children. Well usually call them a left
child and a right child. So, a node will hold a parent, a left child, and a
right child.
class node:
def __init__(self, name, parent=-1):
self._name = name
self._parent = parent
self._left = -1
self._right = -1
def getName(self):
return self._name
def getParent(self):
return self._parent
def getLeft(self):
return self._left
def getRight(self):
return self._right
def setParent(self, p):
self._parent = p
def setLeft(self, l):
self._left = l
def setRight(self, r):
self._right = r
Binary trees have many uses, but a common one is to store objects in
sorted order. We call these binary search trees. Unlike arrays or lists that
we might have to sort every time we add a new value, a binary tree can
keep items always in sorted order. Its usually faster to add an item to a
binary tree than to add it to an array or list thats been sorted.
|
24 6 L e ct u r e 2 2 G r a p hs a nd Tr ees
With a binary search tree, at any node in the tree, all the descendants on
the left side are less than the node, and all those on the right side are
greater than the node. Notice, for example, that 21 is greater than the
root, 15, so its on the right side of the root. Its greater than the next node,
18, so its also on the right side of it. But its less than the next node, 23, so
its on the left side of that one.
15
8 18
3 13 23
11 21 27
Figure 22.b
There are various things we can do with a binary search tree, such as
print out the entire tree in sorted order. This ends up being basically
another sorting routine: We put elements into the binary search tree in
order, and then when we print them out, we get a sorted list. If we take
our set of nodes, insert them all into the tree, and then print it out, we
get a sorted output list. This routine is fast, plus it works really fast, on
average, if you have to update the sorted list.
Reading
Exercises
1 Consider the parts of a body: head, neck, torso, arms, legs, hands, and feet.
Draw a graph showing the connectivity between the parts of the body.
2 From the graph in exercise 1, what is the longest number of edges you
would need to follow to go from one body part to another?
4 Show the binary search tree that you would get by inserting nodes into an
empty tree in the following order: 3, 1, 8, 2, 9.
5 Note that instead of storing indices for cities, we could instead store the
cities in a dictionary instead of in a list. Following is part of the code used to
build a list of cities in the code from the lecture. How would you modify this
so that it uses a dictionary of cities instead of a list?
cities = []
city = node('Rivertown', 1000)
cities.append(city)
city = node('Brookside', 1500)
cities.append(city)
city = node('Hillsview', 500)
cities.append(city)
city = node('Forrest City', 800)
cities.append(city)
city = node('Lakeside', 1100)
cities.append(city)
...
road = roads[0]
pop1 = 0
pop2 = 0
for city in cities:
if city.getName() == road.getName1():
pop1 = city.getPopulation()
if city.getName() == road.getName2():
pop2 = city.getPopulation()
24 8
23 Lecture 23
Graph Search and a Word Game
In Figure 23.a on the following page, the node marked 0 is the node
were starting at, and the striped node is the one were trying to find. In
our social network, node 0 is the first person and the striped node is
the person we want to connect to. People who are friends in the social
network are represented as neighbor nodes in the graph.
Well number the nodes as we find them, and well keep a list of nodes,
visiting them in order. The first thing we do is look at all of the neighbors
of node 0. It has 3 neighbors, so we number them 1, 2, and 3. We didnt
find the goal among them, so we are done with node 0.
Now we move on to the next node, node 1. We will check its neighbors,
numbering them. It has 4 neighbors, but one of those, node 0, already
has been seen, so we dont number it. We check nodes 4, 5, and 6,
giving them numbers. Then, were done with node 1.
| 24 9
0
Figure 23.a
The process continues with node 4, and then with node 5. Finally, when
we get to node 6, we have found our result. It turns out that there is a
path from node 0 to 1 to 6 to our goal node. In other words, the person
represented by node 0 has a friend who is friends with the friend of the
person represented by the striped node.
9
5 7
6
4
1
2
8
0
3
Figure 23.b
|
25 0 L e ct u r e 2 3 G r a p h S ea r ch a nd a Wor d Ga me
Lets see how we would implement that algorithm in code. First, well
write some pseudocode for the algorithm.
Next, well keep a queue of which nodes to visit. A queue is first in,
first out. We can implement a queue with just a list, and we have an
enqueue to add something to the end of the queue and dequeue to
take the next one from the front. Initially, the only node in our queue will
be the starting node.
Then, well go through and visit the nodes. Each time, well take whichever
one is next in the queue. We can then check its neighbors. For each
neighbor, well ignore it if its already seen or visitedthat means weve
already discovered that node and dont need to worry about it again.
However, if its unseen, we will mark that neighbor as seen to make sure
it knows that it was discovered from whatever node we are on now. We
need to check to see if weve found the goal node, and if so, we can stop
our loop. Otherwise, we need to add this node to the queue.
At the very end, we will need to go back over the path that we found,
by following the list of which node discovered the goal, then which one
discovered that one, and so on until were back at the start.
| 251
And we can have our makeFriends routine that lets us link two people
together as friends. It will go through the people list to find the index of
each of the names of the two people, and then add the corresponding
index to the friend list of each.
class node:
def __init__(self, name):
self._name = name
self._friends = []
|
25 2 L e ct u r e 2 3 G r a p h S ea r ch a nd a Wor d Ga me
def getName(self):
return self._name
def getFriends(self):
return self._friends
def addFriend(self, friend_index):
self._friends.append(friend_index)
def makeFriends(name1, name2):
for i in range(len(people)):
if people[i].getName() == name1:
n1 = i
if people[i].getName() == name2:
n2 = i
people[n1].addFriend(n2)
people[n2].addFriend(n1)
In order to run the breadth-first search, well need to augment our node
structure, to have the additional features of being able to mark a node
as seen or unseen and note the previous node on the breadth-first
search path.
class node:
def __init__(self, name):
self._name = name
self._friends = []
self._status = 0
...
def isUnseen(self):
if self._status == 0:
return True
else:
return False
| 253
def isSeen(self):
if self._status == 1:
return True
else:
return False
def setUnseen(self):
self._status = 0
def setSeen(self):
self._status = 1
We also need to augment the node so that it has some information about
which node discovered it during the search. Well add a routine that
lets us set that node. Later, well need to do some more with this when
we print out our result, but thats enough to be able to write our basic
breadth-first search routine.
class node:
def __init__(self, name):
self._name = name
self._friends = []
self._status = 0
self._discoveredby = 0
def getName(self):
return self._name
def getFriends(self):
return self._friends
def addFriend(self, friend_index):
self._friends.append(friend_index)
def isUnseen(self):
if self._status == 0:
return True
else:
return False
def isSeen(self):
if self._status == 1:
return True
|
25 4 L e ct u r e 2 3 G r a p h S ea r ch a nd a Wor d Ga me
else:
return False
def setUnseen(self):
self._status = 0
def setSeen(self):
self._status = 1
def discover(self, n):
self._discoveredby = n
def discovered(self):
return self._discoveredby
At this point, weve augmented our nodes to hold the required information.
Well now actually write a function to do this search for us. We need to
take in the graph, which is going to be our node list, a starting node, and
a goal node. The nodes are just the index of the node.
Lets see how we start our routine. Well first have our queue routine
exactly the same as the one we developed previously. Then, we have
the beginning of our breadth-first search (BFS) routine. The BFS function
will take in a nodelist, a start, and an end.
The first thing to do was to mark the starting node seen and add it
to the queue. So, within our routine, we create a new, empty queue,
called to_visit. We then mark the starting node as visited by going
to nodelist[start]the starting nodeand calling setSeen, which
will mark it as seen. We then add the start node to the queue, by
calling enqueue.
class queue:
def __init__(self):
self._queue = []
def enqueue(self, x):
self._queue.append(x)
def dequeue(self):
return self._queue.pop(0)
def isEmpty(self):
return len(self._queue) == 0
| 255
The next thing to do is create a loop where we pull out the next node and
visit its neighbors. At the beginning, we need a variable to keep track of
whether weve found the goalthatll be False at first. Well then have
to have our loop, which will be a while loop with two conditions: the goal
was not found, and the queue is not empty.
Now we have a loop, and the first thing we need to do is get an item
out of our queue, and then get its neighbors. Well call dequeue to get
the next node out of the queuethe index of the next node. Well call
the index that we pulled out current. For that node, we need to pull
out the neighbors, which we can do with a single function call. We just
call nodelist[current] and then call the getNeighbors method on that,
which returns a list of neighbors to visit.
First, we have a for loop, which will go through all of the neighbors,
so we write for neighbor in neighbors. We next check to see if the
node is an unseen one. If its not, we dont need to think about it. If
it is unseen, we change that. Using the setSeen command and the
discover command, we mark the node as seen and with a prior node
of the current one.
|
25 6 L e ct u r e 2 3 G r a p h S ea r ch a nd a Wor d Ga me
If the goal was found, we need to find the list of nodes that got us to
the goal. Because each node along the way from the start to the goal
has a reference of who discovered the node, we can just read this list
backward to get our result.
We recursively get the path from the start node to that previous node
that is, we call our retreivePath function using that previous node as the
goal. Then, we just append the goal onto the end of that, and return.
Once weve finished our breadth-first search algorithm, we can test it,
using a small graph for five people who have several friend relationships.
When we run this, we get a listJohn, Sue, Fred, Kathywhich is indeed
a path connecting the two friends.
people = []
person = node('John')
people.append(person)
person = node('Joe')
people.append(person)
person = node('Sue')
people.append(person)
person = node('Fred')
people.append(person)
person = node('Kathy')
people.append(person)
makeFriends('John', 'Joe')
makeFriends('John', 'Sue')
makeFriends('Joe', 'Sue')
makeFriends('Sue', 'Fred')
makeFriends('Fred', 'Kathy')
pathlist = BFS(people, 0, 4)
for index in pathlist:
print(people[index].getName())
OUTPUT:
John
Sue
Fred
Kathy
Reading
Exercise
There are many other graph algorithms besides breadth-first search (BFS). One
of these is depth-first search (DFS), which aims to explore as far as possible.
Imagine that the queue used to keep track of nodes to visit in the BFS algorithm
is instead replaced by a stack. Assume that you had the following graph and
were starting at node E, trying to find node G. Assume that neighbors are
listed in alphabetical order in each node. What is the order in which the nodes
are visited?
M
A J G
L F
D B
K
I
C
H
E Figure 23.c
260
24 Lecture 24
Parallel Computing Is Here
[ Parallel Computing ]
In the 1960s, Gordon Moore, one of the founders of Intel, made a famous
prediction: that the number of transistors on an integrated circuit would
follow an exponential growth rate, doubling every so many years. This
idea came to be known as Moores law, and it has driven the computer
chip design industry for many years. And though the rate of growth may
have slowed, were still seeing big improvements as our computers
become smaller and faster.
[ Parallelism in Practice ]
In the following code, there are four computations getting assigned to
variables a through d. The order we execute these statements doesnt
really matter. In any order, we end up with the exact same variables
having the exact same values. We would say that this code is easily
parallelizable. We could do all four of these statements at the exact same
time, and we would come out with the exact same answer.
a = 3*8
b = 7/12
c = 3.14159*4.0*4.0
d = (12+3)*(5-8)
a = 3.3+8.5
b = a*4.0*4.0
c = 16 - b
d = c/4.0
Parallel computers are arranged in many different ways, and the ways
you can use parallelism can change depending on how the processors
are set up and how they can communicate with each other. Some parallel
processing is done automatically and is hidden from you. The graphics
processor, for example, automatically processes all the graphical
elements that need to be drawn in parallel.
if __name__ == '__main__':
p = Process(target=print_function)
p.start()
First, well be using the multiprocessing module. Its part of the Python
standard library, so we just import it to get it. The main thing were going
to want from this module is a class definition called Process. We will be
creating instances of Processes.
Next, well define a function that is the thing were going to run in parallel.
This function is going to be the thing spawned by the main program. In
our case, our function is just called print_function, and all it does is print
Hello, World!
If we run this code, the interpreter comes along, creates the Process
object, and then spawns it. Thats all that happens in the main program, in
this case. In the separate process that was spawned off, we have Hello,
World! printed out, and thats what we see as the output.
if __name__ == '__main__':
p = Process(target=print_function)
p.start()
OUTPUT:
Hello, World!
What if the function that will form our process takes in some parameters?
In the following code, weve modified the print function to take in a name
so that we can print Hello to that name. We can set up the arguments
to be passed into the function through the use of an args (short for
arguments) parameter when we create the process.
Now lets look at how we could spawn multiple processes in practice. The
following is a variation on our earlier program. Notice that our function
just takes in a number and prints a message Printing from process and
then the number that was passed in.
| 265
In our main routine, well create a list of 20 processes. Well have a loop,
with i ranging up to 20, and for each one, well create a process in which
print_process is the function and i is used as the parameter. After that,
well go through and actually start each process, in a separate loop.
When we run this, the following is the output. Notice that every process
number, 0 through 19, gets printed once. But the order that these are
printed seems pretty random; the earlier numbers seem to be getting
printed before the later ones, but theyre certainly not in the same order.
Remember that each of those print statements was getting printed from
a totally separate process. You can think of it as though its a totally
separate program thats running completely independently of the others,
possibly on a different processor. And those different processors might
have other things running on themfor example, some operating system
commands or other applications running in the background. If you run
this code a few times, you should see a different result every timethere
are 20 factorial possible orderings.
[ Parallel Processors ]
The effectiveness of parallelism is measured by how effectively parallel
processors can be applied to a particular problem. If you had two
processors, the best situation would be if you could use both of them all
the time with no time wasted. In this case, your overall running time would
be cut in half. Four processors could cut running time in a quarter.
Reading
Exercise
What code would you write to spawn a new process, running a program named
myProgram.exe?
269
Answers
LECTURE 1
1 15
(** raises to a power, so 3**2
2 25 is 9 and 4**2 is 16.)
3 21
5 1.0
9 print(180/7)
Remember
There is more than
one way to write
code correctly.
|
2 70 A nsw e r s
LECTURE 2
1 25
2 15
3 115
4 250
5 10.0
6 10
LECTURE 3
1 False
4 True
6 False
10 False
11 True
12 True
13 Reasonable cost
15 age = 67
income = 10000
if (income < 15000) and (age >= 70):
print("Eligible for benefits")
elif (income < 20000):
print("Eligible for reduced benefits")
else:
print("Not eligible for benefits")
17 a) if year % 4 == 0:
if year % 100 == 0:
if year % 400 == 0:
print("Leap year")
else:
print("Not a leap year")
else:
print("Leap year")
else:
print("Not a leap year")
b) if year % 400 == 0:
print("Leap year")
elif year % 100 == 0:
print("Not a leap year")
elif year % 4 == 0:
print("Leap year")
else:
print("Not a leap year")
| 273
LECTURE 4
For the first part, notice that we added a variable, cost, and then created
another variable, saved, which we read in from the user. We then compute
balance as the difference.
For the second part, notice that we have a new input line that gets the period
being saved for and that this variable is used in the later input and output
statements.
LECTURE 5
1 10
5.0
2.5
1.25
5 1
4
7
8 The following are two versions: one with a while loop and one with a for
loop. Notice that you need to start at 1, not 0, and include the final number in
the list, either by using <=, as in the while loop, or num+1, as in the for loop.
9 for i in range(2,7,3):
print(i)
|
2 76 A nsw e r s
secret_number = 7
while True:
guess = int(input("Enter your guess from 1 to 10: "))
if guess == secret_number:
break
else:
print("No! Try again.")
print("You guessed it!")
secret_number = 7
guess = 0
while guess != secret_number:
guess = int(input("Enter your guess from 1 to 10: "))
if guess != secret_number:
print("No! Try again.")
print("You guessed it!")
LECTURE 6
3 infile.close()
outfile.close()
LECTURE 7
1 4
2 [6, 8, 10]
3 [2, 4, 6]
4 [18, 20]
6 20
9 2
4
6
8
10
12
14
16
18
20
16 minor_ages = []
for age in ages:
if age < 18:
minor_ages.append(age)
LECTURE 8
The following are changes to the relevant parts of the program. The new lines
are in bold.
# Perform analysis
minsofar = 120
maxsofar = -100
numgooddates = 0
sumofmin=0
sumofmax=0
raindays = 0
for singleday in gooddata:
numgooddates += 1
sumofmin += singleday[1]
sumofmax += singleday[2]
if singleday[1] < minsofar:
minsofar = singleday[1]
if singleday[2] > maxsofar:
maxsofar = singleday[2]
if singleday[3] > 0:
raindays += 1
avglow = sumofmin / numgooddates
avghigh = sumofmax / numgooddates
rainpercent = raindays / numgooddates * 100
########## Present Results ##########
print("There were", numgooddates,"days")
print("The lowest temperature on record was", minsofar)
print("The highest temperature on record was", maxsofar)
print("The average low has been", avglow)
print("The average high has been", avghigh)
print("The chance of rain is", rainpercent, "%")
|
2 80 A nsw e r s
LECTURE 9
1 XXXXX
2 256
3 4 6
4 21
5 9 12
LECTURE 10
1 3
2 [0, 2, 3]
3 21
4 2
5 3
6 [0, 2, 3]
7 2 1
8 def increment_list(a):
for i in range(len(a)):
a[i] += 1
|
2 82 A nsw e r s
LECTURE 11
2 a) def findmiddle(a):
if len(a) < 3:
raise TypeError # This could be a different
exception
if ((a[0] >= a[1]) and (a[1] >= a[2])) or ((a[0] <=
a[1]) and (a[1] <= a[2])):
return a[1]
elif ((a[0] >= a[2]) and (a[2] >= a[1])) or ((a[0] <=
a[2]) and (a[2] <= a[1])):
return a[2]
else:
return a[0]
b) try:
middle = findmiddle(a) #a is some
except TypeError:
pr
int("Problem: You need a list of at least length
3!")
| 283
LECTURE 12
2 import os
os.mkdir("DataDir")
LECTURE 13
Note: If you use a different board definition, your other functions would
change, too.
3 def first_row(board):
if board[0][0] == 'X' and board[0][1] == 'X' and board[0]
[2] == 'X':
return 'X'
elif board[0][0] == 'O' and board[0][1] == 'O' and
board[0][2] == 'O':
return 'O'
else:
return '.'
|
284 A nsw e r s
LECTURE 14
import turtle
def drawA():
# Draw the left side of the A
turtle.left(60)
turtle.forward(20)
# Draw half the right side of the A
turtle.right(120)
turtle.forward(10)
# Draw the cross-part of the A
turtle.left(60)
turtle.backward(10)
turtle.forward(10)
# Draw remainder of the right side
turtle.right(60)
turtle.forward(10)
# Return the turtle to original orientation
turtle.left(60)
# Move turtle over a small amount
turtle.up()
turtle.forward(5)
turtle.down()
LECTURE 15
1 import pyglet
window = pyglet.window.Window(width=400, height=300,
caption="ExerciseWindow")
Im1 = pyglet.image.load('BlueTri.jpg')
@window.event
def on_mouse_press(x, y, button, modifiers):
window.clear()
Im1.blit(x,y)
pyglet.app.run()
| 285
2 import tkinter
class Application(tkinter.Frame):
def __init__(self, master=None):
tkinter.Frame.__init__(self, master)
self.pack()
self.hello_button = tkinter.Button(self)
self.hello_button["text"] = "Print repeatedly"
self.hello_button["command"] = self.printtimes
self.hello_button.pack(side="bottom")
def printtimes(self):
global times
for i in range(times):
print("Hello!")
times += 1
times = 1
root = tkinter.Tk()
app = Application(master=root)
app.mainloop()
LECTURE 16
import random
from matplotlib.pyplot import show, hist
rolls = []
for i in range(10000):
roll = (random.randrange(6)+1) + (random.randrange(6)+1) +
(random.randrange(6)+1)
rolls.append(roll)
hist(rolls, bins=16)
show()
|
2 86 A nsw e r s
LECTURE 17
1 20
100.0
10
300.0
2 40
1800.0
50
750.0
3 def print(self):
print(self.item+" barcode: "+str(self.barcode))
print("Price:",self.price)
print("Current Inventory:", self.quantity)
print("Sold so far:", self.sales)
4 class Movie:
title = ""
genre = ""
rating = 0.0
6 movielist = []
rating = 1.0
while rating >= 0.0:
title = input("Enter the movie title:")
genre = input("What is the genre of this movie?")
rating = float(input("How do you rate the movie?"))
if rating >= 0.0:
movie = Movie(title, genre, rating)
movielist.append(movie)
| 287
LECTURE 18
1 a) class Videogame(Game):
platform = ""
b) class Boardgame(Game):
numpieces = 0
board = [0,0]
2 In Videogame class:
def print(self):
print(self.name)
print("Up to ", self.numplayers, "players")
print("Can be played on", self.platform)
In Boardgame class:
def print(self):
print(self.name)
print("Up to ", self.numplayers, "players")
print("Has", self.numpieces, "pieces, and a board of
size ", self.board[0], "by",self.board[1])
3 tetris = Videogame()
tetris.name = "Tetris"
tetris.numplayers = 1
tetris.platform = "Windows"
tetris.print()
4 import pickle
outfile = open("Game.dat", 'wb')
pickle.dump(tetris,outfile)
outfile.close()
5 import pickle
infile = open("Game.dat", 'rb')
savedgame = pickle.load(infile)
infile.close()
|
288 A nsw e r s
LECTURE 19
1 Joseph
James
John
2 John
James
Joseph
3 Michael Palin
Michael Palin
Terry Gilliam
LECTURE 20
2 def one_bubble_pass(lst):
returnval = False
for i in range(len(lst)-1):
if lst[i] > lst[i+1]:
swap(lst,i)
returnval = True
return returnval
| 289
3 def bubblesort(lst):
keepgoing = True
while keepgoing:
keepgoing = one_bubble_pass(lst)
LECTURE 21
1 It computes the product of all the elements in a list. Notice that the recursive
call gives 1 for an empty list. For a larger list, it multiplies the first element by
the result of the call on the rest of the list.
2 a) swap is a constant time function. The time taken to perform a single swap
function is independent of the length of the list.
b) one_bubble_pass is a linear time function. Each element of the list is
visited once, on a single pass through the list.
c) bubblesort is a quadratic function. In the worst case, there are a linear
number of passes through the while loop, each of which calls one_
bubble_pass, which is again linear.
LECTURE 22
1
Head
Arm Arm
Torso
Leg Leg
2 4. This happens when traveling from one foot, hand, or head to another.
4
3
1 8
2 9
Figure A22.b
5 cities = {}
cities['Rivertown'] = 1000
cities['Brookside'] = 1500
cities['Hillsview'] = 500
cities['Forrest City'] = 800
cities['Lakeside'] = 1100
...
road = roads[0]
pop1 = 0
pop2 = 0
pop1 = cities[road.getName1()]
pop2 = cities[road.getName2()]
LECTURE 23
E, H, K, M, L, J, G.
Alternative answer: E, C, A, B, D, F, G.
If we instead assume that items are put on the stack in the reverse
order they appear in each node, then at node E, we will first put H,
then I, and then C on the stack.
We would then visit node C. It would put F, then D, and then A on
the stack.
A would be visited next. There, we would put B on the stack.
We would next visit B. All of its neighbors would have been seen, so
we will return.
Next, we would visit the next node on the stack, D. Again, its
neighbors have already been seen.
The next node on the stack is F. When we visit F, we will find G as its
neighbor and be done.
Notice that nodes I and H are placed on the stack but never are
popped off.
The route to G will be ECFG.
In this case, the stack managed to find the shortest route to G, but this
is not guaranteed.
The depth-first search approach will still find some path to the goal,
if one exists, but it might not be the shortest path. But a depth-first
search is used as a substep in other graph algorithms.
|
292 A nsw e r s
LECTURE 24
import subprocess
subprocess.Popen("myProgram.exe")
This mirrors the subprocess spawning as shown in the lecture. We will have the
program myProgram.exe running in a separate process at the same time as
our Python program is running.
293
Glossary
adjacency matrix: A matrix describing the edges connecting all pairs of nodes.
[Lecture 22]
Amdahls law: A rule that determines the theoretical limit for how much
speedup can be obtained through parallelism. [Lecture 24]
attribute (also called field (Java) and member variable (C++)): A variable
defined for a particular object. This is usually defined in a class, so that all
objects in that class have that attribute. [Lecture 17]
base case: A special case in a recursive function that returns without making a
recursive call. The base case is what causes a recursive routine to eventually
stop. [Lecture 21]
binary file: A file stored in binary format, which is typically more efficient but is
not able to be read by humans. [Lecture 6]
binary search: A process for searching in a sorted list in which you repeatedly
check the midpoint of the list and then search in either the upper or lower half
of the remaining list. [Lecture 20]
binary search tree: A binary tree in which the nodes contain items ordered
such that for any node, the left descendants are all smaller items and right
descendants are all larger items. [Lecture 22]
binary tree: A tree in which each node will have at most two children.
[Lecture 22]
branching: The result of having conditionals within code. The code is said to
have branching, because any one execution can follow only certain branches
of the program. [Lecture 3]
buffer: Queue of data or events to be handled. For example, user input, such
as mouse movements or keyboard key presses, are often stored in an event
buffer. [Lecture 19]
bug (also called error, fault, or defect): An error created in the process of
programming. [Lecture 11]
call stack (also called control stack, runtime stack, or frame stack): Function
activation records that keep track of all the variables and data defined in that
part of the program. [Lecture 19]
central processing unit (CPU): The processor for the computer. This executes
the commands and performs operations. [Lecture 2]
|
296 G lo s sa ry
chaining: In hash tables, refers to making a list of all items that map to the same
hash value. [Lecture 19]
child class (also called derived class or subclass): A class that inherits
attributes and methods from a parent class. [Lecture 18]
child node: A node that is reachable by an edge from a given node and that is
one level farther away from the root node. [Lecture 22]
class: A way to group both data (defined in attributes) and functions (defined
in methods). Can be thought of as a type of variable. Classes form the heart of
object-oriented programming. [Lecture 17]. See also object.
closing: To complete work with a file from within a program. Closing the file
ensures that it will not be corrupted by the program. [Lecture 6]
cycle: A sequence of edges that, when followed, returns to the starting node.
[Lecture 22]
divide and conquer: Taking a large problem and dividing it into several smaller
problems that are easier to solve. Typically, this involves dividing a large data
set into two or more smaller data sets that can be processed more easily.
[Lecture 21]
edge cases (also called corner cases): Situations that are at the boundaries of
a range of inputs. These should be a part of any test suite. [Lecture 11]
|
2 98 G lo s sa ry
file: A set of data stored in secondary or tertiary memory. Programs must read a
file into main memory to use it or can write from main memory to a file. [Lecture 6]
| 299
for loop: A loop that repeats a certain number of times, with the number of
times controlled by an iterator. [Lecture 5]
function activation record: A region of memory set aside for a function to work
in, including the functions parameters and any variables defined in the function.
The function activation record is destroyed when the function returns. [Lecture 10]
function body: The part of the function definition besides the header,
describing the actions the function will take, along with when and what to
return. [Lecture 9]
function header: The initial line of a function definition, giving its name and
describing its parameters. [Lecture 9]
global variable: A variable that is in scope both outside and within a function.
Declaring variables as global is a way to initialize certain types of data without
using objects. [Lecture 10]
graph: A data structure used to store items and their relationships to each other.
Items are stored at nodes, and the relationships between items are stored by
edges connecting nodes. [Lecture 22]
hardcoding: When a specific value is set within the code, rather than being
read in from a user. Hardcoding tends to be easier to code in the short term but
is less flexible in the long term. [Lecture 12]
hash function: A function that can take a key phrase and convert it to a number
that can be used to index into a list being used for a hash table. [Lecture 19]
hash table: Data structure that maps data with indices in a very large range into
a smaller set of indices that can be stored more compactly. The index for a data
element is called the key. [Lecture 19]
inheritance: When one class (the child class) is defined to have all the attributes
and methods of another class (the parent class). [Lecture 18]
in-place sort: A sort in which the original list is modified to put the elements in
sorted order. [Lecture 20]
input/output (I/O): The interface between a computer and the outside world.
Input can come from many possible sources, including keyboard or mouse
input, network connections, sensors, etc. Output can be text or a graphical
display that is output to the screen, a printed document, data sent over the
network, commands to an attached device, etc. [Lecture 2]
| 30 1
insertion sort: A sort in which one new element is repeatedly inserted into an
already sorted list. [Lecture 20]
instantiation: Creating a new object. This happens when the object is first
encountered in a program. [Lecture 17]
iterator: A variable that gets initialized to a starting value and is incremented for
each iteration of a loop, until it reaches a maximum value. [Lecture 5]
key: In a hash table, the value that is used as an index into the table. Keys can
be any immutable data type. [Lecture 19]
list (also called array): Data stored sequentially so that it can be referred to by
its index. Typically, the data in a list will be of the same type. [Lecture 7]
logic error: An error that causes the program to produce incorrect results, due
to incorrect design of the program. [Lecture 11]
loop: A programming construct that repeats a set of commands over and over.
[Lecture 5]
main memory: Short-term working memory that holds the data currently being
used by the computer. This is separate from the CPU but is connected directly.
Main memory holds the variables that programs use. [Lecture 2]
main program: Part of the computer program that is executed first, apart from
any function definitions. [Lecture 10]
memory: Part of the computer that can store data. Memory is arranged in a
memory hierarchy. [Lecture 2]
mergesort: A recursive sorting routine in which a list is split into two halves,
each of which is then sorted recursively. The sorted lists are then merged
together. [Lecture 21]
method (also called member function (C++)): A function defined for a particular
object or class. Parallel to how attributes define data. [Lecture 17]. See also
attribute.
model: The laws and rules that are assumed to govern a particular process.
[Lecture 16]
module: A Python library, ending with a .py extension, just like other Python
programs. [Lecture 12]
nesting: When one programming construct occurs within another of the same
type. For example, if a conditional contains another conditional, or a loop
contains another loop, these are said to be nested. [Lecture 3]
node: A vertex in a graph that is used to store information about the items or
entities. Nodes are connected by edges. [Lecture 22]
|
304 G lo s sa ry
out-of-place sort: A sort in which a new, sorted, list is created while leaving the
original list unchanged. [Lecture 20]
parallel computing: Computing more than one value simultaneously. [Lecture 24]
parameter passing: Copying the value from the function call (sometimes called
the argument) into the memory set aside for the parameter variable within the
function activation record. [Lecture 10]
parent class (also called base class or superclass): A class that defines
attributes and methods that are inherited by a child class. [Lecture 18]
parent node: A node in a tree that is one edge closer to the root than a given
node. [Lecture 22]
| 30 5
path: Designation for the location of a file within a computers storage system.
The path tells where to find a file relative to the computer or relative to the
current program being executed. [Lecture 6]
pivot: In the quicksort algorithm, the value used for separating the list into
smaller and larger parts. [Lecture 21]
queue: A data structure that allows storage and retrieval of data, following
a first-in, first-out order. Items are added using an enqueue command and
removed using a dequeue command. [Lecture 19]
recursion: A process in which a function calls itself, typically with a different set
of parameters. [Lecture 21]
root: A node in a tree designated as the one from which all other nodes will be
traced. [Lecture 22]
runtime error: An error that occurs when the program is running, causing the
program to fail. Runtime errors can be dealt with using exceptions. [Lecture 11]
search: The process of finding an element within some larger collection, such
as a list. [Lecture 20]
selection sort: A sort in which the smallest element is repeatedly selected from
the remaining elements. [Lecture 20]
| 30 7
set: A data structure for storing items with no fixed order and no duplicate
values. It supports the common mathematical set operations. [Lecture 19]
side effect: Actions that a function takes that are not obviously part of the
functions behavior from its definition. For example, a function might change a
value of a variable that is not a parameter. [Lecture 9]
simulation: The process of taking a model and set of initial conditions and
determining how the process progresses. [Lecture 16]
sort: A basic algorithm for taking a list of values and creating a list in which the
values are ordered from smallest to largest. [Lecture 20]
stack: A data structure that allows storage and retrieval of data, following a last-
in, first-out order. Items are added using a push command and removed using a
pop command. [Lecture 19]
storage: Alternate term for secondary and tertiary memory. Refers to memory
that is not immediately accessible to programs running on the computer; data in
storage must be brought into main memory to be used. [Lecture 2]
syntax error: A bug that is due to writing code that is not valid. Programs with
syntax errors cannot execute. [Lecture 11]
testing: The way to debug code, by running code using specific input and
determining if output is correct. [Lecture 4]
test suite: A set of tests that are run on code to make sure that it is working
correctly. As new features are added, the test suite should be continuously
verified as working. [Lecture 11]
time step: The amount of time by which a simulation advances in one round of
computation. [Lecture 16]
top-down design: Taking a complex task and breaking it into simpler parts,
repeatedly, until the basic parts are obvious. [Lecture 8]
tree: A particular type of connected graph that does not contain a cycle. One of
the most widely used data structures; many algorithms have been developed
just for trees. [Lecture 22]
tuple: Like a list, but with fixed length and types. Like a list, index values can be
used to access elements of a tuple. Tuples are not mutable. Tuples will often
combine different types of data in one tuple. [Lecture 7]
turtle graphics: Graphics created by simulating a small robot turtle that carries
a pen as it moves around, tracing the path it follows. [Lecture 14]
| 30 9
type: The way that data stored in a variable should be interpreted by the
computer. Each variable and value will have a type, such as an integer, a
floating-point number, a string, etc. [Lecture 2]
undirected edge: An edge that connects two nodes, with no distinction for a
source and destination. [Lecture 22]
variable: A memory location with a given name that can hold a value. [Lecture 2]
while loop: A loop that repeats as long as some condition is true. [Lecture 5]
Python Commands
for... in: For loop with an iterator proceeding through a given set of values.
[Lecture 5]
global: Make a variable equivalent to the global variable of the same name.
[Lecture 10]
input: Print text to the screen, and then get input from a user and return.
[Lecture 2]
try ... except ... finally: Try to execute code, and if an exception is
raised, handle it in the except section. [Lecture 11]
with ... as: Use to open a file as a given name and close on completion.
[Lecture 6]
312
The Python Package Index (PyPI) includes thousands of Python modules and
packages of varying degrees of completeness and support. To use these,
you must first download and install them on your computer. This can usually
be done through the pip interface, by typing python m pip install <package
name>. You can also visit the PyPI page for the module or the website devoted
to the module (if there is one) to find more details and download it directly.
json: Converting data to/from JSON format, and then writing and reading JSON
strings to files. [Lecture 18]
pickle: Converting Python data to a binary format and writing to or reading from
a file. [Lecture 18]
Bibliography
Most Python books will go into much greater detail in some features or
applications of the language than others, so the best book will often
depend on which topic you wish to learn more about. The following are
recommended as good books, overall, for further study.
Matthes, Eric. Python Crash Course. No Starch Press, 2015. This book is in two
parts: The first provides an introduction to Python, and the second presents
three in-depth projects: an arcade-style game, a data visualization, and a
web application.
| 315
Sweigart, Al. Automate the Boring Stuff with Python: Practical Programming for
Total Beginners. No Starch Press, 2015. This bookwhich is available for free
online at https://automatetheboringstuff.comis in two parts: The first presents
an overview and introduction to Python, and the second presents several
detailed examples of how to use various modules to build interesting and
useful applications.