You are on page 1of 314

How to Think Like a Computer

Scientist

Learning with Python

2nd Edition

by 

Jeffrey Elkner, Allen B. Downey, and


Chris Meyers
I

Table of Contents

Learning with Python ................................................................................1


2nd Edition.................................................................................................1
How and why I came to use Python...........................................................5
Finding a textbook.....................................................................................6
Introducing programming with Python......................................................8
Building a community..............................................................................11
Second Edition.........................................................................................13
First Edition..............................................................................................13
1.1. The Python programming language..................................................17
1.2. What is a program?...........................................................................20
1.3. What is debugging?..........................................................................21
1.4. Syntax errors....................................................................................22
1.5. Runtime errors..................................................................................22
1.6. Semantic errors................................................................................22
1.7. Experimental debugging...................................................................23
1.8. Formal and natural languages..........................................................24
1.9. The first program..............................................................................27
1.10. Glossary..........................................................................................27
1.11. Exercises........................................................................................30
2.1. Values and types..............................................................................33
2.2. Variables...........................................................................................35
2.3. Variable names and keywords..........................................................36
2.4. Statements.......................................................................................37
2.5. Evaluating expressions.....................................................................38
2.6. Operators and operands...................................................................39
2.7. Order of operations...........................................................................41
2.8. Operations on strings.......................................................................41

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
II

2.9. Input.................................................................................................42
2.10. Composition....................................................................................43
2.11. Comments......................................................................................44
2.12. Glossary..........................................................................................45
2.13. Exercises........................................................................................47
3.1. Definitions and use...........................................................................50
3.2. Flow of execution..............................................................................53
3.3. Parameters, arguments, and the import statement.........................54
3.4. Composition......................................................................................57
3.5. Variables and parameters are local..................................................57
3.6. Stack diagrams.................................................................................58
3.7. Glossary............................................................................................60
3.8. Exercises...........................................................................................63
4.1. The modulus operator.......................................................................65
4.2. Boolean values and expressions.......................................................65
4.3. Logical operators..............................................................................67
4.4. Conditional execution.......................................................................67
4.5. Alternative execution........................................................................68
4.6. Chained conditionals........................................................................69
4.7. Nested conditionals..........................................................................70
4.8. The return statement........................................................................71
4.9. Keyboard input.................................................................................72
4.10. Type conversion.............................................................................73
4.11. GASP...............................................................................................75
4.12. Glossary..........................................................................................76
4.13. Exercises........................................................................................78
5.1. Return values....................................................................................85
5.2. Program development......................................................................87
5.3. Composition......................................................................................90
5.4. Boolean functions.............................................................................91

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
III

5.5. The function type..............................................................................92


5.6. Programming with style....................................................................93
5.7. Triple quoted strings.........................................................................94
5.8. Unit testing with doctest...................................................................95
5.9. Glossary............................................................................................98
5.10. Exercises.........................................................................................99
6.1. Multiple assignment........................................................................103
6.2. Updating variables..........................................................................104
6.3. The while statement.......................................................................105
6.4. Tracing a program..........................................................................107
6.5. Counting digits................................................................................108
6.6. Abbreviated assignment.................................................................109
6.7. Tables.............................................................................................110
6.8. Two-dimensional tables..................................................................112
6.9. Encapsulation and generalization...................................................113
6.10. More encapsulation......................................................................114
6.11. Local variables..............................................................................115
6.12. More generalization......................................................................116
6.13. Functions......................................................................................118
6.14. Newton’s Method..........................................................................119
6.15. Algorithms....................................................................................119
6.16. Glossary........................................................................................121
6.17. Exercises......................................................................................123
7.1. A compound data type...................................................................126
7.2. Length............................................................................................127
7.3. Traversal and the for loop...............................................................128
7.4. String slices....................................................................................129
7.5. String comparison...........................................................................130
7.6. Strings are immutable....................................................................131
7.7. The in operator...............................................................................131

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
IV

7.8. A find function................................................................................132


7.9. Looping and counting.....................................................................133
7.10. Optional parameters.....................................................................133
7.11. The string module.........................................................................135
7.12. Character classification................................................................137
7.13. String formatting..........................................................................138
7.14. Glossary........................................................................................141
7.15. Exercises......................................................................................142
8.1. Getting started...............................................................................147
8.2. Using while to move a ball..............................................................147
8.3. Varying the pitches.........................................................................149
8.4. Making the ball bounce...................................................................150
8.5. The break statement......................................................................150
8.6. Responding to the keyboard...........................................................151
8.7. Checking for collisions....................................................................152
8.8. Putting the pieces together............................................................153
8.9. Displaying text................................................................................154
8.10. Abstraction...................................................................................156
8.11. Glossary........................................................................................160
8.12. Exercises......................................................................................161
8.13. Project: pong.py............................................................................162
9.1. List values.......................................................................................164
9.2. Accessing elements........................................................................165
9.3. List length.......................................................................................167
9.4. List membership.............................................................................167
9.5. List operations................................................................................168
9.6. List slices........................................................................................168
9.7. The range function..........................................................................169
9.8. Lists are mutable............................................................................170
9.9. List deletion....................................................................................171

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
V

9.10. Objects and values.......................................................................172


9.11. Aliasing.........................................................................................173
9.12. Cloning lists..................................................................................174
9.13. Lists and for loops.........................................................................174
9.14. List parameters.............................................................................176
9.15. Pure functions and modifiers........................................................177
9.16. Which is better?............................................................................178
9.17. Nested lists...................................................................................178
9.18. Matrices........................................................................................179
9.19. Test-driven development (TDD)....................................................180
9.20. Strings and lists............................................................................184
9.21. Glossary........................................................................................185
9.22. Exercises.......................................................................................187
10.1. Modules........................................................................................195
10.2. pydoc............................................................................................195
10.3. Creating modules..........................................................................197
10.4. Namespaces.................................................................................198
10.5. Attributes and the dot operator....................................................200
10.6. String and list methods.................................................................201
10.7. Reading and writing text files.......................................................202
10.8. Text files.......................................................................................205
10.9. Directories....................................................................................207
10.10. Counting Letters.........................................................................208
10.11. The sys module and argv............................................................209
10.12. Glossary......................................................................................212
10.13. Exercises....................................................................................216
11.1. Tuples and mutability...................................................................224
11.2. Tuple assignment.........................................................................225
11.3. Tuples as return values................................................................226
11.4. Pure functions and modifiers revisited.........................................227

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
VI

11.5. Recursive data structures.............................................................229


11.6. Recursion......................................................................................231
11.7. Exceptions....................................................................................233
11.8. Tail recursion................................................................................236
11.9. List comprehensions.....................................................................238
11.10. Mini case study: tree..................................................................239
11.11. Glossary......................................................................................240
11.12. Exercises....................................................................................242
12.1. Dictionary operations...................................................................250
12.2. Dictionary methods......................................................................251
12.3. Aliasing and copying.....................................................................252
12.4. Sparse matrices............................................................................253
12.5. Hints.............................................................................................255
12.6. Long integers................................................................................256
12.7. Counting letters............................................................................257
12.8. Case Study: Robots.......................................................................258
12.9. Glossary........................................................................................272
12.10. Exercises....................................................................................273
13.1. Object-oriented programming......................................................278
13.2. User-defined compound types......................................................278
13.3. Attributes......................................................................................280
13.4. The initialization method and self.................................................281
13.5. Instances as parameters...............................................................282
13.6. Sameness.....................................................................................282
13.7. Rectangles....................................................................................283
13.8. Instances as return values............................................................285
13.9. Objects are mutable.....................................................................285
13.10. Copying.......................................................................................285
13.11. Glossary......................................................................................287
13.12. Exercises....................................................................................288

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
VII

Vim........................................................................................................289
GASP......................................................................................................290
$HOME environment..............................................................................291
Making a python script executable and runnable from anywhere.........292
Introduction...........................................................................................293
Coordinates...........................................................................................293
Colors.....................................................................................................293
The Essentials........................................................................................293
Graphics Functions................................................................................294
Screen Objects.......................................................................................295
Screen Object Methods..........................................................................300
Text.......................................................................................................301
Mouse....................................................................................................302
Keyboard...............................................................................................302
Gasp Tools.............................................................................................303
Getting the Source.................................................................................304
Making the HTML Version......................................................................305

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
©Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
1

Copyright Notice
Copyright (C) Jeffrey Elkner, Allen B. Downey and Chris Meyers. Permission
is granted to copy, distribute and/or modify this document under the terms
of the GNU Free Documentation License, Version 1.3 or any later version
published by the Free Software Foundation; with Invariant Sections being
Forward, Preface, and Contributor List, no Front-Cover Texts, and no Back-
Cover Texts. A copy of the license is included in the section entitled “GNU
Free Documentation License”.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
2

Foreword
By David Beazley

As an educator, researcher, and book author, I am delighted to see the


completion of this book. Python is a fun and extremely easy-to-use
programming language that has steadily gained in popularity over the last
few years. Developed over ten years ago by Guido van Rossum, Python’s
simple syntax and overall feel is largely derived from ABC, a teaching
language that was developed in the 1980’s. However, Python was also
created to solve real problems and it borrows a wide variety of features
from programming languages such as C++, Java, Modula-3, and Scheme.
Because of this, one of Python’s most remarkable features is its broad
appeal to professional software developers, scientists, researchers, artists,
and educators.

Despite Python’s appeal to many different communities, you may still


wonder why Python? or why teach programming with Python? Answering
these questions is no simple task—especially when popular opinion is on
the side of more masochistic alternatives such as C++ and Java. However,
I think the most direct answer is that programming in Python is simply a lot
of fun and more productive.

When I teach computer science courses, I want to cover important


concepts in addition to making the material interesting and engaging to
students. Unfortunately, there is a tendency for introductory programming
courses to focus far too much attention on mathematical abstraction and
for students to become frustrated with annoying problems related to low-
level details of syntax, compilation, and the enforcement of seemingly
arcane rules. Although such abstraction and formalism is important to

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
3

professional software engineers and students who plan to continue their


study of computer science, taking such an approach in an introductory
course mostly succeeds in making computer science boring. When I teach
a course, I don’t want to have a room of uninspired students. I would much
rather see them trying to solve interesting problems by exploring different
ideas, taking unconventional approaches, breaking the rules, and learning
from their mistakes. In doing so, I don’t want to waste half of the semester
trying to sort out obscure syntax problems, unintelligible compiler error
messages, or the several hundred ways that a program might generate a
general protection fault.

One of the reasons why I like Python is that it provides a really nice
balance between the practical and the conceptual. Since Python is
interpreted, beginners can pick up the language and start doing neat
things almost immediately without getting lost in the problems of
compilation and linking. Furthermore, Python comes with a large library of
modules that can be used to do all sorts of tasks ranging from web-
programming to graphics. Having such a practical focus is a great way to
engage students and it allows them to complete significant projects.
However, Python can also serve as an excellent foundation for introducing
important computer science concepts. Since Python fully supports
procedures and classes, students can be gradually introduced to topics
such as procedural abstraction, data structures, and object-oriented
programming — all of which are applicable to later courses on Java or C++.
Python even borrows a number of features from functional programming
languages and can be used to introduce concepts that would be covered in
more detail in courses on Scheme and Lisp.

In reading Jeffrey’s preface, I am struck by his comments that Python


allowed him to see a higher level of success and a lower level of frustration
and that he was able to move faster with better results. Although these

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
4

comments refer to his introductory course, I sometimes use Python for


these exact same reasons in advanced graduate level computer science
courses at the University of Chicago. In these courses, I am constantly
faced with the daunting task of covering a lot of difficult course material in
a blistering nine week quarter. Although it is certainly possible for me to
inflict a lot of pain and suffering by using a language like C++, I have often
found this approach to be counterproductive—especially when the course
is about a topic unrelated to just programming. I find that using Python
allows me to better focus on the actual topic at hand while allowing
students to complete substantial class projects.

Although Python is still a young and evolving language, I believe that it has
a bright future in education. This book is an important step in that
direction. David Beazley University of Chicago Author of the Python
Essential Reference

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
5

Preface
By Jeffrey Elkner

This book owes its existence to the collaboration made possible by the
Internet and the free software movement. Its three authors—a college
professor, a high school teacher, and a professional programmer—never
met face to face to work on it, but we have been able to collaborate
closely, aided by many other folks who have taken the time and energy to
send us their feedback.

We think this book is a testament to the benefits and future possibilities of


this kind of collaboration, the framework for which has been put in place by
Richard Stallman and the Free Software Foundation.

How and why I came to use Python

In 1999, the College Board’s Advanced Placement (AP) Computer Science


exam was given in C++ for the first time. As in many high schools
throughout the country, the decision to change languages had a direct
impact on the computer science curriculum at Yorktown High School in
Arlington, Virginia, where I teach. Up to this point, Pascal was the language
of instruction in both our first-year and AP courses. In keeping with past
practice of giving students two years of exposure to the same language,
we made the decision to switch to C++ in the first year course for the
1997-98 school year so that we would be in step with the College Board’s
change for the AP course the following year.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
6

Two years later, I was convinced that C++ was a poor choice to use for
introducing students to computer science. While it is certainly a very
powerful programming language, it is also an extremely difficult language
to learn and teach. I found myself constantly fighting with C++’s difficult
syntax and multiple ways of doing things, and I was losing many students
unnecessarily as a result. Convinced there had to be a better language
choice for our first-year class, I went looking for an alternative to C++.

I needed a language that would run on the machines in our GNU/Linux lab
as well as on the Windows and Macintosh platforms most students have at
home. I wanted it to be free software, so that students could use it at home
regardless of their income. I wanted a language that was used by
professional programmers, and one that had an active developer
community around it. It had to support both procedural and object-oriented
programming. And most importantly, it had to be easy to learn and teach.
When I investigated the choices with these goals in mind, Python stood out
as the best candidate for the job.

I asked one of Yorktown’s talented students, Matt Ahrens, to give Python a


try. In two months he not only learned the language but wrote an
application called pyTicket that enabled our staff to report technology
problems via the Web. I knew that Matt could not have finished an
application of that scale in so short a time in C++, and this
accomplishment, combined with Matt’s positive assessment of Python,
suggested that Python was the solution I was looking for.

Finding a textbook

Having decided to use Python in both of my introductory computer science


classes the following year, the most pressing problem was the lack of an
available textbook.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
7

Free documents came to the rescue. Earlier in the year, Richard Stallman
had introduced me to Allen Downey. Both of us had written to Richard
expressing an interest in developing free educational materials. Allen had
already written a first-year computer science textbook, How to Think Like a
Computer Scientist. When I read this book, I knew immediately that I
wanted to use it in my class. It was the clearest and most helpful computer
science text I had seen. It emphasized the processes of thought involved in
programming rather than the features of a particular language. Reading it
immediately made me a better teacher.

How to Think Like a Computer Scientist was not just an excellent book, but
it had been released under the GNU public license, which meant it could be
used freely and modified to meet the needs of its user. Once I decided to
use Python, it occurred to me that I could translate Allen’s original Java
version of the book into the new language. While I would not have been
able to write a textbook on my own, having Allen’s book to work from
made it possible for me to do so, at the same time demonstrating that the
cooperative development model used so well in software could also work
for educational materials.

Working on this book for the last two years has been rewarding for both my
students and me, and my students played a big part in the process. Since I
could make instant changes whenever someone found a spelling error or
difficult passage, I encouraged them to look for mistakes in the book by
giving them a bonus point each time they made a suggestion that resulted
in a change in the text. This had the double benefit of encouraging them to
read the text more carefully and of getting the text thoroughly reviewed by
its most important critics, students using it to learn computer science.

For the second half of the book on object-oriented programming, I knew


that someone with more real programming experience than I had would be

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
8

needed to do it right. The book sat in an unfinished state for the better part
of a year until the open source community once again provided the needed
means for its completion.

I received an email from Chris Meyers expressing interest in the book.


Chris is a professional programmer who started teaching a programming
course last year using Python at Lane Community College in Eugene,
Oregon. The prospect of teaching the course had led Chris to the book, and
he started helping out with it immediately. By the end of the school year
he had created a companion project on our Website at
http://openbookproject.net called *Python for Fun* and was working with
some of my most advanced students as a master teacher, guiding them
beyond where I could take them.

Introducing programming with Python

The process of translating and using How to Think Like a Computer


Scientist for the past two years has confirmed Python’s suitability for
teaching beginning students. Python greatly simplifies programming
examples and makes important programming ideas easier to teach.

The first example from the text illustrates this point. It is the traditional
hello, world program, which in the Java version of the book looks like this:

class Hello {

public static void main (String[] args) {


System.out.println ("Hello, world.");
}
}

in the Python version it becomes:

print "Hello, World!"

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
9

Even though this is a trivial example, the advantages of Python stand out.
Yorktown’s Computer Science I course has no prerequisites, so many of the
students seeing this example are looking at their first program. Some of
them are undoubtedly a little nervous, having heard that computer
programming is difficult to learn. The Java version has always forced me to
choose between two unsatisfying options: either to explain the class Hello,
public static void main, String[] args, {, and }, statements and risk
confusing or intimidating some of the students right at the start, or to tell
them, Just don’t worry about all of that stuff now; we will talk about it later,
and risk the same thing. The educational objectives at this point in the
course are to introduce students to the idea of a programming statement
and to get them to write their first program, thereby introducing them to
the programming environment. The Python program has exactly what is
needed to do these things, and nothing more.

Comparing the explanatory text of the program in each version of the book
further illustrates what this means to the beginning student. There are
seven paragraphs of explanation of Hello, world! in the Java version; in the
Python version, there are only a few sentences. More importantly, the
missing six paragraphs do not deal with the big ideas in computer
programming but with the minutia of Java syntax. I found this same thing
happening throughout the book. Whole paragraphs simply disappear from
the Python version of the text because Python’s much clearer syntax
renders them unnecessary.

Using a very high-level language like Python allows a teacher to postpone


talking about low-level details of the machine until students have the
background that they need to better make sense of the details. It thus
creates the ability to put first things first pedagogically. One of the best
examples of this is the way in which Python handles variables. In Java a
variable is a name for a place that holds a value if it is a built-in type, and a

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
10

reference to an object if it is not. Explaining this distinction requires a


discussion of how the computer stores data. Thus, the idea of a variable is
bound up with the hardware of the machine. The powerful and
fundamental concept of a variable is already difficult enough for beginning
students (in both computer science and algebra). Bytes and addresses do
not help the matter. In Python a variable is a name that refers to a thing.
This is a far more intuitive concept for beginning students and is much
closer to the meaning of variable that they learned in their math courses. I
had much less difficulty teaching variables this year than I did in the past,
and I spent less time helping students with problems using them.

Another example of how Python aids in the teaching and learning of


programming is in its syntax for functions. My students have always had a
great deal of difficulty understanding functions. The main problem centers
around the difference between a function definition and a function call, and
the related distinction between a parameter and an argument. Python
comes to the rescue with syntax that is nothing short of beautiful. Function
definitions begin with the keyword def, so I simply tell my students, When
you define a function, begin with def, followed by the name of the function
that you are defining; when you call a function, simply call (type) out its
name. Parameters go with definitions; arguments go with calls. There are
no return types, parameter types, or reference and value parameters to
get in the way, so I am now able to teach functions in less than half the
time that it previously took me, with better comprehension.

Using Python improved the effectiveness of our computer science program


for all students. I saw a higher general level of success and a lower level of
frustration than I experienced teaching with either C++ or Java. I moved
faster with better results. More students left the course with the ability to
create meaningful programs and with the positive attitude toward the
experience of programming that this engenders.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
11

Building a community

I have received email from all over the globe from people using this book
to learn or to teach programming. A user community has begun to emerge,
and many people have been contributing to the project by sending in
materials for the companion Website at
http://openbookproject.net/pybiblio.

With the continued growth of Python, I expect the growth in the user
community to continue and accelerate. The emergence of this user
community and the possibility it suggests for similar collaboration among
educators have been the most exciting parts of working on this project for
me. By working together, we can increase the quality of materials available
for our use and save valuable time. I invite you to join our community and
look forward to hearing from you. Please write to me at jeff@elkner.net.

Jeffrey Elkner
Governor’s Career and Technical Academy in Arlington
Arlington, Virginia

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
12

Contributor List
To paraphrase the philosophy of the Free Software Foundation, this book is
free like free speech, but not necessarily free like free pizza. It came about
because of a collaboration that would not have been possible without the
GNU Free Documentation License. So we would like to thank the Free
Software Foundation for developing this license and, of course, making it
available to us.

We would also like to thank the more than 100 sharp-eyed and thoughtful
readers who have sent us suggestions and corrections over the past few
years. In the spirit of free software, we decided to express our gratitude in
the form of a contributor list. Unfortunately, this list is not complete, but
we are doing our best to keep it up to date. It was also getting too large to
include everyone who sends in a typo or two. You have our gratitude, and
you have the personal satisfaction of making a book you found useful
better for you and everyone else who uses it. New additions to the list for
the 2nd edition will be those who have made on-going contributions.

If you have a chance to look through the list, you should realize that each
person here has spared you and all subsequent readers from the confusion
of a technical error or a less-than-transparent explanation, just by sending
us a note.

Impossible as it may seem after so many corrections, there may still be


errors in this book. If you should stumble across one, we hope you will take
a minute to contact us. The email address is jeff@elkner.net . Substantial
changes made due to your suggestions will add you to the next version of
the contributor list (unless you ask to be omitted). Thank you!

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
13

Second Edition

• A special thanks to the students in Jeff’s Computer Science class at


the HB-Woodlawn program during the 2007-2008 school year: James
Crowley, Joshua Eddy, Eric Larson, Brian McGrail, and Iliana Vazuka.
Your willingness to serve as beta-testers of the new chapters as they
were completed, and to endure frequent changes in response to your
feedback have proved invaluable. Thanks to you this is truly a
student tested text.
• Ammar Nabulsi sent in numerous corrections from Chapters 1 and 2.
• Aldric Giacomoni pointed out an error in our definition of the
Fibonacci sequence in Chapter 5.
• Roger Sperberg sent in several spelling corrections and pointed out a
twisted piece of logic in Chapter 3.
• Adele Goldberg sat down with Jeff at PyCon 2007 and gave him a list
of suggestions and corrections from throughout the book.
• Ben Bruno sent in corrections for chapters 4, 5, 6, and 7.
• Carl LaCombe pointed out that we incorrectly used the term
commutative in chapter 6 where symmetric was the correct term.
• Alessandro Montanile sent in corrections for errors in the code
examples and text in chapters 3, 12, 15, 17, 18, 19, and 20.
• Emanuele Rusconi found errors in chapters 4, 8, and 15.
• Michael Vogt reported an indentation error in an example in chapter
6, and sent in a suggestion for improving the clarity of the shell vs.
script section in chapter 1.

First Edition

• Lloyd Hugh Allen sent in a correction to Section 8.4.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
14

• Yvon Boulianne sent in a correction of a semantic error in Chapter 5.


• Fred Bremmer submitted a correction in Section 2.1.
• Jonah Cohen wrote the Perl scripts to convert the LaTeX source for
this book into beautiful HTML.
• Michael Conlon sent in a grammar correction in Chapter 2 and an
improvement in style in Chapter 1, and he initiated discussion on the
technical aspects of interpreters.
• Benoit Girard sent in a correction to a humorous mistake in Section
5.6.
• Courtney Gleason and Katherine Smith wrote horsebet.py, which was
used as a case study in an earlier version of the book. Their program
can now be found on the website.
• Lee Harr submitted more corrections than we have room to list here,
and indeed he should be listed as one of the principal editors of the
text.
• James Kaylin is a student using the text. He has submitted numerous
corrections.
• David Kershaw fixed the broken catTwice function in Section 3.10.
• Eddie Lam has sent in numerous corrections to Chapters 1, 2, and 3.
He also fixed the Makefile so that it creates an index the first time it
is run and helped us set up a versioning scheme.
• Man-Yong Lee sent in a correction to the example code in Section 2.4.
• David Mayo pointed out that the word unconsciously in Chapter 1
needed to be changed to subconsciously .
• Chris McAloon sent in several corrections to Sections 3.9 and 3.10.
• Matthew J. Moelter has been a long-time contributor who sent in
numerous corrections and suggestions to the book.
• Simon Dicon Montford reported a missing function definition and
several typos in Chapter 3. He also found errors in the increment

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
15

function in Chapter 13.


• John Ouzts corrected the definition of return value in Chapter 3.
• Kevin Parks sent in valuable comments and suggestions as to how to
improve the distribution of the book.
• David Pool sent in a typo in the glossary of Chapter 1, as well as kind
words of encouragement.
• Michael Schmitt sent in a correction to the chapter on files and
exceptions.
• Robin Shaw pointed out an error in Section 13.1, where the printTime
function was used in an example without being defined.
• Paul Sleigh found an error in Chapter 7 and a bug in Jonah Cohen’s
Perl script that generates HTML from LaTeX.
• Craig T. Snydal is testing the text in a course at Drew University. He
has contributed several valuable suggestions and corrections.
• Ian Thomas and his students are using the text in a programming
course. They are the first ones to test the chapters in the latter half of
the book, and they have make numerous corrections and
suggestions.
• Keith Verheyden sent in a correction in Chapter 3.
• Peter Winstanley let us know about a longstanding error in our Latin
in Chapter 3.
• Chris Wrobel made corrections to the code in the chapter on file I/O
and exceptions.
• Moshe Zadka has made invaluable contributions to this project. In
addition to writing the first draft of the chapter on Dictionaries, he
provided continual guidance in the early stages of the book.
• Christoph Zwerschke sent several corrections and pedagogic
suggestions, and explained the difference between gleich and selbe.
• James Mayer sent us a whole slew of spelling and typographical

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
16

errors, including two in the contributor list.


• Hayden McAfee caught a potentially confusing inconsistency between
two examples.
• Angel Arnal is part of an international team of translators working on
the Spanish version of the text. He has also found several errors in
the English version.
• Tauhidul Hoque and Lex Berezhny created the illustrations in Chapter
1 and improved many of the other illustrations.
• Dr. Michele Alzetta caught an error in Chapter 8 and sent some
interesting pedagogic comments and suggestions about Fibonacci
and Old Maid.
• Andy Mitchell caught a typo in Chapter 1 and a broken example in
Chapter 2.
• Kalin Harvey suggested a clarification in Chapter 7 and caught some
typos.
• Christopher P. Smith caught several typos and is helping us prepare
to update the book for Python 2.2.
• David Hutchins caught a typo in the Foreword.
• Gregor Lingl is teaching Python at a high school in Vienna, Austria. He
is working on a German translation of the book, and he caught a
couple of bad errors in Chapter 5.
• Julie Peters caught a typo in the Preface.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
17

1. The way of the program


The goal of this book is to teach you to think like a computer scientist. This
way of thinking combines some of the best features of mathematics,
engineering, and natural science. Like mathematicians, computer scientists
use formal languages to denote ideas (specifically computations). Like
engineers, they design things, assembling components into systems and
evaluating tradeoffs among alternatives. Like scientists, they observe the
behavior of complex systems, form hypotheses, and test predictions.

The single most important skill for a computer scientist is problem


solving. Problem solving means the ability to formulate problems, think
creatively about solutions, and express a solution clearly and accurately.
As it turns out, the process of learning to program is an excellent
opportunity to practice problem-solving skills. That’s why this chapter is
called, The way of the program.

On one level, you will be learning to program, a useful skill by itself. On


another level, you will use programming as a means to an end. As we go
along, that end will become clearer.

1.1. The Python programming language

The programming language you will be learning is Python. Python is an


example of a high-level language; other high-level languages you might
have heard of are C++, PHP, and Java.

As you might infer from the name high-level language, there are also low-
level languages, sometimes referred to as machine languages or
assembly languages. Loosely speaking, computers can only execute

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
18

programs written in low-level languages. Thus, programs written in a high-


level language have to be processed before they can run. This extra
processing takes some time, which is a small disadvantage of high-level
languages.

But the advantages are enormous. First, it is much easier to program in a


high-level language. Programs written in a high-level language take less
time to write, they are shorter and easier to read, and they are more likely
to be correct. Second, high-level languages are portable, meaning that
they can run on different kinds of computers with few or no modifications.
Low-level programs can run on only one kind of computer and have to be
rewritten to run on another.

Due to these advantages, almost all programs are written in high-level


languages. Low-level languages are used only for a few specialized
applications.

Two kinds of programs process high-level languages into low-level


languages: interpreters and compilers. An interpreter reads a high-level
program and executes it, meaning that it does what the program says. It
processes the program a little at a time, alternately reading lines and
performing computations.

A compiler reads the program and translates it completely before the


program starts running. In this case, the high-level program is called the
source code, and the translated program is called the object code or the
executable. Once a program is compiled, you can execute it repeatedly
without further translation.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
19

Many modern languages use both processes. They are first compiled into a
lower level language, called byte code, and then interpreted by a program
called a virtual machine. Python uses both processes, but because of the
way programmers interact with it, it is usually considered an interpreted
language.

There are two ways to use the Python interpreter: shell mode and script
mode. In shell mode, you type Python statements into the Python shell
and the interpreter immediately prints the result:

$ python
Python 2.5.1 (r251:54863, May 2 2007, 16:56:35)
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more
information.
>>> print 1 + 1
2

The first line of this example is the command that starts the Python
interpreter at a Unix command prompt. The next three lines are messages
from the interpreter. The fourth line starts with >>>, which is the Python
prompt. The interpreter uses the prompt to indicate that it is ready for
instructions. We typed print 1 + 1, and the interpreter replied 2.

Alternatively, you can write a program in a file and use the interpreter to
execute the contents of the file. Such a file is called a script. For example,
we used a text editor to create a file named firstprogram.py with the
following contents:

print 1 + 1

By convention, files that contain Python programs have names that end

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
20

with .py.

To execute the program, we have to tell the interpreter the name of the
script:

$ python firstprogram.py
2

These examples show Python being run from a Unix command line. In
other development environments, the details of executing programs may
differ. Also, most programs are more interesting than this one.

The examples in this book use both the Python interpreter and scripts. You
will be able to tell which is intended since shell mode examples will always
start with the Python prompt.

Working in shell mode is convenient for testing short bits of code because
you get immediate feedback. Think of it as scratch paper used to help you
work out problems. Anything longer than a few lines should be put into a
script.

1.2. What is a program?

A program is a sequence of instructions that specifies how to perform a


computation. The computation might be something mathematical, such as
solving a system of equations or finding the roots of a polynomial, but it
can also be a symbolic computation, such as searching and replacing text
in a document or (strangely enough) compiling a program.

The details look different in different languages, but a few basic


instructions appear in just about every language:

input

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
21

Get data from the keyboard, a file, or some other device.


output
Display data on the screen or send data to a file or other device.
math
Perform basic mathematical operations like addition and multiplication.
conditional execution
Check for certain conditions and execute the appropriate sequence of
statements.
repetition
Perform some action repeatedly, usually with some variation.

Believe it or not, that’s pretty much all there is to it. Every program you’ve
ever used, no matter how complicated, is made up of instructions that look
more or less like these. Thus, we can describe programming as the process
of breaking a large, complex task into smaller and smaller subtasks until
the subtasks are simple enough to be performed with one of these basic
instructions.

That may be a little vague, but we will come back to this topic later when
we talk about algorithms.

1.3. What is debugging?

Programming is a complex process, and because it is done by human


beings, it often leads to errors. For whimsical reasons, programming errors
are called bugs and the process of tracking them down and correcting
them is called debugging.

Three kinds of errors can occur in a program: syntax errors, runtime errors,
and semantic errors. It is useful to distinguish between them in order to
track them down more quickly.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
22

1.4. Syntax errors

Python can only execute a program if the program is syntactically correct;


otherwise, the process fails and returns an error message. syntax refers to
the structure of a program and the rules about that structure. For example,
in English, a sentence must begin with a capital letter and end with a
period. this sentence contains a syntax error. So does this one

For most readers, a few syntax errors are not a significant problem, which
is why we can read the poetry of e. e. cummings without spewing error
messages. Python is not so forgiving. If there is a single syntax error
anywhere in your program, Python will print an error message and quit,
and you will not be able to run your program. During the first few weeks of
your programming career, you will probably spend a lot of time tracking
down syntax errors. As you gain experience, though, you will make fewer
errors and find them faster.

1.5. Runtime errors

The second type of error is a runtime error, so called because the error
does not appear until you run the program. These errors are also called
exceptions because they usually indicate that something exceptional (and
bad) has happened.

Runtime errors are rare in the simple programs you will see in the first few
chapters, so it might be a while before you encounter one.

1.6. Semantic errors

The third type of error is the semantic error. If there is a semantic error in
your program, it will run successfully, in the sense that the computer will

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
23

not generate any error messages, but it will not do the right thing. It will do
something else. Specifically, it will do what you told it to do.

The problem is that the program you wrote is not the program you wanted
to write. The meaning of the program (its semantics) is wrong. Identifying
semantic errors can be tricky because it requires you to work backward by
looking at the output of the program and trying to figure out what it is
doing.

1.7. Experimental debugging

One of the most important skills you will acquire is debugging. Although it
can be frustrating, debugging is one of the most intellectually rich,
challenging, and interesting parts of programming.

In some ways, debugging is like detective work. You are confronted with
clues, and you have to infer the processes and events that led to the
results you see.

Debugging is also like an experimental science. Once you have an idea


what is going wrong, you modify your program and try again. If your
hypothesis was correct, then you can predict the result of the modification,
and you take a step closer to a working program. If your hypothesis was
wrong, you have to come up with a new one. As Sherlock Holmes pointed
out, When you have eliminated the impossible, whatever remains, however
improbable, must be the truth. (A. Conan Doyle, The Sign of Four)

For some people, programming and debugging are the same thing. That is,
programming is the process of gradually debugging a program until it does
what you want. The idea is that you should start with a program that does
something and make small modifications, debugging them as you go, so

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
24

that you always have a working program.

For example, Linux is an operating system that contains thousands of lines


of code, but it started out as a simple program Linus Torvalds used to
explore the Intel 80386 chip. According to Larry Greenfield, one of Linus’s
earlier projects was a program that would switch between printing AAAA
and BBBB. This later evolved to Linux (The Linux Users’ Guide Beta Version
1).

Later chapters will make more suggestions about debugging and other
programming practices.

1.8. Formal and natural languages

Natural languages are the languages that people speak, such as English,
Spanish, and French. They were not designed by people (although people
try to impose some order on them); they evolved naturally.

Formal languages are languages that are designed by people for specific
applications. For example, the notation that mathematicians use is a
formal language that is particularly good at denoting relationships among
numbers and symbols. Chemists use a formal language to represent the
chemical structure of molecules. And most importantly:

Programming languages are formal languages that have been


designed to express computations.

Formal languages tend to have strict rules about syntax. For example,
3+3=6 is a syntactically correct mathematical statement, but 3=+6$ is
not. H2O is a syntactically correct chemical name, but 2Zz is not.

Syntax rules come in two flavors, pertaining to tokens and structure.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
25

Tokens are the basic elements of the language, such as words, numbers,
and chemical elements. One of the problems with 3=+6$ is that $ is not a
legal token in mathematics (at least as far as we know). Similarly, 2Zz is

not legal because there is no element with the abbreviation Zz.

The second type of syntax rule pertains to the structure of a statement—


that is, the way the tokens are arranged. The statement 3=+6$ is
structurally illegal because you can’t place a plus sign immediately after an
equal sign. Similarly, molecular formulas have to have subscripts after the
element name, not before.

When you read a sentence in English or a statement in a formal language,


you have to figure out what the structure of the sentence is (although in a
natural language you do this subconsciously). This process is called
parsing.

For example, when you hear the sentence, The other shoe fell, you
understand that the other shoe is the subject and fell is the verb. Once you
have parsed a sentence, you can figure out what it means, or the
semantics of the sentence. Assuming that you know what a shoe is and
what it means to fall, you will understand the general implication of this
sentence.

Although formal and natural languages have many features in common —


tokens, structure, syntax, and semantics — there are many differences:

ambiguity
Natural languages are full of ambiguity, which people deal with by
using contextual clues and other information. Formal languages are
designed to be nearly or completely unambiguous, which means that
any statement has exactly one meaning, regardless of context.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
26

redundancy
In order to make up for ambiguity and reduce misunderstandings,
natural languages employ lots of redundancy. As a result, they are
often verbose. Formal languages are less redundant and more concise.
literalness
Natural languages are full of idiom and metaphor. If someone says,
The other shoe fell, there is probably no shoe and nothing falling.
Formal languages mean exactly what they say.

People who grow up speaking a natural language—everyone—often have a


hard time adjusting to formal languages. In some ways, the difference
between formal and natural language is like the difference between poetry
and prose, but more so:

Poetry
Words are used for their sounds as well as for their meaning, and the
whole poem together creates an effect or emotional response.
Ambiguity is not only common but often deliberate.
Prose
The literal meaning of words is more important, and the structure
contributes more meaning. Prose is more amenable to analysis than
poetry but still often ambiguous.
Programs
The meaning of a computer program is unambiguous and literal, and
can be understood entirely by analysis of the tokens and structure.

Here are some suggestions for reading programs (and other formal
languages). First, remember that formal languages are much more dense
than natural languages, so it takes longer to read them. Also, the structure
is very important, so it is usually not a good idea to read from top to
bottom, left to right. Instead, learn to parse the program in your head,

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
27

identifying the tokens and interpreting the structure. Finally, the details
matter. Little things like spelling errors and bad punctuation, which you can
get away with in natural languages, can make a big difference in a formal
language.

1.9. The first program

Traditionally, the first program written in a new language is called Hello,


World! because all it does is display the words, Hello, World! In Python, it
looks like this:

print "Hello, World!"

This is an example of a print statement, which doesn’t actually print


anything on paper. It displays a value on the screen. In this case, the result
is the words

Hello, World!

The quotation marks in the program mark the beginning and end of the
value; they don’t appear in the result.

Some people judge the quality of a programming language by the


simplicity of the Hello, World! program. By this standard, Python does
about as well as possible.

1.10. Glossary

algorithm
A general process for solving a category of problems.
bug
An error in a program.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
28

byte code
An intermediate language between source code and object code. Many
modern languages first compile source code into byte code and then
interpret the byte code with a program called a virtual machine.
compile
To translate a program written in a high-level language into a low-level
language all at once, in preparation for later execution.
debugging
The process of finding and removing any of the three kinds of
programming errors.
exception
Another name for a runtime error.
executable
Another name for object code that is ready to be executed.
formal language
Any one of the languages that people have designed for specific
purposes, such as representing mathematical ideas or computer
programs; all programming languages are formal languages.
high-level language
A programming language like Python that is designed to be easy for
humans to read and write.
interpret
To execute a program in a high-level language by translating it one
line at a time.
low-level language
A programming language that is designed to be easy for a computer to
execute; also called machine language or assembly language.
natural language
Any one of the languages that people speak that evolved naturally.
object code

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
29

The output of the compiler after it translates the program.


parse
To examine a program and analyze the syntactic structure.
portability
A property of a program that can run on more than one kind of
computer.
print statement
An instruction that causes the Python interpreter to display a value on
the screen.
problem solving
The process of formulating a problem, finding a solution, and
expressing the solution.
program
a sequence of instructions that specifies to a computer actions and
computations to be performed.
Python shell
An interactive user interface to the Python interpreter. The user of a
Python shell types commands at the prompt (>>>), and presses the
return key to send these commands immediately to the interpreter for
processing.
runtime error
An error that does not occur until the program has started to execute
but that prevents the program from continuing.
script
A program stored in a file (usually one that will be interpreted).
semantic error
An error in a program that makes it do something other than what the
programmer intended.
semantics
The meaning of a program.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
30

source code
A program in a high-level language before being compiled.
syntax
The structure of a program.
syntax error
An error in a program that makes it impossible to parse — and
therefore impossible to interpret.
token
One of the basic elements of the syntactic structure of a program,
analogous to a word in a natural language.

1.11. Exercises

1. Write an English sentence with understandable semantics but


incorrect syntax. Write another sentence which has correct syntax
but has semantic errors.

2. Start a Python shell. Type 1 + 2 and then hit return. Python evaluates
this expression, prints the result, and then prints another prompt. * is
the multiplication operator, and ** is the exponentiation operator.
Experiment by entering different expressions and recording what is
printed by the Python interpreter. What happens if you use the /
operator? Are the results what you expect? Explain.

3. Type 1 2 and then hit return. Python tries to evaluate the expression,
but it can’t because the expression is not syntactically legal. Instead,
it prints the error message:

File "<stdin>", line 1


12
^
SyntaxError: invalid syntax

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
31

In many cases, Python indicates where the syntax error occurred, but
it is not always right, and it doesn’t give you much information about
what is wrong.

So, for the most part, the burden is on you to learn the syntax rules.

In this case, Python is complaining because there is no operator


between the numbers.

Write down three more examples of strings that will produce error
messages when you enter them at the Python prompt. Explain why
each example is not valid Python syntax.

4. Type print ‘hello’. Python executes this statement, which has the
effect of printing the letters h-e-l-l-o. Notice that the quotation marks
that you used to enclose the string are not part of the output. Now
type "hello" and describe and explain your result.

5. Type print cheese without the quotation marks. The output will look
something like this:

Traceback (most recent call last):


File "<stdin>", line 1, in ?
NameError: name 'cheese' is not defined

This is a run-time error; specifically, it is a NameError, and even more


specifically, it is an error because the name cheese is not defined. If
you don’t know what that means yet, you will soon.

6. Type 'This is a test...' at the Python prompt and hit enter. Record
what happens.

Now create a python script named test1.py with the following


contents (be sure to save it before you try to run it):

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
32

'This is a test...'

What happens when you run this script? Now change the contents to:

print 'This is a test...'

and run it again.

What happened this time?

Whenever an expression is typed at the Python prompt, it is


evaluated and the result is printed on the line below. 'This is a test...'
is an expression, which evaluates to 'This is a test...' (just like the
expression 42 evaluates to 42). In a script, however, evaluations of
expressions are not sent to the program output, so it is necessary to
explicitly print it.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
33

2. Variables, expressions and


statements

2.1. Values and types

A value is one of the fundamental things — like a letter or a number —


that a program manipulates. The values we have seen so far are 2 (the
result when we added 1 + 1), and "Hello, World!".

These values belong to different types: 2 is an integer, and "Hello, World!"


is a string, so-called because it contains a string of letters. You (and the
interpreter) can identify strings because they are enclosed in quotation
marks.

The print statement also works for integers.

>>> print 4
4

If you are not sure what type a value has, the interpreter can tell you.

>>> type("Hello, World!")


<type 'str'>
>>> type(17)
<type 'int'>

Not surprisingly, strings belong to the type str and integers belong to the
type int. Less obviously, numbers with a decimal point belong to a type
called float, because these numbers are represented in a format called
floating-point.

>>> type(3.2)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
34

<type 'float'>

What about values like "17" and "3.2"? They look like numbers, but they
are in quotation marks like strings.

>>> type("17")
<type 'str'>
>>> type("3.2")
<type 'str'>

They’re strings.

Strings in Python can be enclosed in either single quotes (‘) or double


quotes (“):

>>> type('This is a string.')


<type 'str'>
>>> type("And so is this.")
<type 'str'>

Double quoted strings can contain single quotes inside them, as in "Bruce's
beard", and single quoted strings can have double quotes inside them, as
in 'The knights who say "Ni!"'.

When you type a large integer, you might be tempted to use commas
between groups of three digits, as in 1,000,000. This is not a legal integer
in Python, but it is legal:

>>> print 1,000,000


100

Well, that’s not what we expected at all! Python interprets 1,000,000 as a


list of three items to be printed. So remember not to put commas in your
integers.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
35

2.2. Variables

One of the most powerful features of a programming language is the ability


to manipulate variables. A variable is a name that refers to a value.

The assignment statement creates new variables and gives them


values:

>>> message = "What's up, Doc?"


>>> n = 17
>>> pi = 3.14159

This example makes three assignments. The first assigns the string
"What's up, Doc?" to a new variable named message. The second gives the
integer 17 to n, and the third gives the floating-point number 3.14159 to
pi.

The assignment operator, =, should not be confused with an equals sign


(even though it uses the same character). Assignment operators link a
name, on the left hand side of the operator, with a value, on the right hand
side. This is why you will get an error if you enter:

>>> 17 = n

A common way to represent variables on paper is to write the name with


an arrow pointing to the variable’s value. This kind of figure is called a
state diagram because it shows what state each of the variables is in
(think of it as the variable’s state of mind). This diagram shows the result
of the assignment statements:

The print statement also works with variables.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
36

>>> print message


What's up, Doc?
>>> print n
17
>>> print pi
3.14159

In each case the result is the value of the variable. Variables also have
types; again, we can ask the interpreter what they are.

>>> type(message)
<type 'str'>
>>> type(n)
<type 'int'>
>>> type(pi)
<type 'float'>

The type of a variable is the type of the value it refers to.

2.3. Variable names and keywords

Programmers generally choose names for their variables that are


meaningful — they document what the variable is used for.

Variable names can be arbitrarily long. They can contain both letters and
numbers, but they have to begin with a letter. Although it is legal to use
uppercase letters, by convention we don’t. If you do, remember that case
matters. Bruce and bruce are different variables.

The underscore character ( _) can appear in a name. It is often used in


names with multiple words, such as my_name or price_of_tea_in_china.

If you give a variable an illegal name, you get a syntax error:

>>> 76trombones = "big parade"


SyntaxError: invalid syntax
>>> more$ = 1000000

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
37

SyntaxError: invalid syntax


>>> class = "Computer Science 101"
SyntaxError: invalid syntax

76trombones is illegal because it does not begin with a letter. more$ is


illegal because it contains an illegal character, the dollar sign. But what’s
wrong with class?

It turns out that class is one of the Python keywords. Keywords define the
language’s rules and structure, and they cannot be used as variable
names.

Python has thirty-one keywords:

and as assert break class continue

def del elif else except exec

finally for from global if import

in is lambda not or pass

print raise return try while with

yield

You might want to keep this list handy. If the interpreter complains about
one of your variable names and you don’t know why, see if it is on this list.

2.4. Statements

A statement is an instruction that the Python interpreter can execute. We


have seen two kinds of statements: print and assignment.

When you type a statement on the command line, Python executes it and

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
38

displays the result, if there is one. The result of a print statement is a


value. Assignment statements don’t produce a result.

A script usually contains a sequence of statements. If there is more than


one statement, the results appear one at a time as the statements
execute.

For example, the script

print 1
x=2
print x

produces the output:

1
2

Again, the assignment statement produces no output.

2.5. Evaluating expressions

An expression is a combination of values, variables, and operators. If you


type an expression on the command line, the interpreter evaluates it and
displays the result:

>>> 1 + 1
2

The evaluation of an expression produces a value, which is why


expressions can appear on the right hand side of assignment statements. A
value all by itself is a simple expression, and so is a variable.

>>> 17
17
>>> x

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
39

Confusingly, evaluating an expression is not quite the same thing as


printing a value.

>>> message = "What's up, Doc?"


>>> message
"What's up, Doc?"
>>> print message
What's up, Doc?

When the Python shell displays the value of an expression, it uses the
same format you would use to enter a value. In the case of strings, that
means that it includes the quotation marks. But the print statement prints
the value of the expression, which in this case is the contents of the string.

In a script, an expression all by itself is a legal statement, but it doesn’t do


anything. The script

17
3.2
"Hello, World!"
1+1

produces no output at all. How would you change the script to display the
values of these four expressions?

2.6. Operators and operands

Operators are special symbols that represent computations like addition


and multiplication. The values the operator uses are called operands.

The following are all legal Python expressions whose meaning is more or
less clear:

20+32 hour-1 hour*60+minute minute/60 5**2 (5+9)*(15-

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
40

7)

The symbols +, -, and /, and the use of parenthesis for grouping, mean in
Python what they mean in mathematics. The asterisk (*) is the symbol for
multiplication, and ** is the symbol for exponentiation.

When a variable name appears in the place of an operand, it is replaced


with its value before the operation is performed.

Addition, subtraction, multiplication, and exponentiation all do what you


expect, but you might be surprised by division. The following operation has
an unexpected result:

>>> minute = 59
>>> minute/60
0

The value of minute is 59, and 59 divided by 60 is 0.98333, not 0. The


reason for the discrepancy is that Python is performing integer division.

When both of the operands are integers, the result must also be an integer,
and by convention, integer division always rounds down, even in cases like
this where the next integer is very close.

A possible solution to this problem is to calculate a percentage rather than


a fraction:

>>> minute*100/60
98

Again the result is rounded down, but at least now the answer is
approximately correct. Another alternative is to use floating-point division.
We’ll see in the chapter 4 how to convert integer values and variables to
floating-point values.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
41

2.7. Order of operations

When more than one operator appears in an expression, the order of


evaluation depends on the rules of precedence. Python follows the same
precedence rules for its mathematical operators that mathematics does.
The acronym PEMDAS is a useful way to remember the order of operations:

1. Parentheses have the highest precedence and can be used to force


an expression to evaluate in the order you want. Since expressions in
parentheses are evaluated first, 2 * (3-1) is 4, and (1+1)**(5-2) is 8.
You can also use parentheses to make an expression easier to read,
as in (minute * 100) / 60, even though it doesn’t change the result.
2. Exponentiation has the next highest precedence, so 2**1+1 is 3 and
not 4, and 3*1**3 is 3 and not 27.
3. Multiplication and Division have the same precedence, which is
higher than Addition and Subtraction, which also have the same
precedence. So 2*3-1 yields 5 rather than 4, and 2/3-1 is -1, not 1
(remember that in integer division, 2/3=0).
4. Operators with the same precedence are evaluated from left to right.
So in the expression minute*100/60, the multiplication happens first,
yielding 5900/60, which in turn yields 98. If the operations had been
evaluated from right to left, the result would have been 59*1, which
is 59, which is wrong.

2.8. Operations on strings

In general, you cannot perform mathematical operations on strings, even if


the strings look like numbers. The following are illegal (assuming that
message has type string):

message-1 "Hello"/123 message*"Hello" "15"+2

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
42

Interestingly, the + operator does work with strings, although it does not
do exactly what you might expect. For strings, the + operator represents
concatenation, which means joining the two operands by linking them
end-to-end. For example:

fruit = "banana"
baked_good = " nut bread"
print fruit + baked_good

The output of this program is banana nut bread. The space before the word
nut is part of the string, and is necessary to produce the space between
the concatenated strings.

The * operator also works on strings; it performs repetition. For example,


'Fun'*3 is 'FunFunFun'. One of the operands has to be a string; the other
has to be an integer.

On one hand, this interpretation of + and * makes sense by analogy with


addition and multiplication. Just as 4*3 is equivalent to 4+4+4, we expect
"Fun"*3 to be the same as "Fun"+"Fun"+"Fun", and it is. On the other
hand, there is a significant way in which string concatenation and
repetition are different from integer addition and multiplication. Can you
think of a property that addition and multiplication have that string
concatenation and repetition do not?

2.9. Input

There are two built-in functions in Python for getting keyboard input:

n = raw_input("Please enter your name: ")


print n
n = input("Enter a numerical expression: ")
print n

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
43

A sample run of this script would look something like this:

$ python tryinput.py
Please enter your name: Arthur, King of the Britons
Arthur, King of the Britons
Enter a numerical expression: 7 * 3
21

Each of these functions allows a prompt to be given to the function


between the parentheses.

2.10. Composition

So far, we have looked at the elements of a program — variables,


expressions, and statements — in isolation, without talking about how to
combine them.

One of the most useful features of programming languages is their ability


to take small building blocks and compose them. For example, we know
how to add numbers and we know how to print; it turns out we can do both
at the same time:

>>> print 17 + 3
20

In reality, the addition has to happen before the printing, so the actions
aren’t actually happening at the same time. The point is that any
expression involving numbers, strings, and variables can be used inside a
print statement. You’ve already seen an example of this:

print "Number of minutes since midnight: ", hour*60+minute

You can also put arbitrary expressions on the right-hand side of an


assignment statement:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
44

percentage = (minute * 100) / 60

This ability may not seem impressive now, but you will see other examples
where composition makes it possible to express complex computations
neatly and concisely.

Warning: There are limits on where you can use certain


expressions. For example, the left-hand side of an assignment
statement has to be a variable name, not an expression. So, the
following is illegal: minute+1 = hour.

2.11. Comments

As programs get bigger and more complicated, they get more difficult to
read. Formal languages are dense, and it is often difficult to look at a piece
of code and figure out what it is doing, or why.

For this reason, it is a good idea to add notes to your programs to explain
in natural language what the program is doing. These notes are called
comments, and they are marked with the # symbol:

# compute the percentage of the hour that has elapsed


percentage = (minute * 100) / 60

In this case, the comment appears on a line by itself. You can also put
comments at the end of a line:

percentage = (minute * 100) / 60 # caution: integer division

Everything from the # to the end of the line is ignored — it has no effect on
the program. The message is intended for the programmer or for future
programmers who might use this code. In this case, it reminds the reader
about the ever-surprising behavior of integer division.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
45

2.12. Glossary

value
A number or string (or other thing to be named later) that can be
stored in a variable or computed in an expression.
type
A set of values. The type of a value determines how it can be used in
expressions. So far, the types you have seen are integers (type int),
floating-point numbers (type float), and strings (type string).
int
A Python data type that holds positive and negative whole numbers.
str
A Python data type that holds a string of characters.
float
A Python data type which stores floating-point numbers. Floating-point
numbers are stored internally in two parts: a base and an exponent.
When printed in the standard format, they look like decimal numbers.
Beware of rounding errors when you use floats, and remember that
they are only approximate values.
variable
A name that refers to a value.
assignment statement

A statement that assigns a value to a name (variable). To the left of


the assignment operator, =, is a name. To the right of the assignment
operator is an expression which is evaluated by the Python interpreter
and then assigned to the name. The difference between the left and
right hand sides of the assignment statement is often confusing to
new programmers. In the following assignment:

n=n+1

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
46

n plays a very different role on each side of the =. On the right it is a


value and makes up part of the expression which will be evaluated by
the Python interpreter before assigning it to the name on the left.

assignment operator
= is Python’s assignment operator, which should not be confused with
the mathematical comparison operator using the same symbol.
state diagram
A graphical representation of a set of variables and the values to which
they refer.
variable name
A name given to a variable. Variable names in Python consist of a
sequence of letters (a..z, A..Z, and _) and digits (0..9) that begins with
a letter. In best programming practice, variable names should be
choosen so that they describe their use in the program, making the
program self documenting.
keyword
A reserved word that is used by the compiler to parse program; you
cannot use keywords like if, def, and while as variable names.
statement
An instruction that the Python interpreter can execute. Examples of
statements include the assignment statement and the print statement.
expression
A combination of variables, operators, and values that represents a
single result value.
evaluate
To simplify an expression by performing the operations in order to
yield a single value.
operator
A special symbol that represents a simple computation like addition,

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
47

multiplication, or string concatenation.


operand
One of the values on which an operator operates.
integer division
An operation that divides one integer by another and yields an integer.
Integer division yields only the whole number of times that the
numerator is divisible by the denominator and discards any remainder.
rules of precedence
The set of rules governing the order in which expressions involving
multiple operators and operands are evaluated.
concatenate
To join two operands end-to-end.
composition
The ability to combine simple expressions and statements into
compound statements and expressions in order to represent complex
computations concisely.
comment
Information in a program that is meant for other programmers (or
anyone reading the source code) and has no effect on the execution of
the program.

2.13. Exercises

1. Record what happens when you print an assignment statement:

>>> print n = 7

How about this?

>>> print 7 + 5

Or this?

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
48

>>> print 5.2, "this", 4 - 2, "that", 5/2.0

Can you think a general rule for what can follow the print statement?
What does the print statement return?

2. Take the sentence: All work and no play makes Jack a dull boy. Store
each word in a separate variable, then print out the sentence on one
line using print.

3. Add parenthesis to the expression 6 * 1 - 2 to change its value from 4


to -6.

4. Place a comment before a line of code that previously worked, and


record what happens when you rerun the program.

5. The difference between input and raw_input is that input evaluates


the input string and raw_input does not. Try the following in the
interpreter and record what happens:

>>> x = input()
3.14
>>> type(x)

>>> x = raw_input()
3.14
>>> type(x)

>>> x = input()
'The knights who say "ni!"'
>>> x

What happens if you try the example above without the quotation
marks?

>>> x = input()
The knights who say "ni!"
>>> x

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
49

>>> x = raw_input()
'The knights who say "ni!"'
>>> x

Describe and explain each result.

6. Start the Python interpreter and enter bruce + 4 at the prompt. This
will give you an error:

NameError: name 'bruce' is not defined

Assign a value to bruce so that bruce + 4 evaluates to 10.

7. Write a program (Python script) named madlib.py, which asks the


user to enter a series of nouns, verbs, adjectives, adverbs, plural
nouns, past tense verbs, etc., and then generates a paragraph which
is syntactically correct but semantically ridiculous (see
http://madlibs.org for examples).

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
50

3. Functions

3.1. Definitions and use

In the context of programming, a function is a named sequence of


statements that performs a desired operation. This operation is specified in
a function definition. In Python, the syntax for a function definition is:

def NAME( LIST OF PARAMETERS ):


STATEMENTS

You can make up any names you want for the functions you create, except
that you can’t use a name that is a Python keyword. The list of parameters
specifies what information, if any, you have to provide in order to use the
new function.

There can be any number of statements inside the function, but they have
to be indented from the def. In the examples in this book, we will use the
standard indentation of four spaces. Function definitions are the first of
several compound statements we will see, all of which have the same
pattern:

1. A header, which begins with a keyword and ends with a colon.


2. A body consisting of one or more Python statements, each indented
the same amount – 4 spaces is the Python standard – from the
header.

In a function definition, the keyword in the header is def, which is followed


by the name of the function and a list of parameters enclosed in
parentheses. The parameter list may be empty, or it may contain any
number of parameters. In either case, the parentheses are required.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
51

The first couple of functions we are going to write have no parameters, so


the syntax looks like this:

def new_line():
print # a print statement with no arguments prints a new
line

This function is named new_line. The empty parentheses indicate that it


has no parameters. Its body contains only a single statement, which
outputs a newline character. (That’s what happens when you use a print
command without any arguments.)

Defining a new function does not make the function run. To do that we
need a function call. Function calls contain the name of the function
being executed followed by a list of values, called arguments, which are
assigned to the parameters in the function definition. Our first examples
have an empty parameter list, so the function calls do not take any
arguments. Notice, however, that the parentheses are required in the
function call:

print "First Line."


new_line()
print "Second Line."

The output of this program is:

First line.

Second line.

The extra space between the two lines is a result of the new_line() function
call. What if we wanted more space between the lines? We could call the
same function repeatedly:

print "First Line."


new_line()

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
52

new_line()
new_line()
print "Second Line."

Or we could write a new function named three_lines that prints three new
lines:

def three_lines():
new_line()
new_line()
new_line()

print "First Line."


three_lines()
print "Second Line."

This function contains three statements, all of which are indented by four
spaces. Since the next statement is not indented, Python knows that it is
not part of the function.

You should notice a few things about this program:

• You can call the same procedure repeatedly. In fact, it is quite


common and useful to do so.
• You can have one function call another function; in this case
three_lines calls new_line.

So far, it may not be clear why it is worth the trouble to create all of these
new functions. Actually, there are a lot of reasons, but this example
demonstrates two:

1. Creating a new function gives you an opportunity to name a group of


statements. Functions can simplify a program by hiding a complex
computation behind a single command and by using English words in
place of arcane code.
2. Creating a new function can make a program smaller by eliminating

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
53

repetitive code. For example, a short way to print nine consecutive


new lines is to call three_lines three times.

Pulling together the code fragments from the previous section into a script
named tryme1.py, the whole program looks like this:

def new_line():
print

def three_lines():
new_line()
new_line()
new_line()

print "First Line."


three_lines()
print "Second Line."

This program contains two function definitions: new_line and three_lines.


Function definitions get executed just like other statements, but the effect
is to create the new function. The statements inside the function do not get
executed until the function is called, and the function definition generates
no output.

As you might expect, you have to create a function before you can execute
it. In other words, the function definition has to be executed before the first
time it is called.

3.2. Flow of execution

In order to ensure that a function is defined before its first use, you have to
know the order in which statements are executed, which is called the flow
of execution.

Execution always begins at the first statement of the program. Statements


are executed one at a time, in order from top to bottom.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
54

Function definitions do not alter the flow of execution of the program, but
remember that statements inside the function are not executed until the
function is called. Although it is not common, you can define one function
inside another. In this case, the inner definition isn’t executed until the
outer function is called.

Function calls are like a detour in the flow of execution. Instead of going to
the next statement, the flow jumps to the first line of the called function,
executes all the statements there, and then comes back to pick up where it
left off.

That sounds simple enough, until you remember that one function can call
another. While in the middle of one function, the program might have to
execute the statements in another function. But while executing that new
function, the program might have to execute yet another function!

Fortunately, Python is adept at keeping track of where it is, so each time a


function completes, the program picks up where it left off in the function
that called it. When it gets to the end of the program, it terminates.

What’s the moral of this sordid tale? When you read a program, don’t read
from top to bottom. Instead, follow the flow of execution.

3.3. Parameters, arguments, and the import statement

Most functions require arguments, values that control how the function
does its job. For example, if you want to find the absolute value of a
number, you have to indicate what the number is. Python has a built-in
function for computing the absolute value:

>>> abs(5)
5

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
55

>>> abs(-5)
5

In this example, the arguments to the abs function are 5 and -5.

Some functions take more than one argument. For example the built-in
function pow takes two arguments, the base and the exponent. Inside the
function, the values that are passed get assigned to variables called
parameters.

>>> pow(2, 3)
8
>>> pow(7, 4)
2401

Another built-in function that takes more than one argument is max.

>>> max(7, 11)


11
>>> max(4, 1, 17, 2, 12)
17
>>> max(3 * 11, 5**3, 512 - 9, 1024**0)
503

max can be sent any number of arguments, separated by commas, and will
return the maximum value sent. The arguments can be either simple
values or expressions. In the last example, 503 is returned, since it is
larger than 33, 125, and 1.

Here is an example of a user-defined function that has a parameter:

def print_twice(param):
print param, param

This function takes a single argument and assigns it to the parameter


named param. The value of the parameter (at this point we have no idea
what it will be) is printed twice, followed by a newline. The name param
was chosen to reinforce the idea that it is a parameter, but in general, you

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
56

will want to choose a name for your parameters that describes their use in
the function.

The interactive Python shell provides us with a convenient way to test our
functions. We can use the import statement to bring the functions we
have defined in a script into the interpreter session. To see how this works,
assume the print_twice function is defined in a script named chap03.py.
We can now test it interactively by importing it into our Python shell
session:

>>> from chap03 import *


>>> print_twice('Spam')
Spam Spam
>>> print_twice(5)
55
>>> print_twice(3.14159)
3.14159 3.14159

In a function call, the value of the argument is assigned to the


corresponding parameter in the function definition. In effect, it is if param
= 'Spam' is executed when print_twice('Spam') is called, param = 5 in
print_twice(5), and param = 3.14159 in print_twice(3.14159).

Any type of argument that can be printed can be sent to print_twice In the
first function call, the argument is a string. In the second, it’s an integer. In
the third, it’s a float.

As with built-in functions, we can use an expression as an argument for


print_twice:

>>> print_twice('Spam' * 4)
SpamSpamSpamSpam SpamSpamSpamSpam

'Spam'*4 is first evaluated to 'SpamSpamSpamSpam', which is then passed


as an argument to print_twice.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
57

3.4. Composition

Just as with mathematical functions, Python functions can be composed,


meaning that you use the result of one function as the input to another.

>>> print_twice(abs(-7))
77
>>> print_twice(max(3, 1, abs(-11), 7))
11 11

In the first example, abs(-7) evaluates to 7, which then becomes the


argument to print_twice. In the second example we have two levels of
composition, since abs(-11) is first evaluated to 11 before max(3, 1, 11, 7)
is evaluated to 11 and print_twice(11) then displays the result.

We can also use a variable as an argument:

>>> sval = 'Eric, the half a bee.'


>>> print_twice(sval)
Eric, the half a bee. Eric, the half a bee.

Notice something very important here. The name of the variable we pass
as an argument (sval) has nothing to do with the name of the parameter
(param). Again, it is as if param = sval is executed when print_twice(sval)
is called. It doesn’t matter what the value was named in the caller, in
print_twice it’s name is param.

3.5. Variables and parameters are local

When you create a local variable inside a function, it only exists inside
the function, and you cannot use it outside. For example:

def cat_twice(part1, part2):


cat = part1 + part2
print_twice(cat)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
58

This function takes two arguments, concatenates them, and then prints the
result twice. We can call the function with two strings:

>>> chant1 = "Pie Jesu domine, "


>>> chant2 = "Dona eis requiem."
>>> cat_twice(chant1, chant2)
Pie Jesu domine, Dona eis requiem. Pie Jesu domine, Dona eis
requiem.

When cat_twice terminates, the variable cat is destroyed. If we try to print


it, we get an error:

>>> print cat


NameError: name 'cat' is not defined

Parameters are also local. For example, outside the function print_twice,
there is no such thing as param. If you try to use it, Python will complain.

3.6. Stack diagrams

To keep track of which variables can be used where, it is sometimes useful


to draw a stack diagram. Like state diagrams, stack diagrams show the
value of each variable, but they also show the function to which each
variable belongs.

Each function is represented by a frame. A frame is a box with the name


of a function beside it and the parameters and variables of the function
inside it. The stack diagram for the previous example looks like this:

The order of the stack shows the flow of execution. print_twice was called

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
59

by cat_twice, and cat_twice was called by __main__, which is a special


name for the topmost function. When you create a variable outside of any
function, it belongs to __main__.

Each parameter refers to the same value as its corresponding argument.


So, part1 has the same value as chant1, part2 has the same value as
chant2, and param has the same value as cat.

If an error occurs during a function call, Python prints the name of the
function, and the name of the function that called it, and the name of the
function that called that, all the way back to the top most function.

To see how this works, create a Python script named tryme2.py that looks
like this:

def print_twice(param):
print param, param
print cat

def cat_twice(part1, part2):


cat = part1 + part2
print_twice(cat)

chant1 = "Pie Jesu domine, "


chant2 = "Dona eis requim."
cat_twice(chant1, chant2)

We’ve added the statement, print cat inside the print_twice function, but
cat is not defined there. Running this script will produce an error message
like this:

Traceback (innermost last):


File "tryme2.py", line 11, in <module>
cat_twice(chant1, chant2)
File "tryme2.py", line 7, in cat_twice
print_twice(cat)
File "tryme2.py", line 3, in print_twice
print cat

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
60

NameError: global name 'cat' is not defined

This list of functions is called a traceback. It tells you what program file
the error occurred in, and what line, and what functions were executing at
the time. It also shows the line of code that caused the error.

Notice the similarity between the traceback and the stack diagram. It’s not
a coincidence. In fact, another common name for a traceback is a stack
trace.

3.7. Glossary

function
A named sequence of statements that performs some useful operation.
Functions may or may not take parameters and may or may not
produce a result.
function definition
A statement that creates a new function, specifying its name,
parameters, and the statements it executes.
compound statement

A statement that consists of two parts:

1. header - which begins with a keyword determining the statement


type, and ends with a colon.
2. body - containing one or more statements indented the same
amount from the header.

The syntax of a compound statement looks like this:

keyword expression:
statement
statement ...

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
61

header
The first part of a compound statement. Headers begin with a keyword
and end with a colon (:)
body
The second part of a compound statement. The body consists of a
sequence of statements all indented the same amount from the
beginning of the header. The standard amount of indentation used
within the Python community is 4 spaces.
function call
A statement that executes a function. It consists of the name of the
function followed by a list of arguments enclosed in parentheses.
flow of execution
The order in which statements are executed during a program run.
parameter
A name used inside a function to refer to the value passed as an
argument.
import

A statement which permits functions and variables defined in a Python


script to be brought into the environment of another script or a
running Python shell.For example, assume the following is in a script
named tryme.py:

def print_thrice(thing):
print thing, thing, thing

n = 42
s = "And now for something completely different..."

Now begin a python shell from within the same directory where
tryme.py is located:

$ ls

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
62

tryme.py
$ python
>>>

Three names are defined in tryme.py: print_thrice, n, and s. If we try to


access any of these in the shell without first importing, we get an error:

>>> n
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined
>>> print_thrice("ouch!")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'print_thrice' is not defined

If we import everything from tryme.py, however, we can use


everything defined in it:

>>> from tryme import *


>>> n
42
>>> s
'And now for something completely different...'
>>> print_thrice("Yipee!")
Yipee! Yipee! Yipee!
>>>

Note that you do not include the .py from the script name in the
import statement.

argument
A value provided to a function when the function is called. This value is
assigned to the corresponding parameter in the function.
function composition
Using the output from one function call as the input to another.
local variable
A variable defined inside a function. A local variable can only be used

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
63

inside its function.


stack diagram
A graphical representation of a stack of functions, their variables, and
the values to which they refer.
frame
A box in a stack diagram that represents a function call. It contains the
local variables and parameters of the function.
traceback
A list of the functions that are executing, printed when a runtime error
occurs. A traceback is also commonly refered to as a stack trace, since
it lists the functions in the order in which they are stored in the runtime
stack.

3.8. Exercises

1. Using a text editor, create a Python script named tryme3.py . Write a


function in this file called nine_lines that uses three_lines to print nine
blank lines. Now add a function named clear_screen that prints out
twenty-five blank lines. The last line of your program should be a call
to clear_screen.

2. Move the last line of tryme3.py to the top of the program, so the
function call to clear_screen appears before the function definition.
Run the program and record what error message you get. Can you
state a rule about function definitions and function calls which
describes where they can appear relative to each other in a program?

3. Starting with a working version of tryme3.py , move the definition of


new_line after the definition of three_lines. Record what happens
when you run this program. Now move the definition of new_line
below a call to three_lines(). Explain how this is an example of the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
64

rule you stated in the previous exercise.

4. Fill in the body of the function definition for cat_n_times so that it will
print the string, s, n times:

def cat_n_times(s, n):


<fill in your code here>

Save this function in a script named import_test.py. Now at a unix


prompt, make sure you are in the same directory where the
import_test.py is located ( ls should show import_test.py). Start a
Python shell and try the following:

>>> from import_test import *


>>> cat_n_times('Spam', 7)
SpamSpamSpamSpamSpamSpamSpam

If all is well, your session should work the same as this one.
Experiment with other calls to cat_n_times until you feel comfortable
with how it works.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
65

4. Conditionals

4.1. The modulus operator

The modulus operator works on integers (and integer expressions) and


yields the remainder when the first operand is divided by the second. In
Python, the modulus operator is a percent sign (%). The syntax is the same
as for other operators:

>>> quotient = 7 / 3
>>> print quotient
2
>>> remainder = 7 % 3
>>> print remainder
1

So 7 divided by 3 is 2 with 1 left over.

The modulus operator turns out to be surprisingly useful. For example, you
can check whether one number is divisible by another—if x % y is zero,
then x is divisible by y.

Also, you can extract the right-most digit or digits from a number. For
example, x % 10 yields the right-most digit of x (in base 10). Similarly x %
100 yields the last two digits.

4.2. Boolean values and expressions

The Python type for storing true and false values is called bool, named
after the British mathematician, George Boole. George Boole created
Boolean algebra, which is the basis of all modern computer arithmetic.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
66

There are only two boolean values: True and False. Capitalization is
important, since true and false are not boolean values.

>>> type(True)
<type 'bool'>
>>> type(true)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined

A boolean expression is an expression that evaluates to a boolean value.


The operator == compares two values and produces a boolean value:

>>> 5 == 5
True
>>> 5 == 6
False

In the first statement, the two operands are equal, so the expression
evaluates to True; in the second statement, 5 is not equal to 6, so we get
False.

The == operator is one of the comparison operators; the others are:

x != y # x is not equal to y
x >y # x is greater than y
x <y # x is less than y
x >= y # x is greater than or equal to y
x <= y # x is less than or equal to y

Although these operations are probably familiar to you, the Python symbols
are different from the mathematical symbols. A common error is to use a
single equal sign (=) instead of a double equal sign (==). Remember that
= is an assignment operator and == is a comparison operator. Also, there
is no such thing as =< or =>.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
67

4.3. Logical operators

There are three logical operators: and, or, and not. The semantics
(meaning) of these operators is similar to their meaning in English. For
example, x > 0 and x < 10 is true only if x is greater than 0 and less than
10.

n % 2 == 0 or n % 3 == 0 is true if either of the conditions is true, that is,


if the number is divisible by 2 or 3.

Finally, the not operator negates a boolean expression, so not(x > y) is


true if (x > y) is false, that is, if x is less than or equal to y.

4.4. Conditional execution

In order to write useful programs, we almost always need the ability to


check conditions and change the behavior of the program accordingly.
Conditional statements give us this ability. The simplest form is the ** if
statement**:

if x > 0:
print "x is positive"

The boolean expression after the if statement is called the condition. If it


is true, then the indented statement gets executed. If not, nothing
happens.

The syntax for an if statement looks like this:

if BOOLEAN EXPRESSION:
STATEMENTS

As with the function definition from last chapter and other compound
statements, the if statement consists of a header and a body. The header

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
68

begins with the keyword if followed by a boolean expression and ends with
a colon (:).

The indented statements that follow are called a block. The first
unindented statement marks the end of the block. A statement block inside
a compound statement is called the body of the statement.

Each of the statements inside the body are executed in order if the boolean
expression evaluates to True. The entire block is skipped if the boolean
expression evaluates to False.

There is no limit on the number of statements that can appear in the body
of an if statement, but there has to be at least one. Occasionally, it is
useful to have a body with no statements (usually as a place keeper for
code you haven’t written yet). In that case, you can use the pass
statement, which does nothing.

if True: # This is always true


pass # so this is always executed, but it does nothing

4.5. Alternative execution

A second form of the if statement is alternative execution, in which there


are two possibilities and the condition determines which one gets
executed. The syntax looks like this:

if x % 2 == 0:
print x, "is even"
else:
print x, "is odd"

If the remainder when x is divided by 2 is 0, then we know that x is even,


and the program displays a message to that effect. If the condition is false,
the second set of statements is executed. Since the condition must be true

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
69

or false, exactly one of the alternatives will be executed. The alternatives


are called branches, because they are branches in the flow of execution.

As an aside, if you need to check the parity (evenness or oddness) of


numbers often, you might wrap this code in a function:

def print_parity(x):
if x % 2 == 0:
print x, "is even"
else:
print x, "is odd"

For any value of x, print_parity displays an appropriate message. When you


call it, you can provide any integer expression as an argument.

>>> print_parity(17)
17 is odd.
>>> y = 41
>>> print_parity(y+1)
42 is even.

4.6. Chained conditionals

Sometimes there are more than two possibilities and we need more than
two branches. One way to express a computation like that is a chained
conditional:

if x < y:
print x, "is less than", y
elif x > y:
print x, "is greater than", y
else:
print x, "and", y, "are equal"

elif is an abbreviation of else if . Again, exactly one branch will be


executed. There is no limit of the number of elif statements but only a
single (and optional) else statement is allowed and it must be the last

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
70

branch in the statement:

if choice == 'a':
function_a()
elif choice == 'b':
function_b()
elif choice == 'c':
function_c()
else:
print "Invalid choice."

Each condition is checked in order. If the first is false, the next is checked,
and so on. If one of them is true, the corresponding branch executes, and
the statement ends. Even if more than one condition is true, only the first
true branch executes.

4.7. Nested conditionals

One conditional can also be nested within another. We could have written
the trichotomy example as follows:

if x == y:
print x, "and", y, "are equal"
else:
if x < y:
print x, "is less than", y
else:
print x, "is greater than", y

The outer conditional contains two branches. The first branch contains a
simple output statement. The second branch contains another if
statement, which has two branches of its own. Those two branches are
both output statements, although they could have been conditional
statements as well.

Although the indentation of the statements makes the structure apparent,


nested conditionals become difficult to read very quickly. In general, it is a

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
71

good idea to avoid them when you can.

Logical operators often provide a way to simplify nested conditional


statements. For example, we can rewrite the following code using a single
conditional:

if 0 < x:
if x < 10:
print "x is a positive single digit."

The print statement is executed only if we make it past both the


conditionals, so we can use the and operator:

if 0 < x and x < 10:


print "x is a positive single digit."

These kinds of conditions are common, so Python provides an alternative


syntax that is similar to mathematical notation:

if 0 < x < 10:


print "x is a positive single digit."

This condition is semantically the same as the compound boolean


expression and the nested conditional.

4.8. The return statement

The return statement allows you to terminate the execution of a function


before you reach the end. One reason to use it is if you detect an error
condition:

def print_square_root(x):
if x <= 0:
print "Positive numbers only, please."
return

result = x**0.5

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
72

print "The square root of", x, "is", result

The function print_square_root has a parameter named x. The first thing it


does is check whether x is less than or equal to 0, in which case it displays
an error message and then uses return to exit the function. The flow of
execution immediately returns to the caller, and the remaining lines of the
function are not executed.

4.9. Keyboard input

In Input we were introduced to Python’s built-in functions that get input


from the keyboard: raw_input and input. Now let’s look at these again in
greater depth.

When either of these functions are called, the program stops and waits for
the user to type something. When the user presses Return or the Enter
key, the program resumes and raw_input returns what the user typed as a
string:

>>> my_input = raw_input()


What are you waiting for?
>>> print my_input
What are you waiting for?

Before calling raw_input, it is a good idea to print a message telling the


user what to input. This message is called a prompt. We can supply a
prompt as an argument to raw_input:

>>> name = raw_input("What...is your name? ")


What...is your name? Arthur, King of the Britons!
>>> print name
Arthur, King of the Britons!

Notice that the prompt is a string, so it must be enclosed in quotation


marks.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
73

If we expect the response to be an integer, we can use the input function


which evaluates the response as a Python expression:

prompt = "What...is the airspeed velocity of an unladen


swallow?\n"
speed = input(prompt)

If the user types a string of digits, it is converted to an integer and


assigned to speed. Unfortunately, if the user types characters that do not
make up a valid Python expression, the program crashes:

>>> speed = input(prompt)


What...is the airspeed velocity of an unladen swallow?
What do you mean, an African or a European swallow?
...
SyntaxError: invalid syntax

In the last example, if the user had made the response a valid Python
expression by putting quotes around it, it would not have given an error:

>>> speed = input(prompt)


What...is the airspeed velocity of an unladen swallow?
"What do you mean, an African or a European swallow?"
>>> speed
'What do you mean, an African or a European swallow?'
>>>

To avoid this kind of error, it is a good idea to use raw_input to get a string
and then use conversion commands to convert it to other types.

4.10. Type conversion

Each Python type comes with a built-in command that attempts to convert
values of another type into that type. The int(ARGUMENT) command, for
example, takes any value and converts it to an integer, if possible, or
complains otherwise:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
74

>>> int("32")
32
>>> int("Hello")
ValueError: invalid literal for int() with base 10: 'Hello'

int can also convert floating-point values to integers, but remember that it
truncates the fractional part:

>>> int(-2.3)
-2
>>> int(3.99999)
3
>>> int("42")
42
>>> int(1.0)
1

The float(ARGUMENT) command converts integers and strings to floating-


point numbers:

>>> float(32)
32.0
>>> float("3.14159")
3.14159
>>> float(1)
1.0

It may seem odd that Python distinguishes the integer value 1 from the
floating-point value 1.0. They may represent the same number, but they
belong to different types. The reason is that they are represented
differently inside the computer.

The str(ARGUMENT) command converts any argument given to it to type


string:

>>> str(32)
'32'
>>> str(3.14149)
'3.14149'
>>> str(True)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
75

'True'
>>> str(true)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined

str(ARGUMENT) with work with any value and convert it into a string. As
mentioned earlier, True is boolean value; true is not.

For boolean values, the situation is especially interesting:

>>> bool(1)
True
>>> bool(0)
False
>>> bool("Ni!")
True
>>> bool("")
False
>>> bool(3.14159)
True
>>> bool(0.0)
False

Python assigns boolean values to values of other types. For numerical


types like integers and floating-points, zero values are false and non-zero
values are true. For strings, empty strings are false and non-empty strings
are true.

4.11. GASP

GASP (Graphics API for Students of Python) will enable us to write


programs involving graphics. Before you can use GASP, it needs to be
installed on your machine. If you are running Ubuntu GNU/Linux, see GASP
in Appendix A. Current instructions for installing GASP on other platforms
can be found at http://dev.laptop.org/pub/gasp/downloads.

To start gasp after it is installed, try the following:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
76

>>> from gasp import *


>>> begin_graphics()
>>> Circle((200, 200), 60)
Circle instance at (200, 200) with radius 60
>>> Line((100, 400), (580, 200))
Line instance from (100, 400) to (590, 250)
>>> Box((400, 350), 120, 100)
Box instance at (400, 350) with width 120 and height 100
>>> end_graphics()
>>>

Before the last command, which closes the graphics canvas, you should
see a graphics window that looks like this:

We will be using gasp from here on to illustrate (pun intended) computer


programming concepts and to add to our fun while learning. You can find
out more about the GASP module by reading Appendix B.

4.12. Glossary

modulus operator
An operator, denoted with a percent sign ( %), that works on integers
and yields the remainder when one number is divided by another.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
77

boolean value
There are exactly two boolean values: True and False. Boolean values
result when a boolean expression is evaluated by the Python
interepreter. They have type bool.
boolean expression
An expression that is either true or false.
comparison operator
One of the operators that compares two values: ==, !=, >, <, >=, and
<=.
logical operator
One of the operators that combines boolean expressions: and, or, and
not.
conditional statement
A statement that controls the flow of execution depending on some
condition. In Python the keywords if, elif, and else are used for
conditional statements.
condition
The boolean expression in a conditional statement that determines
which branch is executed.
block
A group of consecutive statements with the same indentation.
body
The block of statements in a compound statement that follows the
header.
branch
One of the possible paths of the flow of execution determined by
conditional execution.
chained conditional
A conditional branch with more than two possible flows of execution. In
Python chained conditionals are written with if ... elif ... else

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
78

statements.
nesting
One program structure within another, such as a conditional statement
inside a branch of another conditional statement.
prompt
A visual cue that tells the user to input data.
type conversion
An explicit statement that takes a value of one type and computes a
corresponding value of another type.

4.13. Exercises

1. Try to evaluate the following numerical expressions in your head,


then use the Python interpreter to check your results:

1. >>> 5 % 2
2. >>> 9 % 5
3. >>> 15 % 12
4. >>> 12 % 15
5. >>> 6 % 6
6. >>> 0 % 7
7. >>> 7 % 0

What happened with the last example? Why? If you were able to
correctly anticipate the computer’s response in all but the last one, it
is time to move on. If not, take time now to make up examples of
your own. Explore the modulus operator until you are confident you
understand how it works.

2. if x < y:
print x, "is less than", y
elif x > y:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
79

print x, "is greater than", y


else:
print x, "and", y, "are equal"

Wrap this code in a function called compare(x, y). Call compare three
times: one each where the first argument is less than, greater than,
and equal to the second argument.

3. To better understand boolean expressions, it is helpful to construct


truth tables. Two boolean expressions are logically equivalent if and
only if they have the same truth table.

The following Python script prints out the truth table for the any
boolean expression in two variables: p and q:

expression = raw_input("Enter a boolean expression in two


variables, p and q: ")

print " p q %s" % expression


length = len( " p q %s" % expression)
print length*"="

for p in True, False:


for q in True, False:
print "%-7s %-7s %-7s" % (p, q, eval(expression))

You will learn how this script works in later chapters. For now, you will
use it to learn about boolean expressions. Copy this program to a file
named p_and_q.py, then run it from the command line and give it: p
or q, when prompted for a boolean expression. You should get the
following output:

p q p or q
=====================
True True True
True False True
False True True
False False False

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
80

Now that we see how it works, let’s wrap it in a function to make it


easier to use:

def truth_table(expression):
print " p q %s" % expression
length = len( " p q %s" % expression)
print length*"="

for p in True, False:


for q in True, False:
print "%-7s %-7s %-7s" % (p, q, eval(expression))

We can import it into a Python shell and call truth_table with a string
containing our boolean expression in p and q as an argument:

>>> from p_and_q import *


>>> truth_table("p or q")
p q p or q
=====================
True True True
True False True
False True True
False False False
>>>

Use the truth_table functions with the following boolean expressions,


recording the truth table produced each time:

1. not(p or q)
2. p and q
3. not(p and q)
4. not(p) or not(q)
5. not(p) and not(q)

Which of these are logically equivalent?

4. Enter the following expressions into the Python shell:

True or False

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
81

True and False


not(False) and True
True or 7
False or 7
True and 0
False or 8
"happy" and "sad"
"happy" or "sad"
"" and "sad"
"happy" and ""

Analyze these results. What observations can you make about values
of different types and logical operators? Can you write these
observations in the form of simple rules about and and or
expressions?

5. if choice == 'a':
function_a()
elif choice == 'b':
function_b()
elif choice == 'c':
function_c()
else:
print "Invalid choice."

Wrap this code in a function called dispatch(choice). Then define


function_a, function_b, and function_c so that they print out a
message saying they were called. For example:

def function_a():
print "function_a was called..."

Put the four functions ( dispatch, function_a, function_b, and


function_c into a script named ch04e05.py. At the bottom of this
script add a call to dispatch('b'). Your output should be:

function_b was called...

Finally, modify the script so that user can enter ‘a’, ‘b’, or ‘c’. Test it

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
82

by importing your script into the Python shell.

6. Write a function named is_divisible_by_3 that takes a single integer


as an argument and prints “This number is divisible by three.” if the
argument is evenly divisible by 3 and “This number is not divisible by
three.” otherwise.

Now write a similar function named is_divisible_by_5.

7. Generalize the functions you wrote in the previous exercise into a


function named is_divisible_by_n(x, n) that takes two integer
arguments and prints out whether the first is divisible by the second.
Save this in a file named ch04e07.py. Import it into a shell and try it
out. A sample session might look like this:

>>> from ch04e07 import *


>>> is_divisible_by_n(20, 4)
Yes, 20 is divisible by 4
>>> is_divisible_by_n(21, 8)
No, 21 is not divisible by 8

8. What will be the output of the following?

if "Ni!":
print 'We are the Knights who say, "Ni!"'
else:
print "Stop it! No more of this!"

if 0:
print "And now for something completely different..."
else:
print "What's all this, then?"

Explain what happened and why it happened.

9. The following gasp script, in a file named house.py, draws a simple


house on a gasp canvas:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
83

from gasp import * # import everything from the


gasp library

begin_graphics() # open the graphics canvas

Box((20, 20), 100, 100) # the house


Box((55, 20), 30, 50) # the door
Box((40, 80), 20, 20) # the left window
Box((80, 80), 20, 20) # the right window
Line((20, 120), (70, 160)) # the left roof
Line((70, 160), (120, 120)) # the right roof

update_when('key_pressed') # keep the canvas open until


a key is pressed
end_graphics() # close the canvas (which would
happen
# anyway, since the script ends here, but
it
# is better to be explicit).

Run this script and confirm that you get a window that looks like this:

1. Wrap the house code in a function named draw_house().


2. Run the script now. Do you see a house? Why not?
3. Add a call to draw_house() at the botton of the script so that the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
84

house returns to the screen.


4. Parameterize the function with x and y parameters – the header
should then become def draw_house(x, y):, so that you can pass
in the location of the house on the canvas.
5. Use draw_house to place five houses on the canvas in different
locations.
10.Exploration: Read over Appendix B and write a script named
houses.py that produces the following when run:

hint: You will need to use a Polygon for the roof instead of two Lines
to get filled=True to work with it.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
85

5. Fruitful functions

5.1. Return values

The built-in functions we have used, such as abs, pow, and max, have
produced results. Calling each of these functions generates a value, which
we usually assign to a variable or use as part of an expression.

biggest = max(3, 7, 2, 5)
x = abs(3 - 11) + 10

But so far, none of the functions we have written has returned a value.

In this chapter, we are going to write functions that return values, which
we will call fruitful functions, for want of a better name. The first example
is area, which returns the area of a circle with the given radius:

def area(radius):
temp = 3.14159 * radius**2
return temp

We have seen the return statement before, but in a fruitful function the
return statement includes a return value. This statement means: Return
immediately from this function and use the following expression as a return
value. The expression provided can be arbitrarily complicated, so we could
have written this function more concisely:

def area(radius):
return 3.14159 * radius**2

On the other hand, temporary variables like temp often make debugging
easier.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
86

Sometimes it is useful to have multiple return statements, one in each


branch of a conditional. We have already seen the built-in abs, now we see
how to write our own:

def absolute_value(x):
if x < 0:
return -x
else:
return x

Since these return statements are in an alternative conditional, only one


will be executed. As soon as one is executed, the function terminates
without executing any subsequent statements.

Another way to write the above function is to leave out the else and just
follow the if condition by the second return statement.

def absolute_value(x):
if x < 0:
return -x
return x

Think about this version and convince yourself it works the same as the
first one.

Code that appears after a return statement, or any other place the flow of
execution can never reach, is called dead code.

In a fruitful function, it is a good idea to ensure that every possible path


through the program hits a return statement. The following version of
absolute_value fails to do this:

def absolute_value(x):
if x < 0:
return -x
elif x > 0:
return x

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
87

This version is not correct because if x happens to be 0, neither condition is


true, and the function ends without hitting a return statement. In this case,
the return value is a special value called None:

>>> print absolute_value(0)


None

None is the unique value of a type called the NoneType:

>>> type(None)

All Python functions return None whenever they do not return another
value.

5.2. Program development

At this point, you should be able to look at complete functions and tell what
they do. Also, if you have been doing the exercises, you have written some
small functions. As you write larger functions, you might start to have more
difficulty, especially with runtime and semantic errors.

To deal with increasingly complex programs, we are going to suggest a


technique called incremental development. The goal of incremental
development is to avoid long debugging sessions by adding and testing
only a small amount of code at a time.

As an example, suppose you want to find the distance between two points,
given by the coordinates (x1, y1) and (x2, y2). By the Pythagorean theorem,

the distance is:

The first step is to consider what a distance function should look like in

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
88

Python. In other words, what are the inputs (parameters) and what is the
output (return value)?

In this case, the two points are the inputs, which we can represent using
four parameters. The return value is the distance, which is a floating-point
value.

Already we can write an outline of the function:

def distance(x1, y1, x2, y2):


return 0.0

Obviously, this version of the function doesn’t compute distances; it always


returns zero. But it is syntactically correct, and it will run, which means that
we can test it before we make it more complicated.

To test the new function, we call it with sample values:

>>> distance(1, 2, 4, 6)
0.0

We chose these values so that the horizontal distance equals 3 and the
vertical distance equals 4; that way, the result is 5 (the hypotenuse of a 3-
4-5 triangle). When testing a function, it is useful to know the right answer.

At this point we have confirmed that the function is syntactically correct,


and we can start adding lines of code. After each incremental change, we
test the function again. If an error occurs at any point, we know where it
must be — in the last line we added.

A logical first step in the computation is to find the differences x2- x1and

y2- y1. We will store those values in temporary variables named dx and dy

and print them.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
89

def distance(x1, y1, x2, y2):


dx = x2 - x1
dy = y2 - y1
print "dx is", dx
print "dy is", dy
return 0.0

If the function is working, the outputs should be 3 and 4. If so, we know


that the function is getting the right parameters and performing the first
computation correctly. If not, there are only a few lines to check.

Next we compute the sum of squares of dx and dy:

def distance(x1, y1, x2, y2):


dx = x2 - x1
dy = y2 - y1
dsquared = dx**2 + dy**2
print "dsquared is: ", dsquared
return 0.0

Notice that we removed the print statements we wrote in the previous


step. Code like that is called scaffolding because it is helpful for building
the program but is not part of the final product.

Again, we would run the program at this stage and check the output (which
should be 25).

Finally, using the fractional exponent 0.5 to find the square root, we
compute and return the result:

def distance(x1, y1, x2, y2):


dx = x2 - x1
dy = y2 - y1
dsquared = dx**2 + dy**2
result = dsquared**0.5
return result

If that works correctly, you are done. Otherwise, you might want to print
the value of result before the return statement.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
90

When you start out, you should add only a line or two of code at a time. As
you gain more experience, you might find yourself writing and debugging
bigger chunks. Either way, the incremental development process can save
you a lot of debugging time.

The key aspects of the process are:

1. Start with a working program and make small incremental changes.


At any point, if there is an error, you will know exactly where it is.
2. Use temporary variables to hold intermediate values so you can
output and check them.
3. Once the program is working, you might want to remove some of the
scaffolding or consolidate multiple statements into compound
expressions, but only if it does not make the program difficult to read.

5.3. Composition

As you should expect by now, you can call one function from within
another. This ability is called composition.

As an example, we’ll write a function that takes two points, the center of
the circle and a point on the perimeter, and computes the area of the
circle.

Assume that the center point is stored in the variables xc and yc, and the
perimeter point is in xp and yp. The first step is to find the radius of the
circle, which is the distance between the two points. Fortunately, we’ve
just written a function, distance, that does just that, so now all we have to
do is use it:

radius = distance(xc, yc, xp, yp)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
91

The second step is to find the area of a circle with that radius and return it.
Again we will use one of our earlier functions:

result = area(radius)
return result

Wrapping that up in a function, we get:

def area2(xc, yc, xp, yp):


radius = distance(xc, yc, xp, yp)
result = area(radius)
return result

We called this function area2 to distinguish it from the area function


defined earlier. There can only be one function with a given name within a
given module.

The temporary variables radius and result are useful for development and
debugging, but once the program is working, we can make it more concise
by composing the function calls:

def area2(xc, yc, xp, yp):


return area(distance(xc, yc, xp, yp))

5.4. Boolean functions

Functions can return boolean values, which is often convenient for hiding
complicated tests inside functions. For example:

def is_divisible(x, y):


if x % y == 0:
return True
else:
return False

The name of this function is is_divisible. It is common to give boolean


functions names that sound like yes/no questions. is_divisible returns

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
92

either True or False to indicate whether the x is or is not divisible by y.

We can make the function more concise by taking advantage of the fact
that the condition of the if statement is itself a boolean expression. We can
return it directly, avoiding the if statement altogether:

def is_divisible(x, y):


return x % y == 0

This session shows the new function in action:

>>> is_divisible(6, 4)
False
>>> is_divisible(6, 3)
True

Boolean functions are often used in conditional statements:

if is_divisible(x, y):
print "x is divisible by y"
else:
print "x is not divisible by y"

It might be tempting to write something like:

if is_divisible(x, y) == True:

but the extra comparison is unnecessary.

5.5. The function type

A function is another type in Python, joining int, float, str, bool, and
NoneType.

>>> def func():


... return "function func was called..."
...
>>> type(func)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
93

<type 'function'>
>>>

Just like the other types, functions can be passed as arguments to other
functions:

def f(n):
return 3*n - 6

def g(n):
return 5*n + 2

def h(n):
return -2*n + 17

def doto(value, func):


return func(value)

print doto(7, f)
print doto(7, g)
print doto(7, h)

doto is called three times. 7 is the argument for value each time, and the
functions f, g, and h are passed in for func in turn. The output of this script
is:

15
37
3

This example is a bit contrived, but we will see situations later where it is
quite useful to pass a function to a function.

5.6. Programming with style

Readability is very important to programmers, since in practice programs


are read and modified far more often then they are written. All the code
examples in this book will be consistent with the Python Enhancement
Proposal 8 (PEP 8), a style guide developed by the Python community.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
94

We’ll have more to say about style as our programs become more
complex, but a few pointers will be helpful already:

• use 4 spaces for indentation


• imports should go at the top of the file
• separate function definitions with two blank lines
• keep function definitions together
• keep top level statements, including function calls, together at the
bottom of the program

5.7. Triple quoted strings

In addition to the single and double quoted strings we first saw in Values
and types, Python also has triple quoted strings:

>>> type("""This is a triple quoted string using 3 double


quotes.""")
<type 'str'>
>>> type('''This triple quoted strings uses 3 single quotes.''')
<type 'str'>
>>>

Triple quoted strings can contain both single and double quotes inside
them:

>>> print '''"Oh no", she exclaimed, "Ben's bike is broken!"'''


"Oh no", she exclaimed, "Ben's bike is broken!"
>>>

Finally, triple quoted strings can span multiple lines:

>>> message = """This message will


... span several
... lines."""
>>> print message
This message will

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
95

span several
lines.
>>>

5.8. Unit testing with doctest

It is a common best practice in software development these days to


include automatic unit testing of source code. Unit testing provides a way
to automatically verify that individual pieces of code, such as functions, are
working properly. This makes it possible to change the implimentation of a
function at a later time and quickly test that it still does what it was
intended to do.

Python has a built-in doctest module for easy unit testing. Doctests can be
written within a triple quoted string on the first line of the body of a
function or script. They consist of sample interpreter sessions with a series
of inputs to a Python prompt followed by the expected output from the
Python interpreter.

The doctest module automatically runs any statement begining with >>>
and compares the following line with the output from the interpreter.

To see how this works, put the following in a script named myfunctions.py:

def is_divisible_by_2_or_5(n):
"""
>>> is_divisible_by_2_or_5(8)
True
"""

if __name__ == '__main__':
import doctest
doctest.testmod()

The last three lines are what make doctest run. Put them at the bottom of
any file that includes doctests. We will explain how they work in Chapter 10

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
96

when we discuss modules.

Running the script will produce the following output:

$ python myfunctions.py
******************************************************************
****
File "myfunctions.py", line 3, in __main__.is_divisible_by_2_or_5
Failed example:
is_divisible_by_2_or_5(8)
Expected:
True
Got nothing
******************************************************************
****
1 items had failures:
1 of 1 in __main__.is_divisible_by_2_or_5
***Test Failed*** 1 failures.
$

This is an example of a failing test. The test says: if you call


is_divisible_by_2_or_5(8) the result should be True. Since
is_divisible_by_2_or_5 as written doesn’t return anything at all, the test
fails, and doctest tells us that it expected True but got nothing.

We can make this test pass by returning True:

def is_divisible_by_2_or_5(n):
"""
>>> is_divisible_by_2_or_5(8)
True
"""
return True

if __name__ == '__main__':
import doctest
doctest.testmod()

If we run it now, there will be no output, which indicates that the test
passed. Note again that the doctest string must be placed immediately
after the function definition header in order to run.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
97

To see more detailed out put, call the script with the -v command line
option:

$ python myfunctions.py -v
Trying:
is_divisible_by_2_or_5(8)
Expecting:
True
ok
1 items had no tests:
__main__
1 items passed all tests:
1 tests in __main__.is_divisible_by_2_or_5
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
$

While the test passed, our test suite is clearly inadequete, since
is_divisible_by_2_or_5 will now return True no matter what argument is
passed to it. Here is a completed version with a more complete test suite
and code that makes the tests pass:

def is_divisible_by_2_or_5(n):
"""
>>> is_divisible_by_2_or_5(8)
True
>>> is_divisible_by_2_or_5(7)
False
>>> is_divisible_by_2_or_5(5)
True
>>> is_divisible_by_2_or_5(9)
False
"""
return n % 2 == 0 or n % 5 == 0

if __name__ == '__main__':
import doctest
doctest.testmod()

Run this script now with the -v command line option and see what you get.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
98

5.9. Glossary

fruitful function
A function that yields a return value.
return value
The value provided as the result of a function call.
temporary variable
A variable used to store an intermediate value in a complex
calculation.
dead code
Part of a program that can never be executed, often because it
appears after a return statement.
None
A special Python value returned by functions that have no return
statement, or a return statement without an argument. None is the
only value of the type, NoneType.
incremental development
A program development plan intended to avoid debugging by adding
and testing only a small amount of code at a time.
scaffolding
Code that is used during program development but is not part of the
final version.
boolean function
A function that returns a boolean value.
composition (of functions)
Calling one function from within the body of another, or using the
return value of one function as an argument to the call of another.
unit testing
An automatic procedure used to validate that individual units of code
are working properly. Python has doctest built in for this purpose.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
99

5.10. Exercises

All of the exercises below should be added to a file named ch05.py that
contains the following at the bottom:

if __name__ == '__main__':
import doctest
doctest.testmod()

After completing each exercise in turn, run the program to confirm that the
doctests for your new function pass.

1. Write a compare function that returns 1 if a > b, 0 if a == b, and -1 if


a < b.

def compare(a, b):


"""
>>> compare(5, 4)
1
>>> compare(7, 7)
0
>>> compare(2, 3)
-1
>>> compare(42, 1)
1
"""
# Your function body should begin here.

Fill in the body of the function so the doctests pass.

2. Use incremental development to write a function called hypotenuse


that returns the length of the hypotenuse of a right triangle given the
lengths of the two legs as parameters. Record each stage of the
incremental development process as you go.

def hypotenuse(a, b):


"""
>>> hypotenuse(3, 4)
5.0

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
100

>>> hypotenuse(12, 5)
13.0
>>> hypotenuse(7, 24)
25.0
>>> hypotenuse(9, 12)
15.0
"""

When you are finished add your completed function with the doctests
to ch05.py and confirm that the doctests pass.

3. Write a function slope(x1, y1, x2, y2) that returns the slope of the line
through the points (x1, y1) and (x2, y2). Be sure your implimentation
of slope can pass the following doctests:

def slope(x1, y1, x2, y2):


"""
>>> slope(5, 3, 4, 2)
1.0
>>> slope(1, 2, 3, 2)
0.0
>>> slope(1, 2, 3, 3)
0.5
>>> slope(2, 4, 1, 2)
2.0
"""

Then a call to slope in a new function named intercept(x1, y1, x2, y2)
that returns the y-intercept of the line through the points (x1, y1) and
(x2, y2).

def intercept(x1, y1, x2, y2):


"""
>>> intercept(1, 6, 3, 12)
3.0
>>> intercept(6, 1, 1, 6)
7.0
>>> intercept(4, 6, 12, 8)
5.0
"""

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
101

intercept should pass the doctests above.

4. Write a function called is_even(n) that takes an integer as an


argument and returns True if the argument is an even number and
False if it is odd.
Add your own doctests to this function.
5. Now write the function is_odd(n) that returns True when n is odd and
False otherwise. Include doctests for this function as you write it.
Finally, modify it so that it uses a call to is_even to determine if its
argument is an odd integer.
6. def is_factor(f, n):
"""
>>> is_factor(3, 12)
True
>>> is_factor(5, 12)
False
>>> is_factor(7, 14)
True
>>> is_factor(2, 14)
True
>>> is_factor(7, 15)
False
"""

Add a body to is_factor to make the doctests pass.

7. def is_multiple(m, n):


"""
>>> is_multiple(12, 3)
True
>>> is_multiple(12, 4)
True
>>> is_multiple(12, 5)
False
>>> is_multiple(12, 6)
True
>>> is_multiple(12, 7)
False
"""

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
102

Add a body to is_multiple to make the doctests pass. Can you find a
way to use is_factor in your definition of is_multiple?
8. def f2c(t):
"""
>>> f2c(212)
100
>>> f2c(32)
0
>>> f2c(-40)
-40
>>> f2c(36)
2
>>> f2c(37)
3
>>> f2c(38)
3
>>> f2c(39)
4
"""

Write a body for the function definition of f2c designed to return the
integer value of the nearest degree Celsius for given tempurature in
Fahrenheit. (hint: you may want to make use of the built-in function,
round. Try printing round.__doc__ in a Python shell and experimenting
with round until you are comfortable with how it works.)
9. def c2f(t):
"""
>>> c2f(0)
32
>>> c2f(100)
212
>>> c2f(-40)
-40
>>> c2f(12)
54
>>> c2f(18)
64
>>> c2f(-48)
-54
"""

Add a function body for c2f to convert from Celsius to Fahrenheit.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
103

6. Iteration

6.1. Multiple assignment

As you may have discovered, it is legal to make more than one assignment
to the same variable. A new assignment makes an existing variable refer to
a new value (and stop referring to the old value).

bruce = 5
print bruce,
bruce = 7
print bruce

The output of this program is 5 7, because the first time bruce is printed,
his value is 5, and the second time, his value is 7. The comma at the end of
the first print statement suppresses the newline after the output, which is
why both outputs appear on the same line.

Here is what multiple assignment looks like in a state diagram:

With multiple assignment it is especially important to distinguish between


an assignment operation and a statement of equality. Because Python uses
the equal sign (=) for assignment, it is tempting to interpret a statement
like a = b as a statement of equality. It is not!

First, equality is symmetric and assignment is not. For example, in


mathematics, if a = 7 then 7 = a. But in Python, the statement a = 7 is
legal and 7 = a is not.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
104

Furthermore, in mathematics, a statement of equality is always true. If a =


b now, then a will always equal b. In Python, an assignment statement can
make two variables equal, but they don’t have to stay that way:

a=5
b=a # a and b are now equal
a=3 # a and b are no longer equal

The third line changes the value of a but does not change the value of b, so
they are no longer equal. (In some programming languages, a different
symbol is used for assignment, such as <- or :=, to avoid confusion.)

6.2. Updating variables

One of the most common forms of multiple assignment is an update, where


the new value of the variable depends on the old.

x=x+1

This means get the current value of x, add one, and then update x with the
new value.

If you try to update a variable that doesn’t exist, you get an error, because
Python evaluates the expression on the right side of the assignment
operator before it assigns the resulting value to the name on the left:

>>> x = x + 1
Traceback (most recent call last):
File "", line 1, in
NameError: name 'x' is not defined

Before you can update a variable, you have to initialize it, usually with a
simple assignment:

>>> x = 0
>>> x = x + 1

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
105

>>>

Updating a variable by adding 1 is called an increment; subtracting 1 is


called a decrement.

6.3. The while statement

Computers are often used to automate repetitive tasks. Repeating identical


or similar tasks without making errors is something that computers do well
and people do poorly.

Repeated execution of a set of statements is called iteration. Because


iteration is so common, Python provides several language features to make
it easier. The first feature we are going to look at is the while statement.

Here is a function called countdown that demonstrates the use of the while
statement:

def countdown(n):
while n > 0:
print n
n = n-1
print "Blastoff!"

You can almost read the while statement as if it were English. It means,
While n is greater than 0, continue displaying the value of n and then
reducing the value of n by 1. When you get to 0, display the word Blastoff!

More formally, here is the flow of execution for a while statement:

1. Evaluate the condition, yielding False or True.


2. If the condition is false, exit the while statement and continue
execution at the next statement.
3. If the condition is true, execute each of the statements in the body

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
106

and then go back to step 1.

The body consists of all of the statements below the header with the same
indentation.

This type of flow is called a loop because the third step loops back around
to the top. Notice that if the condition is false the first time through the
loop, the statements inside the loop are never executed.

The body of the loop should change the value of one or more variables so
that eventually the condition becomes false and the loop terminates.
Otherwise the loop will repeat forever, which is called an infinite loop. An
endless source of amusement for computer scientists is the observation
that the directions on shampoo, Lather, rinse, repeat, are an infinite loop.

In the case of countdown, we can prove that the loop terminates because
we know that the value of n is finite, and we can see that the value of n
gets smaller each time through the loop, so eventually we have to get to 0.
In other cases, it is not so easy to tell. Look at the following function,
definied for all postitive integers n:

def sequence(n):
while n != 1:
print n,
if n % 2 == 0: # n is even
n=n/2
else: # n is odd
n=n*3+1

The condition for this loop is n != 1, so the loop will continue until n is 1,
which will make the condition false.

Each time through the loop, the program outputs the value of n and then
checks whether it is even or odd. If it is even, the value of n is divided by 2.
If it is odd, the value is replaced by n * 3 + 1. For example, if the starting

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
107

value (the argument passed to sequence) is 3, the resulting sequence is 3,


10, 5, 16, 8, 4, 2, 1.

Since n sometimes increases and sometimes decreases, there is no


obvious proof that n will ever reach 1, or that the program terminates. For
some particular values of n, we can prove termination. For example, if the
starting value is a power of two, then the value of n will be even each time
through the loop until it reaches 1. The previous example ends with such a
sequence, starting with 16.

Particular values aside, the interesting question is whether we can prove


that this program terminates for all values of n. So far, no one has been
able to prove it or disprove it!

6.4. Tracing a program

To write effective computer programs a programmer needs to develop the


ability to trace the execution of a computer program. Tracing involves
becoming the computer and following the flow of execution through a
sample program run, recording the state of all variables and any output the
program generates after each instruction is executed.

To understand this process, let’s trace the call to sequence(3) from the
previous section. At the start of the trace, we have a local variable, n (the
parameter), with an initial value of 3. Since 3 is not equal to 1, the while
loop body is executed. 3 is printed and 3 % 2 == 0 is evaluated. Since it
evaluates to False, the else branch is executed and 3 * 3 + 1 is evaluated
and assigned to n.

To keep track of all this as you hand trace a program, make a column
heading on a piece of paper for each variable created as the program runs

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
108

and another one for output. Our trace so far would look something like this:

n output
-- ------
3 3
10

Since 10 != 1 evaluates to True, the loop body is again executed, and 10 is


printed. 10 % 2 == 0 is true, so the if branch is executed and n becomes 5.
By the end of the trace we have:

n output
-- ------
3 3
10 10
5 5
16 16
8 8
4 4
2 2
1

Tracing can be a bit tedious and error prone (that’s why we get computers
to do this stuff in the first place!), but it is an essential skill for a
programmer to have. From this trace we can learn a lot about the way our
code works. We can observe that as soon as n becomes a power of 2, for
example, the program will require log2(n) executions of the loop body to

complete. We can also see that the final 1 will not be printed as output.

6.5. Counting digits

The following function counts the number of decimal digits in a positive


integer expressed in decimal format:

def num_digits(n):
count = 0
while n:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
109

count = count + 1
n = n / 10
return count

A call to num_digits(710) will return 3. Trace the execution of this function


call to convince yourself that it works.

This function demonstrates another pattern of computation called a


counter. The variable count is initialized to 0 and then incremented each
time the loop body is executed. When the loop exits, count contains the
result – the total number of times the loop body was executed, which is the
same as the number of digits.

If we wanted to only count digits that are either 0 or 5, adding a conditional


before incrementing the counter will do the trick:

def num_zero_and_five_digits(n):
count = 0
while n:
digit = n % 10
if digit == 0 or digit == 5:
count = count + 1
n = n / 10
return count

Confirm that num_zero_and_five_digits(1055030250) returns 7.

6.6. Abbreviated assignment

Incrementing a variable is so common that Python provides an abbreviated


syntax for it:

>>> count = 0
>>> count += 1
>>> count
1
>>> count += 1

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
110

>>> count
2
>>>

count += 1 is an abreviation for count = count + 1 . The increment value


does not have to be 1:

>>> n = 2
>>> n += 5
>>> n
7
>>>

There are also abbreviations for -=, *=, /=, and %=:

>>> n=2
>>> n *= 5
>>> n
10
>>> n -= 4
>>> n
6
>>> n /= 2
>>> n
3
>>> n %= 2
>>> n
1

6.7. Tables

One of the things loops are good for is generating tabular data. Before
computers were readily available, people had to calculate logarithms, sines
and cosines, and other mathematical functions by hand. To make that
easier, mathematics books contained long tables listing the values of these
functions. Creating the tables was slow and boring, and they tended to be
full of errors.

When computers appeared on the scene, one of the initial reactions was,

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
111

This is great! We can use the computers to generate the tables, so there
will be no errors. That turned out to be true (mostly) but shortsighted. Soon
thereafter, computers and calculators were so pervasive that the tables
became obsolete.

Well, almost. For some operations, computers use tables of values to get
an approximate answer and then perform computations to improve the
approximation. In some cases, there have been errors in the underlying
tables, most famously in the table the Intel Pentium used to perform
floating-point division.

Although a log table is not as useful as it once was, it still makes a good
example of iteration. The following program outputs a sequence of values
in the left column and 2 raised to the power of that value in the right
column:

x=1
while x < 13:
print x, '\t', 2**x
x += 1

The string '\t' represents a tab character. The backslash character in '\t'
indicates the beginning of an escape sequence. Escape sequences are
used to represent invisible characters like tabs and newlines. The sequence
\n represents a newline.

An escape sequence can appear anywhere in a string; in this example, the


tab escape sequence is the only thing in the string. How do you think you
represent a backslash in a string?

As characters and strings are displayed on the screen, an invisible marker


called the cursor keeps track of where the next character will go. After a
print statement, the cursor normally goes to the beginning of the next line.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
112

The tab character shifts the cursor to the right until it reaches one of the
tab stops. Tabs are useful for making columns of text line up, as in the
output of the previous program:

1 2
2 4
3 8
4 16
5 32
6 64
7 128
8 256
9 512
10 1024
11 2048
12 4096

Because of the tab characters between the columns, the position of the
second column does not depend on the number of digits in the first
column.

6.8. Two-dimensional tables

A two-dimensional table is a table where you read the value at the


intersection of a row and a column. A multiplication table is a good
example. Let’s say you want to print a multiplication table for the values
from 1 to 6.

A good way to start is to write a loop that prints the multiples of 2, all on
one line:

i=1
while i <= 6:
print 2 * i, ' ',
i += 1
print

The first line initializes a variable named i, which acts as a counter or loop

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
113

variable. As the loop executes, the value of i increases from 1 to 6. When i


is 7, the loop terminates. Each time through the loop, it displays the value
of 2 * i, followed by three spaces.

Again, the comma in the print statement suppresses the newline. After the
loop completes, the second print statement starts a new line.

The output of the program is:

2 4 6 8 10 12

So far, so good. The next step is to encapsulate and generalize.

6.9. Encapsulation and generalization

Encapsulation is the process of wrapping a piece of code in a function,


allowing you to take advantage of all the things functions are good for. You
have already seen two examples of encapsulation: print_parity in chapter
4; and is_divisible in chapter 5.

Generalization means taking something specific, such as printing the


multiples of 2, and making it more general, such as printing the multiples
of any integer.

This function encapsulates the previous loop and generalizes it to print


multiples of n:

def print_multiples(n):
i=1
while i <= 6:
print n * i, '\t',
i += 1
print

To encapsulate, all we had to do was add the first line, which declares the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
114

name of the function and the parameter list. To generalize, all we had to do
was replace the value 2 with the parameter n.

If we call this function with the argument 2, we get the same output as
before. With the argument 3, the output is:

3 6 9 12 15 18

With the argument 4, the output is:

4 8 12 16 20 24

By now you can probably guess how to print a multiplication table — by


calling print_multiples repeatedly with different arguments. In fact, we can
use another loop:

i=1
while i <= 6:
print_multiples(i)
i += 1

Notice how similar this loop is to the one inside print_multiples. All we did
was replace the print statement with a function call.

The output of this program is a multiplication table:

1 2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30
6 12 18 24 30 36

6.10. More encapsulation

To demonstrate encapsulation again, let’s take the code from the last
section and wrap it up in a function:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
115

def print_mult_table():
i=1
while i <= 6:
print_multiples(i)
i += 1

This process is a common development plan. We develop code by writing


lines of code outside any function, or typing them in to the interpreter.
When we get the code working, we extract it and wrap it up in a function.

This development plan is particularly useful if you don’t know how to divide
the program into functions when you start writing. This approach lets you
design as you go along.

6.11. Local variables

You might be wondering how we can use the same variable, i, in both
print_multiples and print_mult_table. Doesn’t it cause problems when one
of the functions changes the value of the variable?

The answer is no, because the i in print_multiples and the i in


print_mult_table are not the same variable.

Variables created inside a function definition are local; you can’t access a
local variable from outside its home function. That means you are free to
have multiple variables with the same name as long as they are not in the
same function.

The stack diagram for this program shows that the two variables named i
are not the same variable. They can refer to different values, and changing
one does not affect the other.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
116

The value of i in print_mult_table goes from 1 to 6. In the diagram it


happens to be 3. The next time through the loop it will be 4. Each time
through the loop, print_mult_table calls print_multiples with the current
value of i as an argument. That value gets assigned to the parameter n.

Inside print_multiples, the value of i goes from 1 to 6. In the diagram, it


happens to be 2. Changing this variable has no effect on the value of i in
print_mult_table.

It is common and perfectly legal to have different local variables with the
same name. In particular, names like i and j are used frequently as loop
variables. If you avoid using them in one function just because you used
them somewhere else, you will probably make the program harder to read.

6.12. More generalization

As another example of generalization, imagine you wanted a program that


would print a multiplication table of any size, not just the six-by-six table.
You could add a parameter to print_mult_table:

def print_mult_table(high):
i=1
while i <= high:
print_multiples(i)
i += 1

We replaced the value 6 with the parameter high. If we call


print_mult_table with the argument 7, it displays:

1 2 3 4 5 6

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
117

2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30
6 12 18 24 30 36
7 14 21 28 35 42

This is fine, except that we probably want the table to be square — with
the same number of rows and columns. To do that, we add another
parameter to print_multiples to specify how many columns the table should
have.

Just to be annoying, we call this parameter high, demonstrating that


different functions can have parameters with the same name (just like local
variables). Here’s the whole program:

def print_multiples(n, high):


i=1
while i <= high:
print n*i, '\t',
i += 1
print

def print_mult_table(high):
i=1
while i <= high:
print_multiples(i, high)
i += 1

Notice that when we added a new parameter, we had to change the first
line of the function (the function heading), and we also had to change the
place where the function is called in print_mult_table.

As expected, this program generates a square seven-by-seven table:

1 2 3 4 5 6 7
2 4 6 8 10 12 14
3 6 9 12 15 18 21
4 8 12 16 20 24 28
5 10 15 20 25 30 35

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
118

6 12 18 24 30 36 42
7 14 21 28 35 42 49

When you generalize a function appropriately, you often get a program


with capabilities you didn’t plan. For example, you might notice that,
because ab = ba, all the entries in the table appear twice. You could save
ink by printing only half the table. To do that, you only have to change one
line of print_mult_table. Change

print_multiples(i, high)

to

print_multiples(i, i)

and you get:

1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49

6.13. Functions

A few times now, we have mentioned all the things functions are good for.
By now, you might be wondering what exactly those things are. Here are
some of them:

1. Giving a name to a sequence of statements makes your program


easier to read and debug.
2. Dividing a long program into functions allows you to separate parts of
the program, debug them in isolation, and then compose them into a
whole.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
119

3. Functions facilitate the use of iteration.


4. Well-designed functions are often useful for many programs. Once
you write and debug one, you can reuse it.

6.14. Newton’s Method

Loops are often used in programs that compute numerical results by


starting with an approximate answer and iteratively improving it.

For example, one way of computing square roots is Newton’s method.


Suppose that you want to know the square root of n. If you start with
almost any approximation, you can compute a better approximation with
the following formula:

better = (approx + n/approx)/2

By repeatedly applying this formula until the better approximation is equal


to the previous one, we can write a function for computing the square root:

def sqrt(n):
approx = n/2.0
better = (approx + n/approx)/2.0
while better != approx:
approx = better
better = (approx + n/approx)/2.0
return approx

Try calling this function with 25 as an argument to confirm that it returns


5.0.

6.15. Algorithms

Newton’s method is an example of an algorithm: it is a mechanical


process for solving a category of problems (in this case, computing square

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
120

roots).

It is not easy to define an algorithm. It might help to start with something


that is not an algorithm. When you learned to multiply single-digit
numbers, you probably memorized the multiplication table. In effect, you
memorized 100 specific solutions. That kind of knowledge is not
algorithmic.

But if you were lazy, you probably cheated by learning a few tricks. For
example, to find the product of n and 9, you can write n - 1 as the first digit
and 10 - n as the second digit. This trick is a general solution for
multiplying any single-digit number by 9. That’s an algorithm!

Similarly, the techniques you learned for addition with carrying, subtraction
with borrowing, and long division are all algorithms. One of the
characteristics of algorithms is that they do not require any intelligence to
carry out. They are mechanical processes in which each step follows from
the last according to a simple set of rules.

In our opinion, it is embarrassing that humans spend so much time in


school learning to execute algorithms that, quite literally, require no
intelligence.

On the other hand, the process of designing algorithms is interesting,


intellectually challenging, and a central part of what we call programming.

Some of the things that people do naturally, without difficulty or conscious


thought, are the hardest to express algorithmically. Understanding natural
language is a good example. We all do it, but so far no one has been able
to explain how we do it, at least not in the form of an algorithm.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
121

6.16. Glossary

multiple assignment
Making more than one assignment to the same variable during the
execution of a program.
initialization (of a variable)
To initialize a variable is to give it an initial value, usually in the
context of multiple assignment. Since in Python variables don’t exist
until they are assigned values, they are initialized when they are
created. In other programming languages this is not the case, and
variables can be created without being initialized, in which case they
have either default or garbage values.
increment
Both as a noun and as a verb, increment means to increase by 1.
decrement
Decrease by 1.
iteration
Repeated execution of a set of programming statements.
loop
A statement or group of statements that execute repeatedly until a
terminating condition is satisfied.
infinite loop
A loop in which the terminating condition is never satisfied.
trace
To follow the flow of execution of a program by hand, recording the
change of state of the variables and any output produced.
counter
A variable used to count something, usually initialized to zero and
incremented in the body of a loop.
body

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
122

The statements inside a loop.


loop variable
A variable used as part of the terminating condition of a loop.
tab
A special character that causes the cursor to move to the next tab stop
on the current line.
newline
A special character that causes the cursor to move to the beginning of
the next line.
cursor
An invisible marker that keeps track of where the next character will
be printed.
escape sequence
An escape character, , followed by one or more printable characters
used to designate a nonprintable character.
encapsulate
To divide a large complex program into components (like functions)
and isolate the components from each other (by using local variables,
for example).
generalize
To replace something unnecessarily specific (like a constant value)
with something appropriately general (like a variable or parameter).
Generalization makes code more versatile, more likely to be reused,
and sometimes even easier to write.
development plan
A process for developing a program. In this chapter, we demonstrated
a style of development based on developing code to do simple, specific
things and then encapsulating and generalizing.
algorithm
A step-by-step process for solving a category of problems.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
123

6.17. Exercises

1. Write a single string that:

produces
this
output.

2. Add a print statement to the sqrt function defined in section 6.14 that
prints out better each time it is calculated. Call your modified function
with 25 as an argument and record the results.

3. Trace the execution of the last version of print_mult_table and figure


out how it works.

4. Write a function print_triangular_numbers(n) that prints out the first n


triangular numbers. A call to print_triangular_numbers(5) would
produce the following output:

1 1
2 3
3 6
4 10
5 15

(hint: use a web search to find out what a triangular number is.)

5. Open a file named ch06.py and add the following:

if __name__ == '__main__':
import doctest
doctest.testmod()

Write a function, is_prime, which takes a single integral argument and


returns True when the argument is a prime number and False
otherwise. Add doctests to your function as you develop it.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
124

6. What will num_digits(0) return? Modify it to return 1 for this case.


Why does a call to num_digits(-24) result in an infinite loop (hint:
-1/10 evaluates to -1)? Modify num_digits so that it works correctly
with any integer value. Add the following to the ch06.py file you
created in the previous exercise:

def num_digits(n):
"""
>>> num_digits(12345)
5
>>> num_digits(0)
1
>>> num_digits(-12345)
5
"""

Add your function body to num_digits and confirm that it passes the
doctests.

7. Add the following to the ch06.py:

def num_even_digits(n):
"""
>>> num_even_digits(123456)
3
>>> num_even_digits(2468)
4
>>> num_even_digits(1357)
0
>>> num_even_digits(2)
1
>>> num_even_digits(20)
2
"""

Write a body for num_even_digits so that it works as expected.

8. Add the following to ch06.py:

def print_digits(n):

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
125

"""
>>> print_digits(13789)
98731
>>> print_digits(39874613)
31647893
>>> print_digits(213141)
141312
"""

Write a body for print_digits so that it passes the given doctests.

9. Write a function sum_of_squares_of_digits that computes the sum of


the squares of the digits of an integer passed to it. For example,
sum_of_squares_of_digits(987) should return 194, since 9**2 + 8**2
+ 7**2 == 81 + 64 + 49 == 194.

def sum_of_squares_of_digits(n):
"""
>>> sum_of_squares_of_digits(1)
1
>>> sum_of_squares_of_digits(9)
81
>>> sum_of_squares_of_digits(11)
2
>>> sum_of_squares_of_digits(121)
6
>>> sum_of_squares_of_digits(987)
194
"""

Check your solution against the doctests above.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
126

7. Strings

7.1. A compound data type

So far we have seen five types: int, float, bool, NoneType and str. Strings
are qualitatively different from the other four because they are made up of
smaller pieces — characters.

Types that comprise smaller pieces are called compound data types.
Depending on what we are doing, we may want to treat a compound data
type as a single thing, or we may want to access its parts. This ambiguity is
useful.

The bracket operator selects a single character from a string:

>>> fruit = "banana"


>>> letter = fruit[1]
>>> print letter

The expression fruit[1] selects character number 1 from fruit. The variable
letter refers to the result. When we display letter, we get a surprise:

The first letter of "banana" is not a, unless you are a computer scientist.
For perverse reasons, computer scientists always start counting from zero.
The 0th letter ( zero-eth ) of "banana" is b. The 1th letter ( one-eth ) is a,
and the 2th ( two-eth ) letter is n.

If you want the zero-eth letter of a string, you just put 0, or any expression
with the value 0, in the brackets:

>>> letter = fruit[0]

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
127

>>> print letter


b

The expression in brackets is called an index. An index specifies a member


of an ordered set, in this case the set of characters in the string. The index
indicates which one you want, hence the name. It can be any integer
expression.

7.2. Length

The len function returns the number of characters in a string:

>>> fruit = "banana"


>>> len(fruit)
6

To get the last letter of a string, you might be tempted to try something
like this:

length = len(fruit)
last = fruit[length] # ERROR!

That won’t work. It causes the runtime error IndexError: string index out of
range. The reason is that there is no 6th letter in "banana". Since we
started counting at zero, the six letters are numbered 0 to 5. To get the
last character, we have to subtract 1 from length:

length = len(fruit)
last = fruit[length-1]

Alternatively, we can use negative indices, which count backward from


the end of the string. The expression fruit[-1] yields the last letter, fruit[-2]
yields the second to last, and so on.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
128

7.3. Traversal and the for loop

A lot of computations involve processing a string one character at a time.


Often they start at the beginning, select each character in turn, do
something to it, and continue until the end. This pattern of processing is
called a traversal. One way to encode a traversal is with a while
statement:

index = 0
while index < len(fruit):
letter = fruit[index]
print letter
index += 1

This loop traverses the string and displays each letter on a line by itself.
The loop condition is index < len(fruit), so when index is equal to the
length of the string, the condition is false, and the body of the loop is not
executed. The last character accessed is the one with the index len(fruit)-1,
which is the last character in the string.

Using an index to traverse a set of values is so common that Python


provides an alternative, simpler syntax — the for loop:

for char in fruit:


print char

Each time through the loop, the next character in the string is assigned to
the variable char. The loop continues until no characters are left.

The following example shows how to use concatenation and a for loop to
generate an abecedarian series. Abecedarian refers to a series or list in
which the elements appear in alphabetical order. For example, in Robert
McCloskey’s book Make Way for Ducklings, the names of the ducklings are
Jack, Kack, Lack, Mack, Nack, Ouack, Pack, and Quack. This loop outputs

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
129

these names in order:

prefixes = "JKLMNOPQ"
suffix = "ack"

for letter in prefixes:


print letter + suffix

The output of this program is:

Jack
Kack
Lack
Mack
Nack
Oack
Pack
Qack

Of course, that’s not quite right because Ouack and Quack are misspelled.
You’ll fix this as an exercise below.

7.4. String slices

A substring of a string is called a slice. Selecting a slice is similar to


selecting a character:

>>> s = "Peter, Paul, and Mary"


>>> print s[0:5]
Peter
>>> print s[7:11]
Paul
>>> print s[17:21]
Mary

The operator [n:m] returns the part of the string from the n-eth character
to the m-eth character, including the first but excluding the last. This
behavior is counterintuitive; it makes more sense if you imagine the
indices pointing between the characters, as in the following diagram:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
130

If you omit the first index (before the colon), the slice starts at the
beginning of the string. If you omit the second index, the slice goes to the
end of the string. Thus:

>>> fruit = "banana"


>>> fruit[:3]
'ban'
>>> fruit[3:]
'ana'

What do you think s[:] means?

7.5. String comparison

The comparison operators work on strings. To see if two strings are equal:

if word == "banana":
print "Yes, we have no bananas!"

Other comparison operations are useful for putting words in alphabetical


order:

if word < "banana":


print "Your word," + word + ", comes before banana."
elif word > "banana":
print "Your word," + word + ", comes after banana."
else:
print "Yes, we have no bananas!"

You should be aware, though, that Python does not handle upper and
lowercase letters the same way that people do. All the uppercase letters
come before all the lowercase letters. As a result:

Your word, Zebra, comes before banana.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
131

A common way to address this problem is to convert strings to a standard


format, such as all lowercase, before performing the comparison. A more
difficult problem is making the program realize that zebras are not fruit.

7.6. Strings are immutable

It is tempting to use the [] operator on the left side of an assignment, with


the intention of changing a character in a string. For example:

greeting = "Hello, world!"


greeting[0] = 'J' # ERROR!
print greeting

Instead of producing the output Jello, world!, this code produces the
runtime error TypeError: 'str' object doesn't support item assignment.

Strings are immutable, which means you can’t change an existing string.
The best you can do is create a new string that is a variation on the
original:

greeting = "Hello, world!"


newGreeting = 'J' + greeting[1:]
print newGreeting

The solution here is to concatenate a new first letter onto a slice of


greeting. This operation has no effect on the original string.

7.7. The in operator

The in operator tests if one string is a substring of another:

>>> 'p' in 'apple'


True
>>> 'i' in 'apple'
False

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
132

>>> 'ap' in 'apple'


True
>>> 'pa' in 'apple'
False

Note that a string is a substring of itself:

>>> 'a' in 'a'


True
>>> 'apple' in 'apple'
True

Combining the in operator with string concatenation using +, we can write


a function that removes all the vowels from a string:

def remove_vowels(s):
vowels = "aeiouAEIOU"
s_without_vowels = ""
for letter in s:
if letter not in vowels:
s_without_vowels += letter
return s_without_vowels

Test this function to confirm that it does what we wanted it to do.

7.8. A find function

What does the following function do?

def find(strng, ch):


index = 0
while index < len(strng):
if strng[index] == ch:
return index
index += 1
return -1

In a sense, find is the opposite of the [] operator. Instead of taking an index


and extracting the corresponding character, it takes a character and finds
the index where that character appears. If the character is not found, the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
133

function returns -1.

This is the first example we have seen of a return statement inside a loop.
If strng[index] == ch, the function returns immediately, breaking out of the
loop prematurely.

If the character doesn’t appear in the string, then the program exits the
loop normally and returns -1.

This pattern of computation is sometimes called a eureka traversal


because as soon as we find what we are looking for, we can cry Eureka!
and stop looking.

7.9. Looping and counting

The following program counts the number of times the letter a appears in a
string, and is another example of the counter pattern introduced in
Counting digits:

fruit = "banana"
count = 0
for char in fruit:
if char == 'a':
count += 1
print count

7.10. Optional parameters

To find the locations of the second or third occurence of a character in a


string, we can modify the find function, adding a third parameter for the
starting postion in the search string:

def find2(strng, ch, start):


index = start

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
134

while index < len(strng):


if strng[index] == ch:
return index
index += 1
return -1

The call find2('banana', 'a', 2) now returns 3, the index of the first
occurance of ‘a’ in ‘banana’ after index 2. What does find2('banana', 'n', 3)
return? If you said, 4, there is a good chance you understand how find2
works.

Better still, we can combine find and find2 using an optional parameter:

def find(strng, ch, start=0):


index = start
while index < len(strng):
if strng[index] == ch:
return index
index += 1
return -1

The call find('banana', 'a', 2) to this version of find behaves just like find2,
while in the call find('banana', 'a'), start will be set to the default value of
0.

Adding another optional parameter to find makes it search both forward


and backward:

def find(strng, ch, start=0, step=1):


index = start
while 0 <= index < len(strng):
if strng[index] == ch:
return index
index += step
return -1

Passing in a value of len(strng)-1 for start and -1 for step will make it
search toward the beginning of the string instead of the end. Note that we
needed to check for a lower bound for index in the while loop as well as an

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
135

upper bound to accomodate this change.

7.11. The string module

The string module contains useful functions that manipulate strings. As


usual, we have to import the module before we can use it:

>>> import string

To see what is inside it, use the dir function with the module name as an
argument.

>>> dir(string)

which will return the list of items inside the string module:

['Template', '_TemplateMetaclass', '__builtins__', '__doc__', '__file__',


'__name__', '_float', '_idmap', '_idmapL', '_int', '_long', '_multimap', '_re',
'ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'atof', 'atof_error', 'atoi',
'atoi_error', 'atol', 'atol_error', 'capitalize', 'capwords', 'center', 'count',
'digits', 'expandtabs', 'find', 'hexdigits', 'index', 'index_error', 'join',
'joinfields', 'letters', 'ljust', 'lower', 'lowercase', 'lstrip', 'maketrans',
'octdigits', 'printable', 'punctuation', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit',
'rstrip', 'split', 'splitfields', 'strip', 'swapcase', 'translate', 'upper',
'uppercase', 'whitespace', 'zfill']

To find out more about an item in this list, we can use the type command.
We need to specify the module name followed by the item using dot
notation.

>>> type(string.digits)
<type 'str'>
>>> type(string.find)
<type 'function'>

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
136

Since string.digits is a string, we can print it to see what it contains:

>>> print string.digits


0123456789

Not surprisingly, it contains each of the decimal digits.

string.find is a function which does much the same thing as the function we
wrote. To find out more about it, we can print out its docstring, __doc__,
which contains documentation on the function:

>>> print string.find.__doc__


find(s, sub [,start [,end]]) -> in

Return the lowest index in s where substring sub is found,


such that sub is contained within s[start,end]. Optional
arguments start and end are interpreted as in slice notation.

Return -1 on failure.

The parameters in square brackets are optional parameters. We can use


string.find much as we did our own find:

>>> fruit = "banana"


>>> index = string.find(fruit, "a")
>>> print index
1

This example demonstrates one of the benefits of modules — they help


avoid collisions between the names of built-in functions and user-defined
functions. By using dot notation we can specify which version of find we
want.

Actually, string.find is more general than our version. it can find substrings,
not just characters:

>>> string.find("banana", "na")


2

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
137

Like ours, it takes an additional argument that specifies the index at which
it should start:

>>> string.find("banana", "na", 3)


4

Unlike ours, its second optional parameter specifies the index at which the
search should end:

>>> string.find("bob", "b", 1, 2)


-1

In this example, the search fails because the letter b does not appear in
the index range from 1 to 2 (not including 2).

7.12. Character classification

It is often helpful to examine a character and test whether it is upper- or


lowercase, or whether it is a character or a digit. The string module
provides several constants that are useful for these purposes. One of
these, string.digits, we have already seen.

The string string.lowercase contains all of the letters that the system
considers to be lowercase. Similarly, string.uppercase contains all of the
uppercase letters. Try the following and see what you get:

print string.lowercase
print string.uppercase
print string.digits

We can use these constants and find to classify characters. For example, if
find(lowercase, ch) returns a value other than -1, then ch must be
lowercase:

def is_lower(ch):

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
138

return string.find(string.lowercase, ch) != -1

Alternatively, we can take advantage of the in operator:

def is_lower(ch):
return ch in string.lowercase

As yet another alternative, we can use the comparison operator:

def is_lower(ch):
return 'a' <= ch <= 'z'

If ch is between a and z, it must be a lowercase letter.

Another constant defined in the string module may surprise you when you
print it:

>>> print string.whitespace

Whitespace characters move the cursor without printing anything. They


create the white space between visible characters (at least on white
paper). The constant string.whitespace contains all the whitespace
characters, including space, tab (\t), and newline (\n).

There are other useful functions in the string module, but this book isn’t
intended to be a reference manual. On the other hand, the Python Library
Reference is. Along with a wealth of other documentation, it’s available
from the Python website, http://www.python.org.

7.13. String formatting

The most concise and powerful way to format a string in Python is to use
the string formatting operator, %, together with Python’s string formatting
operations. To see how this works, let’s start with a few examples:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
139

>>> "His name is %s." % "Arthur"


'His name is Arthur.'
>>> name = "Alice"
>>> age = 10
>>> "I am %s and I am %d years old." % (name, age)
'I am Alice and I am 10 years old.'
>>> n1 = 4
>>> n2 = 5
>>> "2**10 = %d and %d * %d = %f" % (2**10, n1, n2, n1 * n2)
'2**10 = 1024 and 4 * 5 = 20.000000'
>>>

The syntax for the string formatting operation looks like this:

"<FORMAT>" % (<VALUES>)

It begins with a format which contains a sequence of characters and


conversion specifications. Conversion specifications start with a %
operator. Following the format string is a single % and then a sequence of
values, one per conversion specification, seperated by commas and
enclosed in parenthesis. The parenthesis are optional if there is only a
single value.

In the first example above, there is a single conversion specification, %s,


which indicates a string. The single value, "Arthur", maps to it, and is not
enclosed in parenthesis.

In the second example, name has string value, "Alice", and age has integer
value, 10. These map to the two converstion specifications, %s and %d.
The d in the second converstion specification indicates that the value is a
decimal integer.

In the third example variables n1 and n2 have integer values 4 and 5


respectively. There are four converstion specifications in the format string:
three %d‘s and a %f. The f indicates that the value should be represented
as a floating point number. The four values that map to the four

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
140

converstion specifications are: 2**10, n1, n2, and n1 * n2.

s, d, and f are all the conversion types we will need for this book. To see a
complete list, see the String Formatting Operations section of the Python
Library Reference.

The following example illustrates the real utility of string formatting:

i=1
print "i\ti**2\ti**3\ti**5\ti**10\ti**20"
while i <= 10:
print i, '\t', i**2, '\t', i**3, '\t', i**5, '\t', i**10, '\t', i**20
i += 1

This program prints out a table of various powers of the numbers from 1 to
10. In its current form it relies on the tab character ( \t) to align the
columns of values, but this breaks down when the values in the table get
larger than the 8 character tab width:

i i**2 i**3 i**5 i**10 i**20


1 1 1 1 1 1
2 4 8 32 1024 1048576
3 9 27 243 59049 3486784401
4 16 64 1024 1048576 1099511627776
5 25 125 3125 9765625 95367431640625
6 36 216 7776 60466176 3656158440062976
7 49 343 16807 282475249 79792266297612001
8 64 512 32768 1073741824
1152921504606846976
9 81 729 59049 3486784401
12157665459056928801
10 100 1000 100000 10000000000
100000000000000000000

One possible solution would be to change the tab width, but the first
column already has more space than it needs. The best solution would be
to set the width of each column independently. As you may have guessed
by now, string formatting provides the solution:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
141

i=1
print "%-4s%-5s%-6s%-8s%-13s%-15s" % \
('i', 'i**2', 'i**3', 'i**5', 'i**10', 'i**20')
while i <= 10:
print "%-4d%-5d%-6d%-8d%-13d%-15d" % (i, i**2, i**3, i**5,
i**10, i**20)
i += 1

Running this version produces the following output:

i i**2 i**3 i**5 i**10 i**20


1 1 1 1 1 1
2 4 8 32 1024 1048576
3 9 27 243 59049 3486784401
4 16 64 1024 1048576 1099511627776
5 25 125 3125 9765625 95367431640625
6 36 216 7776 60466176 3656158440062976
7 49 343 16807 282475249 79792266297612001
8 64 512 32768 1073741824 1152921504606846976
9 81 729 59049 3486784401 12157665459056928801
10 100 1000 100000 10000000000 100000000000000000000

The - after each % in the converstion specifications indicates left


justification. The numerical values specify the minimum length, so %-13d is
a left justified number at least 13 characters wide.

7.14. Glossary

compound data type


A data type in which the values are made up of components, or
elements, that are themselves values.
index
A variable or value used to select a member of an ordered set, such as
a character from a string.
traverse
To iterate through the elements of a set, performing a similar
operation on each.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
142

slice
A part of a string (substring) specified by a range of indices. More
generally, a subsequence of any sequence type in Python can be
created using the slice operator (sequence[start:stop]).
immutable
A compound data types whose elements can not be assigned new
values.
optional parameter
A parameter written in a function header with an assignment to a
default value which it will receive if no corresponding argument is
given for it in the function call.
default value
The value given to an optional parameter if no argument for it is
provided in the function call.
dot notation
Use of the dot operator, ., to access functions inside a module.
docstring
A string constant on the first line of a function or module definition
(and as we will see later, in class and method definitions as well).
Docstrings provide a convinient way to associate documentation with
code. Docstrings are also used by the doctest module for automated
testing.
whitespace
Any of the characters that move the cursor without printing visible
characters. The constant string.whitespace contains all the white-space
characters.

7.15. Exercises

1. Modify:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
143

prefixes = "JKLMNOPQ"
suffix = "ack"

for letter in prefixes:


print letter + suffix

so that Ouack and Quack are spelled correctly.

2. Encapsulate

fruit = "banana"
count = 0
for char in fruit:
if char == 'a':
count += 1
print count

in a function named count_letters, and generalize it so that it accepts


the string and the letter as arguments.

3. Now rewrite the count_letters function so that instead of traversing


the string, it repeatedly calls find (the version from Optional
parameters), with the optional third parameter to locate new
occurences of the letter being counted.

4. Which version of is_lower do you think will be fastest? Can you think
of other reasons besides speed to prefer one version or the other?

5. Create a file named stringtools.py and put the following in it:

def reverse(s):
"""
>>> reverse('happy')
'yppah'
>>> reverse('Python')
'nohtyP'
>>> reverse("")
''
>>> reverse("P")
'P'

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
144

"""

if __name__ == '__main__':
import doctest
doctest.testmod()

Add a function body to reverse to make the doctests pass.

6. Add mirror to stringtools.py .

def mirror(s):
"""
>>> mirror("good")
'gooddoog'
>>> mirror("yes")
'yessey'
>>> mirror('Python')
'PythonnohtyP'
>>> mirror("")
''
>>> mirror("a")
'aa'
"""

Write a function body for it that will make it work as indicated by the
doctests.

7. Include remove_letter in stringtools.py .

def remove_letter(letter, strng):


"""
>>> remove_letter('a', 'apple')
'pple'
>>> remove_letter('a', 'banana')
'bnn'
>>> remove_letter('z', 'banana')
'banana'
>>> remove_letter('i', 'Mississippi')
'Msssspp'
"""

Write a function body for it that will make it work as indicated by the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
145

doctests.

8. Finally, add bodies to each of the following functions, one at a time

def is_palindrome(s):
"""
>>> is_palindrome('abba')
True
>>> is_palindrome('abab')
False
>>> is_palindrome('tenet')
True
>>> is_palindrome('banana')
False
>>> is_palindrome('straw warts')
True
"""

def count(sub, s):


"""
>>> count('is', 'Mississippi')
2
>>> count('an', 'banana')
2
>>> count('ana', 'banana')
2
>>> count('nana', 'banana')
1
>>> count('nanan', 'banana')
0
"""

def remove(sub, s):


"""
>>> remove('an', 'banana')
'bana'
>>> remove('cyc', 'bicycle')
'bile'
>>> remove('iss', 'Mississippi')
'Missippi'
>>> remove('egg', 'bicycle')
'bicycle'
"""

def remove_all(sub, s):


"""

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
146

>>> remove_all('an', 'banana')


'ba'
>>> remove_all('cyc', 'bicycle')
'bile'
>>> remove_all('iss', 'Mississippi')
'Mippi'
>>> remove_all('eggs', 'bicycle')
'bicycle'
"""

until all the doctests pass.

9. Try each of the following formatted string operations in a Python shell


and record the results:

1. “%s %d %f” % (5, 5, 5)


2. “%-.2f” % 3
3. “%-10.2f%-10.2f” % (7, 1.0/2)
4. print ” $%5.2fn $%5.2fn $%5.2f” % (3, 4.5, 11.2)
10.The following formatted strings have errors. Fix them:

1. “%s %s %s %s” % (‘this’, ‘that’, ‘something’)


2. “%s %s %s” % (‘yes’, ‘no’, ‘up’, ‘down’)
3. “%d %f %f” % (3, 3, ‘three’)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
147

8. Case Study: Catch

8.1. Getting started

In our first case study we will build a small video game using the facilities
in the GASP package. The game will shoot a ball across a window from left
to right and you will manipulate a mitt at the right side of the window to
catch it.

8.2. Using while to move a ball

while statements can be used with gasp to add motion to a program. The
following program moves a black ball across an 800 x 600 pixel graphics
canvas. Add this to a file named pitch.py:

from gasp import *

begin_graphics(800, 600, title="Catch",


background=color.YELLOW)
set_speed(120)

ball_x = 10
ball_y = 300
ball = Circle((ball_x, ball_y), 10, filled=True)
dx = 4
dy = 1

while ball_x < 810:


ball_x += dx
ball_y += dy
move_to(ball, (ball_x, ball_y))
update_when('next_tick')

end_graphics()

As the ball moves across the screen, you will see a graphics window that

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
148

looks like this:

Trace the first few iterations of this program to be sure you see what is
happening to the variables x and y.

Some new things to learn about GASP from this example:

• begin_graphics can take arguments for width, height, title, and


background color of the graphics canvas.
• set_speed can takes a frame rate in frames per second.
• Adding filled=True to Circle(...) makes the resulting circle solid.
• ball = Circle stores the circle (we will talk later about what a circle
actually is) in a variable named ball so that it can be referenced later.
• The move_to function in GASP allows a programmer to pass in a
shape (the ball in this case) and a location, and moves the shape to
that location.
• The update_when function is used to delay the action in a gasp
program util a specified event occurs. The event 'next_tick' waits until
the next frame, for an amount of time determined by the frame rate
set with set_speed. Other valid arguments for update_when are
'key_pressed' and 'mouse_clicked'.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
149

8.3. Varying the pitches

To make our game more interesting, we want to be able to vary the speed
and direction of the ball. GASP has a function, random_between(low, high),
that returns a random integer between low and high. To see how this
works, run the following program:

from gasp import *

i=0
while i < 10:
print random_between(-5, 5)
i += 1

Each time the function is called a more or less random integer is chosen
between -5 and 5. When we ran this program we got:

-2
-1
-4
1
-2
3
-5
-3
4
-5

You will probably get a different sequence of numbers.

Let’s use random_between to vary the direction of the ball. Replace the
line in pitch.py that assigns 1 to y:

dy = 1

with an assignment to a random number between -4 and 4:

dy = random_between(-4, 4)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
150

8.4. Making the ball bounce

Running this new version of the program, you will notice that ball
frequently goes off either the top or bottom edges of the screen before it
completes its journey. To prevent this, let’s make the ball bounce off the
edges by changing the sign of dy and sending the ball back in the opposite
verticle direction.

Add the following as the first line of the body of the while loop in pitch.py:

if ball_y >= 590 or ball_y <= 10:


dy *= -1

Run the program several times to see how it behaves.

8.5. The break statement

The break statement is used to immediately leave the body of a loop. The
following program impliments simple simple guessing game:

from gasp import *

number = random_between(1, 1000)


guesses = 1
guess = input("Guess the number between 1 and 1000: ")

while guess != number:


if guess > number:
print "Too high!"
else:
print "Too low!"
guess = input("Guess the number between 1 and 1000: ")
guesses += 1

print "\n\nCongratulations, you got it in %d guesses!\n\n" %


guesses

Using a break statement, we can rewrite this program to eliminate the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
151

duplication of the input statement:

from gasp import *

number = random_between(1, 1000)


guesses = 0

while True:
guess = input("Guess the number between 1 and 1000: ")
guesses += 1
if guess > number:
print "Too high!"
elif guess < number:
print "Too low!"
else:
print "\n\nCongratulations, you got it in %d guesses!\n\n" %
guesses
break

This program makes use of the mathematical law of trichotomy (given


real numbers a and b, a > b, a < b, or a = b). While both versions of the
program are 15 lines long, it could be argued that the logic in the second
version is clearer.

Put this program in a file named guess.py.

8.6. Responding to the keyboard

The following program creates a circle (or mitt ) which responds to


keyboard input. Pressing the j or k keys moves the mitt up and down,
respectively. Add this to a file named mitt.py:

from gasp import *

begin_graphics(800, 600, title="Catch",


background=color.YELLOW)
set_speed(120)

mitt_x = 780
mitt_y = 300

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
152

mitt = Circle((mitt_x, mitt_y), 20)

while True:
if key_pressed('k') and mitt_y <= 580:
mitt_y += 5
elif key_pressed('j') and mitt_y >= 20:
mitt_y -= 5

if key_pressed('escape'):
break

move_to(mitt, (mitt_x, mitt_y))


update_when('next_tick')

end_graphics()

Run mitt.py, pressing j and k to move up and down the screen.

8.7. Checking for collisions

The following program moves two balls toward each other from opposite
sides of the screen. When they collide , both balls disappear and the
program ends:

from gasp import *

def distance(x1, y1, x2, y2):


return ((x2 - x1)**2 + (y2 - y1)**2)**0.5

begin_graphics(800, 600, title="Catch",


background=color.YELLOW)
set_speed(120)

ball1_x = 10
ball1_y = 300
ball1 = Circle((ball1_x, ball1_y), 10, filled=True)
ball1_dx = 4

ball2_x = 790
ball2_y = 300
ball2 = Circle((ball2_x, ball2_y), 10)
ball2_dx = -4

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
153

while ball1_x < 810:


ball1_x += ball1_dx
ball2_x += ball2_dx
move_to(ball1, (ball1_x, ball1_y))
move_to(ball2, (ball2_x, ball2_y))
if distance(ball1_x, ball1_y, ball2_x, ball2_y) <= 20:
remove_from_screen(ball1)
remove_from_screen(ball2)
break
update_when('next_tick')

sleep(1)
end_graphics()

Put this program in a file named collide.py and run it.

8.8. Putting the pieces together

In order to combine the moving ball, moving mitt, and collision detection,
we need a single while loop that does each of these things in turn:

from gasp import *

def distance(x1, y1, x2, y2):


return ((x2 - x1)**2 + (y2 - y1)**2)**0.5

begin_graphics(800, 600, title="Catch",


background=color.YELLOW)
set_speed(120)

ball_x = 10
ball_y = 300
ball = Circle((ball_x, ball_y), 10, filled=True)
dx = 4
dy = random_between(-4, 4)

mitt_x = 780
mitt_y = 300
mitt = Circle((mitt_x, mitt_y), 20)

while True:
# move the ball

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
154

if ball_y >= 590 or ball_y <= 10:


dy *= -1
ball_x += dx
if ball_x > 810: # the ball has gone off the screen
break
ball_y += dy
move_to(ball, (ball_x, ball_y))

# check on the mitt


if key_pressed('k') and mitt_y <= 580:
mitt_y += 5
elif key_pressed('j') and mitt_y >= 20:
mitt_y -= 5

if key_pressed('escape'):
break

move_to(mitt, (mitt_x, mitt_y))

if distance(ball_x, ball_y, mitt_x, mitt_y) <= 30: # ball is


caught
remove_from_screen(ball)
break

update_when('next_tick')

end_graphics()

Put this program in a file named catch.py and run it several times. Be sure
to catch the ball on some runs and miss it on others.

8.9. Displaying text

This program displays scores for both a player and the computer on the
graphics screen. It generates a random number of 0 or 1 (like flipping a
coin) and adds a point to the player if the value is 1 and to the computer if
it is not. It then updates the display on the screen.

from gasp import *

begin_graphics(800, 600, title="Catch",

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
155

background=color.YELLOW)
set_speed(120)

player_score = 0
comp_score = 0

player = Text("Player: %d Points" % player_score, (10, 570),


size=24)
computer = Text("Computer: %d Points" % comp_score, (640,
570), size=24)

while player_score < 5 and comp_score < 5:


sleep(1)
winner = random_between(0, 1)
if winner:
player_score += 1
remove_from_screen(player)
player = Text("Player: %d Points" % player_score, (10, 570),
size=24)
else:
comp_score += 1
remove_from_screen(computer)
computer = Text("Computer: %d Points" % comp_score,
(640, 570), size=24)

if player_score == 5:
Text("Player Wins!", (340, 290), size=32)
else:
Text("Computer Wins!", (340, 290), size=32)

sleep(4)

end_graphics()

Put this program in a file named scores.py and run it.

We can now modify catch.py to diplay the winner. Immediately after the if
ball_x > 810: conditional, add the following:

Text("Computer Wins!", (340, 290), size=32)


sleep(2)

It is left as an excercise to display when the player wins.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
156

8.10. Abstraction

Our program is getting a bit complex. To make matters worse, we are


about to increase its complexity. The next stage of development requires a
nested loop. The outer loop will handle repeating rounds of play until
either the player or the computer reaches a winning score. The inner loop
will be the one we already have, which plays a single round, moving the
ball and mitt, and determining if a catch or a miss has occured.

Research suggests there is are clear limits to our ability to process


cognitive tasks (see George A. Miller’s The Magical Number Seven, Plus or
Minus Two: Some Limits on our Capacity for Processing Information). The
more complex a program becomes, the more difficult it is for even an
experienced programmer to develop and maintain.

To handle increasing complexity, we can wrap groups of related


statements in functions, using abstraction to hide program details. This
allows us to mentally treat a group of programming statements as a single
concept, freeing up mental bandwidth for further tasks. The ability to use
abstraction is one of the most powerful ideas in computer programming.

Here is a completed version of catch.py:

from gasp import *

COMPUTER_WINS = 1
PLAYER_WINS = 0
QUIT = -1

def distance(x1, y1, x2, y2):


return ((x2 - x1)**2 + (y2 - y1)**2)**0.5

def play_round():
ball_x = 10

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
157

ball_y = random_between(20, 280)


ball = Circle((ball_x, ball_y), 10, filled=True)
dx = 4
dy = random_between(-5, 5)

mitt_x = 780
mitt_y = random_between(20, 280)
mitt = Circle((mitt_x, mitt_y), 20)

while True:
if ball_y >= 590 or ball_y <= 10:
dy *= -1
ball_x += dx
ball_y += dy
if ball_x >= 810:
remove_from_screen(ball)
remove_from_screen(mitt)
return COMPUTER_WINS
move_to(ball, (ball_x, ball_y))

if key_pressed('k') and mitt_y <= 580:


mitt_y += 5
elif key_pressed('j') and mitt_y >= 20:
mitt_y -= 5

if key_pressed('escape'):
return QUIT

move_to(mitt, (mitt_x, mitt_y))

if distance(ball_x, ball_y, mitt_x, mitt_y) <= 30:


remove_from_screen(ball)
remove_from_screen(mitt)
return PLAYER_WINS

update_when('next_tick')

def play_game():
player_score = 0
comp_score = 0

while True:
pmsg = Text("Player: %d Points" % player_score, (10, 570),
size=24)
cmsg = Text("Computer: %d Points" % comp_score, (640,
570), size=24)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
158

sleep(3)
remove_from_screen(pmsg)
remove_from_screen(cmsg)

result = play_round()

if result == PLAYER_WINS:
player_score += 1
elif result == COMPUTER_WINS:
comp_score += 1
else:
return QUIT

if player_score == 5:
return PLAYER_WINS
elif comp_score == 5:
return COMPUTER_WINS

begin_graphics(800, 600, title="Catch",


background=color.YELLOW)
set_speed(120)

result = play_game()

if result == PLAYER_WINS:
Text("Player Wins!", (340, 290), size=32)
elif result == COMPUTER_WINS:
Text("Computer Wins!", (340, 290), size=32)

sleep(4)

end_graphics()

Some new things to learn from this example:

• Following good organizational practices makes programs easier to


read. Use the following organization in your programs:

• imports
• global constants
• function definitions

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
159

• main body of the program

• Symbolic constants like COMPUTER_WINS, PLAYER_WINS, and QUIT


can be used to enhance readability of the program. It is customary to
name constants with all capital letters. In Python it is up to the
programmer to never assign a new value to a constant , since the
language does not provide an easy way to enforce this (many other
programming languages do).

• We took the version of the program developed in section 8.8 and


wrapped it in a function named play_round(). play_round makes use
of the constants defined at the top of the program. It is much easier
to remember COMPUTER_WINS than it is the arbitrary numeric value
assigned to it.

• A new function, play_game(), creates variables for player_score and


comp_score. Using a while loop, it repeatedly calls play_round,
checking the result of each call and updating the score appropriately.
Finally, when either the player or computer reach 5 points,
play_game returns the winner to the main body of the program,
which then displays the winner and then quits.

• There are two variables named result—one in the play_game function


and one in the main body of the program. While they have the same
name, they are in different namespaces, and bear no relation to each
other. Each function creates its own namespace, and names defined
within the body of the function are not visible to code outside the
function body. Namespaces will be discussed in greater detail in the
next chapter.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
160

8.11. Glossary

random
Having no specific pattern. Unpredictable. Computers are designed to
be predicatable, and it is not possible to get a truly random value from
a computer. Certain functions produce sequences of values that
appear as if they were random, and it is these psuedorandom values
that we get from Python.
trichotomy
Given any real numbers a and b, exactly one of the following relations
holds: a < b, a > b, or a = b. Thus when you can establish that two of
the relations are false, you can assume the remaining one is true.
nested loop
A loop inside the body of another loop.
abstraction
Generalization by reducing the information content of a concept.
Functions in Python can be used to group a number of program
statements with a single name, abstracting out the details and making
the program easier to understand.
constant
A numerical value that does not change during the execution of a
program. It is conventional to use names with all uppercase letters to
repesent constants, though Python programs rely on the discipline of
the programmers to enforce this, since there is no language
mechanism to to support true constants in Python.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
161

8.12. Exercises

1. What happens when you press the key while running mitt.py? List the
two lines from the program that produce this behavior and explain
how they work.

2. What is the name of the counter variable in guess.py? With a proper


strategy, the maximum number of guesses required to arrive at the
correct number should be 11. What is this strategy?

3. What happens when the mitt in mitt.py gets to the top or bottom of
the graphics window? List the lines from the program that control this
behavior and explain in detail how they work.

4. Change the value of ball1_dx in collide.py to 2. How does the


program behave differently? Now change ball1_dx back to 4 and set
ball2_dx to -2. Explain in detail how these changes effect the
behavior of the program.

5. Comment out (put a # in front of the statement) the break statement


in collide.py. Do you notice any change in the behavior of the
program? Now also comment out the remove_from_screen(ball1)
statement. What happens now? Experiment with commenting and
uncommenting the two remove_from_screen statements and the
break statement until you can describe specifically how these
statement work together to produce the desired behavior in the
program.

6. Where can you add the lines

Text("Player Wins!", (340, 290), size=32)


sleep(2)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
162

to the version of catch.py in section 8.8 so that the program displays


this message when the ball is caught?

7. Trace the flow of execution in the final version of catch.py when you
press the escape key during the execution of play_round. What
happens when you press this key? Why?

8. List the main body of the final version of catch.py. Describe in detail
what each line of code does. Which statement calls the function that
starts the game?

9. Identify the function responsible for displaying the ball and the mitt.
What other operations are provided by this function?

10.Which function keeps track of the score? Is this also the function that
displays the score? Justify your answer by discussing specific parts of
the code which implement these operations.

8.13. Project: pong.py

Pong was one of the first commercial video games. With a capital P it is a
registered trademark, but pong is used to refer any of the table tennis like
paddle and ball video games.

catch.py already contains all the programming tools we need to develop


our own version of pong. Incrementally changing catch.py into pong.py is
the goal of this project, which you will accomplish by completing the
following series of exercises:

1. Copy catch.py to pong1.py and change the ball into a paddle by using
Box instead of the Circle. You can look at Appendix A for more
information on Box. Make the adjustments needed to keep the paddle

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
163

on the screen.

2. Copy pong1.py to pong2.py. Replace the distance function with a


boolean function hit(bx, by, r, px, py, h) that returns True when the
vertical coordinate of the ball (by) is between the bottom and top of
the paddle, and the horizontal location of the ball (bx) is less than or
equal to the radius (r) away from the front of the paddle. Use hit to
determine when the ball hits the paddle, and make the ball bounce
back in the opposite horizontal direction when hit returns True. Your
completed function should pass these doctests:

def hit(bx, by, r, px, py, h):


"""
>>> hit(760, 100, 10, 780, 100, 100)
False
>>> hit(770, 100, 10, 780, 100, 100)
True
>>> hit(770, 200, 10, 780, 100, 100)
True
>>> hit(770, 210, 10, 780, 100, 100)
False
"""

Finally, change the scoring logic to give the player a point when the
ball goes off the screen on the left.

3. Copy pong2.py to pong3.py. Add a new paddle on the left side of the
screen which moves up when 'a' is pressed and down when 's' is
pressed. Change the starting point for the ball to the center of the
screen, (400, 300), and make it randomly move to the left or right at
the start of each round.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
164

9. Lists
A list is an ordered set of values, where each value is identified by an
index. The values that make up a list are called its elements. Lists are
similar to strings, which are ordered sets of characters, except that the
elements of a list can have any type. Lists and strings — and other things
that behave like ordered sets — are called sequences.

9.1. List values

There are several ways to create a new list; the simplest is to enclose the
elements in square brackets ( [ and ]):

[10, 20, 30, 40]


["spam", "bungee", "swallow"]

The first example is a list of four integers. The second is a list of three
strings. The elements of a list don’t have to be the same type. The
following list contains a string, a float, an integer, and (mirabile dictu)
another list:

["hello", 2.0, 5, [10, 20]]

A list within another list is said to be nested.

Finally, there is a special list that contains no elements. It is called the


empty list, and is denoted [].

Like numeric 0 values and the empty string, the empty list is false in a
boolean expression:

>>> if []:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
165

... print 'This is true."...


else:
... print 'This is false."...

This is false.
>>>

With all these ways to create lists, it would be disappointing if we couldn’t


assign list values to variables or pass lists as parameters to functions. We
can:

>>> vocabulary = ["ameliorate", "castigate", "defenestrate"]


>>> numbers = [17, 123]
>>> empty = []
>>> print vocabulary, numbers, empty
['ameliorate', 'castigate', 'defenestrate'] [17, 123] []

9.2. Accessing elements

The syntax for accessing the elements of a list is the same as the syntax
for accessing the characters of a string—the bracket operator ( [] – not to
be confused with an empty list). The expression inside the brackets
specifies the index. Remember that the indices start at 0:

>>> print numbers[0]


17

Any integer expression can be used as an index:

>>> numbers[9-8]
5
>>> numbers[1.0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers

If you try to read or write an element that does not exist, you get a runtime
error:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
166

>>> numbers[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

If an index has a negative value, it counts backward from the end of the
list:

>>> numbers[-1]
5
>>> numbers[-2]
17
>>> numbers[-3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

numbers[-1] is the last element of the list, numbers[-2] is the second to


last, and numbers[-3] doesn’t exist.

It is common to use a loop variable as a list index.

horsemen = ["war", "famine", "pestilence", "death"]

i=0
while i < 4:
print horsemen[i]
i += 1

This while loop counts from 0 to 4. When the loop variable i is 4, the
condition fails and the loop terminates. So the body of the loop is only
executed when i is 0, 1, 2, and 3.

Each time through the loop, the variable i is used as an index into the list,
printing the i-eth element. This pattern of computation is called a list
traversal.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
167

9.3. List length

The function len returns the length of a list, which is equal to the number of
its elements. It is a good idea to use this value as the upper bound of a
loop instead of a constant. That way, if the size of the list changes, you
won’t have to go through the program changing all the loops; they will
work correctly for any size list:

horsemen = ["war", "famine", "pestilence", "death"]

i=0
num = len(horsemen)
while i < num:
print horsemen[i]
i += 1

The last time the body of the loop is executed, i is len(horsemen) - 1, which
is the index of the last element. When i is equal to len(horsemen), the
condition fails and the body is not executed, which is a good thing,
because len(horsemen) is not a legal index.

Although a list can contain another list, the nested list still counts as a
single element. The length of this list is 4:

['spam!', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]

9.4. List membership

in is a boolean operator that tests membership in a sequence. We used it


previously with strings, but it also works with lists and other sequences:

>>> horsemen = ['war', 'famine', 'pestilence', 'death']


>>> 'pestilence' in horsemen
True
>>> 'debauchery' in horsemen
False

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
168

Since pestilence is a member of the horsemen list, the in operator returns


True. Since debauchery is not in the list, in returns False.

We can use the not in combination with in to test whether an element is


not a member of a list:

>>> 'debauchery' not in horsemen


True

9.5. List operations

The + operator concatenates lists:

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print c
[1, 2, 3, 4, 5, 6]

Similarly, the * operator repeats a list a given number of times:

>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

The first example repeats [0] four times. The second example repeats the
list [1, 2, 3] three times.

9.6. List slices

The slice operations we saw with strings also work on lists:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']


>>> a_list[1:3]
['b', 'c']
>>> a_list[:4]

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
169

['a', 'b', 'c', 'd']


>>> a_list[3:]
['d', 'e', 'f']
>>> a_list[:]
['a', 'b', 'c', 'd', 'e', 'f']

9.7. The range function

Lists that contain consecutive integers are common, so Python provides a


simple way to create them:

>>> range(1, 5)
[1, 2, 3, 4]

The range function takes two arguments and returns a list that contains all
the integers from the first to the second, including the first but not the
second.

There are two other forms of range. With a single argument, it creates a
list that starts at 0:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

If there is a third argument, it specifies the space between successive


values, which is called the step size. This example counts from 1 to 10 by
steps of 2:

>>> range(1, 10, 2)


[1, 3, 5, 7, 9]

If the step size is negative, then start must be greater than stop

>>> range(20, 4, -5)


[20, 15, 10, 5]

or the result will be an empty list.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
170

>>> range(10, 20, -5)


[]

9.8. Lists are mutable

Unlike strings, lists are mutable, which means we can change their
elements. Using the bracket operator on the left side of an assignment, we
can update one of the elements:

>>> fruit = ["banana", "apple", "quince"]


>>> fruit[0] = "pear"
>>> fruit[-1] = "orange"
>>> print fruit
['pear', 'apple', 'orange']

The bracket operator applied to a list can appear anywhere in an


expression. When it appears on the left side of an assignment, it changes
one of the elements in the list, so the first element of fruit has been
changed from 'banana' to 'pear', and the last from 'quince' to 'orange'. An
assignment to an element of a list is called item assignment. Item
assignment does not work for strings:

>>> my_string = 'TEST'


>>> my_string[2] = 'X'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

but it does for lists:

>>> my_list = ['T', 'E', 'S', 'T']


>>> my_list[2] = 'X'
>>> my_list
['T', 'E', 'X', 'T']

With the slice operator we can update several elements at once:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
171

>>> a_list[1:3] = ['x', 'y']


>>> print a_list
['a', 'x', 'y', 'd', 'e', 'f']

We can also remove elements from a list by assigning the empty list to
them:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']


>>> a_list[1:3] = []
>>> print a_list
['a', 'd', 'e', 'f']

And we can add elements to a list by squeezing them into an empty slice
at the desired location:

>>> a_list = ['a', 'd', 'f']


>>> a_list[1:1] = ['b', 'c']
>>> print a_list
['a', 'b', 'c', 'd', 'f']
>>> a_list[4:4] = ['e']
>>> print a_list
['a', 'b', 'c', 'd', 'e', 'f']

9.9. List deletion

Using slices to delete list elements can be awkward, and therefore error-
prone. Python provides an alternative that is more readable.

del removes an element from a list:

>>> a = ['one', 'two', 'three']


>>> del a[1]
>>> a
['one', 'three']

As you might expect, del handles negative indices and causes a runtime
error if the index is out of range.

You can use a slice as an index for del:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
172

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']


>>> del a_list[1:5]
>>> print a_list
['a', 'f']

As usual, slices select all the elements up to, but not including, the second
index.

9.10. Objects and values

If we execute these assignment statements,

a = "banana"
b = "banana"

we know that a and b will refer to a string with the letters "banana". But we
can’t tell whether they point to the same string.

There are two possible states:

In one case, a and b refer to two different things that have the same value.
In the second case, they refer to the same thing. These things have names
—they are called objects. An object is something a variable can refer to.

Every object has a unique identifier, which we can obtain with the id
function. By printing the identifier of a and b, we can tell whether they
refer to the same object.

>>> id(a)
135044008
>>> id(b)
135044008

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
173

In fact, we get the same identifier twice, which means that Python only
created one string, and both a and b refer to it. Your actual id value will be
probably be different.

Interestingly, lists behave differently. When we create two lists, we get two
objects:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
135045528
>>> id(b)
135041704

So the state diagram looks like this:

a and b have the same value but do not refer to the same object.

9.11. Aliasing

Since variables refer to objects, if we assign one variable to another, both


variables refer to the same object:

>>> a = [1, 2, 3]
>>> b = a
>>> id(a) == id(b)
True

In this case, the state diagram looks like this:

Because the same list has two different names, a and b, we say that it is
aliased. Changes made with one alias affect the other:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
174

>>> b[0] = 5
>>> print a
[5, 2, 3]

Although this behavior can be useful, it is sometimes unexpected or


undesirable. In general, it is safer to avoid aliasing when you are working
with mutable objects. Of course, for immutable objects, there’s no
problem. That’s why Python is free to alias strings when it sees an
opportunity to economize.

9.12. Cloning lists

If we want to modify a list and also keep a copy of the original, we need to
be able to make a copy of the list itself, not just the reference. This process
is sometimes called cloning, to avoid the ambiguity of the word copy.

The easiest way to clone a list is to use the slice operator:

>>> a = [1, 2, 3]
>>> b = a[:]
>>> print b
[1, 2, 3]

Taking any slice of a creates a new list. In this case the slice happens to
consist of the whole list.

Now we are free to make changes to b without worrying about a:

>>> b[0] = 5
>>> print a
[1, 2, 3]

9.13. Lists and for loops

The for loop also works with lists. The generalized syntax of a for loop is:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
175

for VARIABLE in LIST:


BODY

This statement is equivalent to:

i=0
while i < len(LIST):
VARIABLE = LIST[i]
BODY
i += 1

The for loop is more concise because we can eliminate the loop variable, i.
Here is the previous loop written with a for loop.

for horseman in horsemen:


print horseman

It almost reads like English: For (every) horseman in (the list of) horsemen,
print (the name of the) horseman.

Any list expression can be used in a for loop:

for number in range(20):


if number % 3 == 0:
print number

for fruit in ["banana", "apple", "quince"]:


print "I like to eat " + fruit + "s!"

The first example prints all the multiples of 3 between 0 and 19. The
second example expresses enthusiasm for various fruits.

Since lists are mutable, it is often desirable to traverse a list, modifying


each of its elements. The following squares all the numbers from 1 to 5:

numbers = [1, 2, 3, 4, 5]

for index in range(len(numbers)):


numbers[index] = numbers[index]**2

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
176

Take a moment to think about range(len(numbers)) until you understand


how it works. We are interested here in both the value and its index within
the list, so that we can assign a new value to it.

This pattern is common enough that Python provides a nicer way to


impliment it:

numbers = [1, 2, 3, 4, 5]

for index, value in enumerate(numbers):


numbers[index] = value**2

enumerate generates both the index and the value associated with it
during the list traversal. Try this next example to see more clearly how
enumerate works:

>>> for index, value in enumerate(['banana', 'apple', 'pear',


'quince']):
... print index, value
...
0 banana
1 apple
2 pear
3 quince
>>>

9.14. List parameters

Passing a list as an argument actually passes a reference to the list, not a


copy of the list. Since lists are mutable changes made to the parameter
change the argument as well. For example, the function below takes a list
as an argument and multiplies each element in the list by 2:

def double_stuff(a_list):
for index, value in enumerate(a_list):
a_list[index] = 2 * value

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
177

If we put double_stuff in a file named ch09.py, we can test it out like this:

>>> from ch09 import double_stuff


>>> things = [2, 5, 'Spam', 9.5]
>>> double_stuff(things)
>>> things
[4, 10, 'SpamSpam', 19.0]
>>>

The parameter a_list and the variable things are aliases for the same
object. The state diagram looks like this:

Since the list object is shared by two frames, we drew it between them.

If a function modifies a list parameter, the caller sees the change.

9.15. Pure functions and modifiers

Functions which take lists as arguments and change them during execution
are called modifiers and the changes they make are called side effects.

A pure function does not produce side effects. It communicates with the
calling program only through parameters, which it does not modify, and a
return value. Here is double_stuff written as a pure function:

def double_stuff(a_list):
new_list = []
for value in a_list:
new_list += [2 * value]
return new_list

This version of double_stuff does not change its arguments:

>>> from ch09 import double_stuff


>>> things = [2, 5, 'Spam', 9.5]

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
178

>>> double_stuff(things)
[4, 10, 'SpamSpam', 19.0]
>>> things
[2, 5, 'Spam', 9.5]
>>>

To use the pure function version of double_stuff to modify things, you


would assign the return value back to things:

>>> things = double_stuff(things)


>>> things
[4, 10, 'SpamSpam', 19.0]
>>>

9.16. Which is better?

Anything that can be done with modifiers can also be done with pure
functions. In fact, some programming languages only allow pure functions.
There is some evidence that programs that use pure functions are faster to
develop and less error-prone than programs that use modifiers.
Nevertheless, modifiers are convenient at times, and in some cases,
functional programs are less efficient.

In general, we recommend that you write pure functions whenever it is


reasonable to do so and resort to modifiers only if there is a compelling
advantage. This approach might be called a functional programming style.

9.17. Nested lists

A nested list is a list that appears as an element in another list. In this list,
the element with index 3 is a nested list:

>>> nested = ["hello", 2.0, 5, [10, 20]]

If we print nested[3], we get [10, 20]. To extract an element from the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
179

nested list, we can proceed in two steps:

>>> elem = nested[3]


>>> elem[0]
10

Or we can combine them:

>>> nested[3][1]
20

Bracket operators evaluate from left to right, so this expression gets the
three-eth element of nested and extracts the one-eth element from it.

9.18. Matrices

Nested lists are often used to represent matrices. For example, the matrix:

might be represented as:

>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

matrix is a list with three elements, where each element is a row of the
matrix. We can select an entire row from the matrix in the usual way:

>>> matrix[1]
[4, 5, 6]

Or we can extract a single element from the matrix using the double-index
form:

>>> matrix[1][1]
5

The first index selects the row, and the second index selects the column.
Although this way of representing matrices is common, it is not the only
possibility. A small variation is to use a list of columns instead of a list of

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
180

rows. Later we will see a more radical alternative using a dictionary.

9.19. Test-driven development (TDD)

Test-driven development (TDD) is a software development practice


which arrives at a desired feature through a series of small, iterative steps
motivated by automated tests which are written first that express
increasing refinements of the desired feature.

Doctest enables us to easily demonstrate TDD. Let’s say we want a


function which creates a rows by columns matrix given arguments for rows
and columns.

We first setup a test for this function in a file named matrices.py:

def make_matrix(rows, columns):


"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
"""

if __name__ == '__main__':
import doctest
doctest.testmod()

Running this returns in a failing test:

******************************************************************
****
File "matrices.py", line 3, in __main__.make_matrix
Failed example:
make_matrix(3, 5)
Expected:
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Got nothing
******************************************************************
****
1 items had failures:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
181

1 of 1 in __main__.make_matrix
***Test Failed*** 1 failures.

The test fails because the body of the function contains only a single triple
quoted string and no return statement, so it returns None. Our test
indicates that we wanted it to return a matrix with 3 rows of 5 columns of
zeros.

The rule in using TDD is to use the simplest thing that works in writing a
solution to pass the test, so in this case we can simply return the desired
result:

def make_matrix(rows, columns):


"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
"""
return [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Running this now the test passes, but our current implimentation of
make_matrix always returns the same result, which is clearly not what we
intended. To fix this, we first motivate our improvement by adding a test:

def make_matrix(rows, columns):


"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
"""
return [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

which as we expect fails:

******************************************************************
****
File "matrices.py", line 5, in __main__.make_matrix
Failed example:
make_matrix(4, 2)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
182

Expected:
[[0, 0], [0, 0], [0, 0], [0, 0]]
Got:
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
******************************************************************
****
1 items had failures:
1 of 2 in __main__.make_matrix
***Test Failed*** 1 failures.

This technique is called test-driven because code should only be written


when there is a failing test to make pass. Motivated by the failing test, we
can now produce a more general solution:

def make_matrix(rows, columns):


"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
"""
return [[0] * columns] * rows

This solution appears to work, and we may think we are finished, but when
we use the new function later we discover a bug:

>>> from matrices import *


>>> m = make_matrix(4, 3)
>>> m
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> m[1][2] = 7
>>> m
[[0, 0, 7], [0, 0, 7], [0, 0, 7], [0, 0, 7]]
>>>

We wanted to assign the element in the second row and the third column
the value 7, instead, all elements in the third column are 7!

Upon reflection, we realize that in our current solution, each row is an alias
of the other rows. This is definitely not what we intended, so we set about
fixing the problem, first by writing a failing test:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
183

def make_matrix(rows, columns):


"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
>>> m = make_matrix(4, 2)
>>> m[1][1] = 7
>>> m
[[0, 0], [0, 7], [0, 0], [0, 0]]
"""
return [[0] * columns] * rows

With a failing test to fix, we are now driven to a better solution:

def make_matrix(rows, columns):


"""
>>> make_matrix(3, 5)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> make_matrix(4, 2)
[[0, 0], [0, 0], [0, 0], [0, 0]]
>>> m = make_matrix(4, 2)
>>> m[1][1] = 7
>>> m
[[0, 0], [0, 7], [0, 0], [0, 0]]
"""
matrix = []
for row in range(rows):
matrix += [[0] * columns]
return matrix

Using TDD has several benefits to our software development process. It:

• helps us think concretely about the problem we are trying solve


before we attempt to solve it.
• encourages breaking down complex problems into smaller, simpler
problems and working our way toward a solution of the larger
problem step-by-step.
• assures that we have a well developed automated test suite for our
software, facilitating later additions and improvements.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
184

9.20. Strings and lists

Python has a command called list that takes a sequence type as an


argument and creates a list out of its elements.

>>> list("Crunchy Frog")


['C', 'r', 'u', 'n', 'c', 'h', 'y', ' ', 'F', 'r', 'o', 'g']

There is also a str command that takes any Python value as an argument
and returns a string representation of it.

>>> str(5)
'5'
>>> str(None)
'None'
>>> str(list("nope"))
"['n', 'o', 'p', 'e']"

As we can see from the last example, str can’t be used to join a list of
characters together. To do this we could use the join function in the string
module:

>>> import string


>>> char_list = list("Frog")
>>> char_list
['F', 'r', 'o', 'g']
>>> string.join(char_list, '')
'Frog'

Two of the most useful functions in the string module involve lists of
strings. The split function breaks a string into a list of words. By default,
any number of whitespace characters is considered a word boundary:

>>> import string


>>> song = "The rain in Spain..."
>>> string.split(song)
['The', 'rain', 'in', 'Spain...']

An optional argument called a delimiter can be used to specify which

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
185

characters to use as word boundaries. The following example uses the


string ai as the delimiter:

>>> string.split(song, 'ai')


['The r', 'n in Sp', 'n...']

Notice that the delimiter doesn’t appear in the list.

string.join is the inverse of string.split. It takes two arguments: a list of


strings and a separator which will be placed between each element in the
list in the resultant string.

>>> import string


>>> words = ['crunchy', 'raw', 'unboned', 'real', 'dead', 'frog']
>>> string.join(words, ' ')
'crunchy raw unboned real dead frog'
>>> string.join(words, '**')
'crunchy**raw**unboned**real**dead**frog'

9.21. Glossary

list
A named collection of objects, where each object is identified by an
index.
index
An integer variable or value that indicates an element of a list.
element
One of the values in a list (or other sequence). The bracket operator
selects elements of a list.
sequence
Any of the data types that consist of an ordered set of elements, with
each element identified by an index.
nested list
A list that is an element of another list.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
186

step size
The interval between successive elements of a linear sequence. The
third (and optional argument) to the range function is called the step
size. If not specified, it defaults to 1.
list traversal
The sequential accessing of each element in a list.
mutable type
A data type in which the elements can be modified. All mutable types
are compound types. Lists are mutable data types; strings are not.
object
A thing to which a variable can refer.
aliases
Multiple variables that contain references to the same object.
clone
To create a new object that has the same value as an existing object.
Copying a reference to an object creates an alias but doesn’t clone the
object.
modifier
A function which changes its arguments inside the function body. Only
mutable types can be changed by modifiers.
side effect
A change in the state of a program made by calling a function that is
not a result of reading the return value from the function. Side effects
can only be produced by modifiers.
pure function
A function which has no side effects. Pure functions only make changes
to the calling program through their return values.
test-driven development (TDD)
A software development practice which arrives at a desired feature
through a series of small, iterative steps motivated by automated tests

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
187

which are written first that express increasing refinements of the


desired feature. (see the Wikipedia article on Test-driven development
for more information.)
delimiter
A character or string used to indicate where a string should be split.

9.22. Exercises

1. Write a loop that traverses:

['spam!', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]

and prints the length of each element. What happens if you send an
integer to len? Change 1 to 'one' and run your solution again.

2. Open a file named ch09e02.py and with the following content:

# Add your doctests here:


"""
"""

# Write your Python code here:

if __name__ == '__main__':
import doctest
doctest.testmod()

Add each of the following sets of doctests to the docstring at the top
of the file and write Python code to make the doctests pass.

1. """
>>> a_list[3]
42
>>> a_list[6]
'Ni!'
>>> len(a_list)
8

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
188

"""

2. """
>>> b_list[1:]
['Stills', 'Nash']
>>> group = b_list + c_list
>>> group[-1]
'Young'
"""

3. """
>>> 'war' in mystery_list
False
>>> 'peace' in mystery_list
True
>>> 'justice' in mystery_list
True
>>> 'oppression' in mystery_list
False
>>> 'equality' in mystery_list
True
"""

4. """
>>> range(a, b, c)
[5, 9, 13, 17]
"""

Only add one set of doctests at a time. The next set of doctests
should not be added until the previous set pass.

3. What is the Python interpreter’s response to the following?

>>> range(10, 0, -2)

The three arguments to the range function are start, stop, and step,
respectively. In this example, start is greater than stop. What
happens if start < stop and step < 0? Write a rule for the
relationships among start, stop, and step.

4. a = [1, 2, 3]
b = a[:]

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
189

b[0] = 5

Draw a state diagram for a and b before and after the third line is
executed.

5. What will be the output of the following program?

this = ['I', 'am', 'not', 'a', 'crook']


that = ['I', 'am', 'not', 'a', 'crook']
print "Test 1: %s" % (id(this) == id(that))
that = this
print "Test 2: %s" % (id(this) == id(that))

Provide a detailed explaination of the results.

6. Open a file named ch09e06.py and use the same procedure as in


exercise 2 to make the following doctests pass:

1. """
>>> 13 in junk
True
>>> del junk[4]
>>> junk
[3, 7, 9, 10, 17, 21, 24, 27]
>>> del junk[a:b]
>>> junk
[3, 7, 27]
"""

2. """
>>> nlist[2][1]
0
>>> nlist[0][2]
17
>>> nlist[1][1]
5
"""

3. """
>>> import string
>>> string.split(message, '??')
['this', 'and', 'that']
"""

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
190

7. Write a function add_lists(a, b) that takes two lists of numbers of the


same length, and returns a new list containing the sums of the
corresponding elements of each.

def add_lists(a, b):


"""
>>> add_lists([1, 1], [1, 1])
[2, 2]
>>> add_lists([1, 2], [1, 4])
[2, 6]
>>> add_lists([1, 2, 1], [1, 4, 3])
[2, 6, 4]
"""

add_lists should pass the doctests above.

8. Write a function mult_lists(a, b) that takes two lists of numbers of the


same length, and returns the sum of the products of the
corresponding elements of each.

def mult_lists(a, b):


"""
>>> mult_lists([1, 1], [1, 1])
2
>>> mult_lists([1, 2], [1, 4])
9
>>> mult_lists([1, 2, 1], [1, 4, 3])
12
"""

Verify that mult_lists passes the doctests above.

9. Add the following two functions to the matrices.py module introduced


in the section on test-driven development:

def add_row(matrix):
"""
>>> m = [[0, 0], [0, 0]]
>>> add_row(m)
[[0, 0], [0, 0], [0, 0]]

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
191

>>> n = [[3, 2, 5], [1, 4, 7]]


>>> add_row(n)
[[3, 2, 5], [1, 4, 7], [0, 0, 0]]
>>> n
[[3, 2, 5], [1, 4, 7]]
"""

def add_column(matrix):
"""
>>> m = [[0, 0], [0, 0]]
>>> add_column(m)
[[0, 0, 0], [0, 0, 0]]
>>> n = [[3, 2], [5, 1], [4, 7]]
>>> add_column(n)
[[3, 2, 0], [5, 1, 0], [4, 7, 0]]
>>> n
[[3, 2], [5, 1], [4, 7]]
"""

Your new functions should pass the doctests. Note that the last
doctest in each function assures that add_row and add_column are
pure functions. ( hint: Python has a copy module with a function
named deepcopy that could make your task easier here. We will talk
more about deepcopy in chapter 13, but google python copy module
if you would like to try it now.)

10.Write a function add_matrices(m1, m2) that adds m1 and m2 and


returns a new matrix containing their sum. You can assume that m1
and m2 are the same size. You add two matrices by adding their
corresponding values.

def add_matrices(m1, m2):


"""
>>> a = [[1, 2], [3, 4]]
>>> b = [[2, 2], [2, 2]]
>>> add_matrices(a, b)
[[3, 4], [5, 6]]
>>> c = [[8, 2], [3, 4], [5, 7]]
>>> d = [[3, 2], [9, 2], [10, 12]]
>>> add_matrices(c, d)
[[11, 4], [12, 6], [15, 19]]

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
192

>>> c
[[8, 2], [3, 4], [5, 7]]
>>> d
[[3, 2], [9, 2], [10, 12]]
"""

Add your new function to matrices.py and be sure it passes the


doctests above. The last two doctests confirm that add_matrices is a
pure function.

11.Write a function scalar_mult(n, m) that multiplies a matrix, m, by a


scalar, n.

def scalar_mult(n, m):


"""
>>> a = [[1, 2], [3, 4]]
>>> scalar_mult(3, a)
[[3, 6], [9, 12]]
>>> b = [[3, 5, 7], [1, 1, 1], [0, 2, 0], [2, 2, 3]]
>>> scalar_mult(10, b)
[[30, 50, 70], [10, 10, 10], [0, 20, 0], [20, 20, 30]]
>>> b
[[3, 5, 7], [1, 1, 1], [0, 2, 0], [2, 2, 3]]
"""

Add your new function to matrices.py and be sure it passes the


doctests above.

12.def row_times_column(m1, row, m2, column):


"""
>>> row_times_column([[1, 2], [3, 4]], 0, [[5, 6], [7,
8]], 0)
19
>>> row_times_column([[1, 2], [3, 4]], 0, [[5, 6], [7,
8]], 1)
22
>>> row_times_column([[1, 2], [3, 4]], 1, [[5, 6], [7,
8]], 0)
43
>>> row_times_column([[1, 2], [3, 4]], 1, [[5, 6], [7,
8]], 1)
50

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
193

"""

def matrix_mult(m1, m2):


"""
>>> matrix_mult([[1, 2], [3, 4]], [[5, 6], [7, 8]])
[[19, 22], [43, 50]]
>>> matrix_mult([[1, 2, 3], [4, 5, 6]], [[7, 8], [9, 1], [2,
3]])
[[31, 19], [85, 55]]
>>> matrix_mult([[7, 8], [9, 1], [2, 3]], [[1, 2, 3], [4, 5,
6]])
[[39, 54, 69], [13, 23, 33], [14, 19, 24]]
"""

Add your new functions to matrices.py and be sure it passes the


doctests above.

13.import string

song = "The rain in Spain..."

Describe the relationship between string.join(string.split(song)) and


song. Are they the same for all strings? When would they be
different?

14.Write a function replace(s, old, new) that replaces all occurences of


old with new in a string s.

def replace(s, old, new):


"""
>>> replace('Mississippi', 'i', 'I')
'MIssIssIppI'
>>> s = 'I love spom! Spom is my favorite food.
Spom, spom, spom, yum!'
>>> replace(s, 'om', 'am')
'I love spam! Spam is my favorite food. Spam, spam,
spam, yum!'
>>> replace(s, 'o', 'a')
'I lave spam! Spam is my favarite faad. Spam, spam,
spam, yum!'
"""

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
194

Your solution should pass the doctests above. Hint: use string.split
and string.join.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
195

10. Modules and files

10.1. Modules

A module is a file containing Python definitions and statements intended


for use in other Python programs. There are many Python modules that
come with Python as part of the standard library. We have seen two of
these already, the doctest module and the string module.

10.2. pydoc

You can use pydoc to search through the Python libraries installed on your
system. At the command prompt type the following:

$ pydoc -g

and the following will appear:

(note: see exercise 2 if you get an error)

Click on the open browser button to launch a web browser window


containing the documentation generated by pydoc:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
196

This is a listing of all the python libraries found by Python on your system.
Clicking on a module name opens a new page with documentation for that
module. Clicking keyword, for example, opens the following page:

Documentation for most modules contains three color coded sections:

• Classes in pink

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
197

• Functions in orange
• Data in green

Classes will be discussed in later chapters, but for now we can use pydoc
tosee the functions and data contained within modules.

The keyword module contains a single function, iskeyword, which as its


name suggests is a boolean function that returns True if a string passed to
it is a keyword:

>>> from keyword import *


>>> iskeyword('for')
True
>>> iskeyword('all')
False
>>>

The data item, kwlist contains a list of all the current keywords in Python:

>>> from keyword import *


>>> print kwlist
['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif',
'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import',
'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try',
'while', 'with', 'yield']
>>>

We encourage you to use pydoc to explore the extensive libraries that


come with Python. There are so many treasures to discover!

10.3. Creating modules

All we need to create a module is a text file with a .py extension on the
filename:

# seqtools.py
#

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
198

def remove_at(pos, seq):


return seq[:pos] + seq[pos+1:]

We can now use our module in both scripts and the Python shell. To do so,
we must first import the module. There are two ways to do this:

>>> from seqtools import remove_at


>>> s = "A string!"
>>> remove_at(4, s)
'A sting!'

and:

>>> import seqtools


>>> s = "A string!"
>>> seqtools.remove_at(4, s)
'A sting!'

In the first example, remove_at is called just like the functions we have
seen previously. In the second example the name of the module and a dot
(.) are written before the function name.

Notice that in either case we do not include the .py file extension when
importing. Python expects the file names of Python modules to end in .py,
so the file extention is not included in the import statement.

The use of modules makes it possible to break up very large programs into
managable sized parts, and to keep related parts together.

10.4. Namespaces

A namespace is a syntactic container which permits the same name to be


used in different modules or functions (and as we will see soon, in classes
and methods).

Each module determines its own namespace, so we can use the same

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
199

name in multiple modules without causing an identification problem.

# module1.py

question = "What is the meaning of life, the Universe, and


everything?"
answer = 42

# module2.py

question = "What is your quest?"


answer = "To seek the holy grail."

We can now import both modules and access question and answer in each:

>>> import module1


>>> import module2
>>> print module1.question
What is the meaning of life, the Universe, and everything?
>>> print module2.question
What is your quest?
>>> print module1.answer
42
>>> print module2.answer
To seek the holy grail.
>>>

If we had used from module1 import * and from module2 import * instead,
we would have a naming collision and would not be able to access
question and answer from module1.

Functions also have their own namespace:

def f():
n=7
print "printing n inside of f: %d" % n

def g():
n = 42
print "printing n inside of g: %d" % n

n = 11
print "printing n before calling f: %d" % n

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
200

f()
print "printing n after calling f: %d" % n
g()
print "printing n after calling g: %d" % n

Running this program produces the following output:

printing n before calling f: 11


printing n inside of f: 7
printing n after calling f: 11
printing n inside of g: 42
printing n after calling g: 11

The three n‘s here do not collide since they are each in a different
namespace.

Namespaces permit several programmers to work on the same project


without having naming collisions.

10.5. Attributes and the dot operator

Variables defined inside a module are called attributes of the module.


They are accessed by using the dot operator ( .). The question attribute
of module1 and module2 are accessed using module1.question and
module2.question.

Modules contain functions as well as attributes, and the dot operator is


used to access them in the same way. seqtools.remove_at refers to the
remove_at function in the seqtools module.

In Chapter 7 we introduced the find function from the string module. The
string module contains many other useful functions:

>>> import string


>>> string.capitalize('maryland')
'Maryland'

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
201

>>> string.capwords("what's all this, then, amen?")


"What's All This, Then, Amen?"
>>> string.center('How to Center Text Using Python', 70)
' How to Center Text Using Python '
>>> string.upper('angola')
'ANGOLA'
>>>

You should use pydoc to browse the other functions and attributes in the
string module.

10.6. String and list methods

As the Python language developed, most of functions from the string


module have also been added as methods of string objects. A method
acts much like a function, but the syntax for calling it is a bit different:

>>> 'maryland'.capitalize()
'Maryland'
>>> "what's all this, then, amen?".title()
"What'S All This, Then, Amen?"
>>> 'How to Center Text Using Python'.center(70)
' How to Center Text Using Python '
>>> 'angola'.upper()
'ANGOLA'
>>>

String methods are built into string objects, and they are invoked (called)
by following the object with the dot operator and the method name.

We will be learning how to create our own objects with their own methods
in later chapters. For now we will only be using methods that come with
Python’s built-in objects.

The dot operator can also be used to access built-in methods of list objects:

>>> mylist = []
>>> mylist.append(5)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
202

>>> mylist.append(27)
>>> mylist.append(3)
>>> mylist.append(12)
>>> mylist
[5, 27, 3, 12]
>>>

append is a list method which adds the argument passed to it to the end of
the list. Continuing with this example, we show several other list methods:

>>> mylist.insert(1, 12)


>>> mylist
[5, 12, 27, 3, 12]
>>> mylist.count(12)
2
>>> mylist.extend([5, 9, 5, 11])
>>> mylist
[5, 12, 27, 3, 12, 5, 9, 5, 11])
>>> mylist.index(9)
6
>>> mylist.count(5)
3
>>> mylist.reverse()
>>> mylist
[11, 5, 9, 5, 12, 3, 27, 12, 5]
>>> mylist.sort()
>>> mylist
[3, 5, 5, 5, 9, 11, 12, 12, 27]
>>> mylist.remove(12)
>>> mylist
[3, 5, 5, 5, 9, 11, 12, 27]
>>>

Experiment with the list methods in this example until you feel confident
that you understand how they work.

10.7. Reading and writing text files

While a program is running, its data is stored in random access memory


(RAM). RAM is fast and inexpensive, but it is also volatile, which means
that when the program ends, or the computer shuts down, data in RAM

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
203

disappears. To make data available the next time you turn on your
computer and start your program, you have to write it to a non-volatile
storage medium, such a hard drive, usb drive, or CD-RW.

Data on non-volatile storage media is stored in named locations on the


media called files. By reading and writing files, programs can save
information between program runs.

Working with files is a lot like working with a notebook. To use a notebook,
you have to open it. When you’re done, you have to close it. While the
notebook is open, you can either write in it or read from it. In either case,
you know where you are in the notebook. You can read the whole notebook
in its natural order or you can skip around.

All of this applies to files as well. To open a file, you specify its name and
indicate whether you want to read or write.

Opening a file creates a file object. In this example, the variable myfile
refers to the new file object.

>>> myfile = open('test.dat', 'w')


>>> print myfile
<open file 'test.dat', mode 'w' at 0x2aaaaab80cd8>

The open function takes two arguments. The first is the name of the file,
and the second is the mode. Mode 'w' means that we are opening the file
for writing.

If there is no file named test.dat, it will be created. If there already is one,


it will be replaced by the file we are writing.

When we print the file object, we see the name of the file, the mode, and
the location of the object.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
204

To put data in the file we invoke the write method on the file object:

>>> myfile.write("Now is the time")


>>> myfile.write("to close the file")

Closing the file tells the system that we are done writing and makes the file
available for reading:

>>> myfile.close()

Now we can open the file again, this time for reading, and read the
contents into a string. This time, the mode argument is 'r' for reading:

>>> myfile = open('test.dat', 'r')

If we try to open a file that doesn’t exist, we get an error:

>>> myfile = open('test.cat', 'r')


IOError: [Errno 2] No such file or directory: 'test.cat'

Not surprisingly, the read method reads data from the file. With no
arguments, it reads the entire contents of the file into a single string:

>>> text = myfile.read()


>>> print text
Now is the timeto close the file

There is no space between time and to because we did not write a space
between the strings.

read can also take an argument that indicates how many characters to
read:

>>> myfile = open('test.dat', 'r')


>>> print myfile.read(5)
Now i

If not enough characters are left in the file, read returns the remaining

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
205

characters. When we get to the end of the file, read returns the empty
string:

>>> print myfile.read(1000006)


s the timeto close the file
>>> print myfile.read()

>>>

The following function copies a file, reading and writing up to fifty


characters at a time. The first argument is the name of the original file; the
second is the name of the new file:

def copy_file(oldfile, newfile):


infile = open(oldfile, 'r')
outfile = open(newfile, 'w')
while True:
text = infile.read(50)
if text == "":
break
outfile.write(text)
infile.close()
outfile.close()
return

This functions continues looping, reading 50 characters from infile and


writing the same 50 charaters to outfile until the end of infile is reached, at
which point text is empty and the break statement is executed.

10.8. Text files

A text file is a file that contains printable characters and whitespace,


organized into lines separated by newline characters. Since Python is
specifically designed to process text files, it provides methods that make
the job easy.

To demonstrate, we’ll create a text file with three lines of text separated by

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
206

newlines:

>>> outfile = open("test.dat","w")


>>> outfile.write("line one\nline two\nline three\n")
>>> outfile.close()

The readline method reads all the characters up to and including the next
newline character:

>>> infile = open("test.dat","r")


>>> print infile.readline()
line one

>>>

readlines returns all of the remaining lines as a list of strings:

>>> print infile.readlines()


['line two\012', 'line three\012']

In this case, the output is in list format, which means that the strings
appear with quotation marks and the newline character appears as the
escape sequence \\012.

At the end of the file, readline returns the empty string and readlines
returns the empty list:

>>> print infile.readline()

>>> print infile.readlines()


[]

The following is an example of a line-processing program. filter makes a


copy of oldfile, omitting any lines that begin with #:

def filter(oldfile, newfile):


infile = open(oldfile, 'r')
outfile = open(newfile, 'w')
while True:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
207

text = infile.readline()
if text == "":
break
if text[0] == '#':
continue
outfile.write(text)
infile.close()
outfile.close()
return

The continue statement ends the current iteration of the loop, but
continues looping. The flow of execution moves to the top of the loop,
checks the condition, and proceeds accordingly.

Thus, if text is the empty string, the loop exits. If the first character of text
is a hash mark, the flow of execution goes to the top of the loop. Only if
both conditions fail do we copy text into the new file.

10.9. Directories

Files on non-volatile storage media are organized by a set of rules known


as a file system. File systems are made up of files and directories, which
are containers for both files and other directories.

When you create a new file by opening it and writing, the new file goes in
the current directory (wherever you were when you ran the program).
Similarly, when you open a file for reading, Python looks for it in the
current directory.

If you want to open a file somewhere else, you have to specify the path to
the file, which is the name of the directory (or folder) where the file is
located:

>>> wordsfile = open('/usr/share/dict/words', 'r')


>>> wordlist = wordsfile.readlines()

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
208

>>> print wordlist[:6]


['\n', 'A\n', "A's\n", 'AOL\n', "AOL's\n", 'Aachen\n']

This example opens a file named words that resides in a directory named
dict, which resides in share, which resides in usr, which resides in the top-
level directory of the system, called /. It then reads in each line into a list
using readlines, and prints out the first 5 elements from that list.

You cannot use / as part of a filename; it is reserved as a delimiter


between directory and filenames.

The file /usr/share/dict/words should exist on unix based systems, and


contains a list of words in alphabetical order.

10.10. Counting Letters

The ord function returns the integer representation of a character:

>>> ord('a')
97
>>> ord('A')
65
>>>

This example explains why 'Apple' < 'apple' evaluates to True.

The chr function is the inverse of ord. It takes an integer as an argument


and returns its character representation:

>>> for i in range(65, 71):


... print chr(i)
...
A
B
C
D
E

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
209

F
>>>

The following program, countletters.py counts the number of times each


character occurs in the book Alice in Wonderland:

#
# countletters.py
#

def display(i):
if i == 10: return 'LF'
if i == 13: return 'CR'
if i == 32: return 'SPACE'
return chr(i)

infile = open('alice_in_wonderland.txt', 'r')


text = infile.read()
infile.close()

counts = 128 * [0]

for letter in text:


counts[ord(letter)] += 1

outfile = open('alice_counts.dat', 'w')


outfile.write("%-12s%s\n" % ("Character", "Count"))
outfile.write("=================\n")

for i in range(len(counts)):
if counts[i]:
outfile.write("%-12s%d\n" % (display(i), counts[i]))

outfile.close()

Run this program and look at the output file it generates using a text
editor. You will be asked to analyze the program in the exercies below.

10.11. The sys module and argv

The sys module contains functions and variables which provide access to
the environment in which the python interpreter runs.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
210

The following example shows the values of a few of these variables on one
of our systems:

>>> import sys


>>> sys.platform
'linux2'
>>> sys.path
['', '/home/jelkner/lib/python', '/usr/lib/python25.zip',
'/usr/lib/python2.5',
'/usr/lib/python2.5/plat-linux2', '/usr/lib/python2.5/lib-tk',
'/usr/lib/python2.5/lib-dynload', '/usr/local/lib/python2.5/site-
packages',
'/usr/lib/python2.5/site-packages', '/usr/lib/python2.5/site-
packages/Numeric',
'/usr/lib/python2.5/site-packages/gst-0.10',
'/var/lib/python-support/python2.5', '/usr/lib/python2.5/site-
packages/gtk-2.0',
'/var/lib/python-support/python2.5/gtk-2.0']
>>> sys.version
'2.5.1 (r251:54863, Mar 7 2008, 04:10:12) \n[GCC 4.1.3
20070929 (prerelease)
(Ubuntu 4.1.2-16ubuntu2)]'
>>>

Starting Jython on the same machine produces different values for the
same variables:

>>> import sys


>>> sys.platform
'java1.6.0_03'
>>> sys.path
['', '/home/jelkner/.', '/usr/share/jython/Lib', '/usr/share/jython/Lib-
cpython']
>>> sys.version
'2.1'
>>>

The results will be different on your machine of course.

The argv variable holds a list of strings read in from the command line
when a Python script is run. These command line arguments can be
used to pass information into a program at the same time it is invoked.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
211

#
# demo_argv.py
#
import sys

print sys.argv

Running this program from the unix command prompt demonstrates how
sys.argv works:

$ python demo_argv.py this and that 1 2 3


['demo_argv.py', 'this', 'and', 'that', '1', '2', '3']
$

argv is a list of strings. Notice that the first element is the name of the
program. Arguments are separated by white space, and separated into a
list in the same way that string.split operates. If you want an argument
with white space in it, use quotes:

$ python demo_argv.py "this and" that "1 2" 3


['demo_argv.py', 'this and', 'that', '1 2', '3']
$

With argv we can write useful programs that take their input directly from
the command line. For example, here is a program that finds the sum of a
series of numbers:

#
# sum.py
#
from sys import argv

nums = argv[1:]

for index, value in enumerate(nums):


nums[index] = float(value)

print sum(nums)

In this program we use the from <module> import <attribute> style of

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
212

importing, so argv is brought into the module’s main namespace.

We can now run the program from the command prompt like this:

$ python sum.py 3 4 5 11
23
$ python sum.py 3.5 5 11 100
119.5

You are asked to write similar programs as exercises.

10.12. Glossary

module
A file containing Python definitions and statements intended for use in
other Python programs. The contents of a module are made available
to the other program by using the import statement.
standard library
A library is a collection of software used as tools in the development of
other software. The standard library of a programming language is the
set of such tools that are distributed with the core programming
language. Python comes with an extensive standard library.
pydoc
A documentation generator that comes with the Python standard
library.
command prompt
A string displayed by a command line interface indicating that
commands can be entered.
import statement

A statement which makes the objects contained in a module available


for use. There are two forms for the import statement. Using a
hypothetical module named mymod containing functions f1 and f2,

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
213

and variables v1 and v2, examples of these two forms include:

import mymod

The first form.

from mymod import f1, f2, v1, v2

Import all objects

namespace
A syntactic container providing a context for names so that the same
name can reside in different namespaces without ambiguity. In Python,
modules, classes, functions and methods all form namespaces.
naming collision

A situation in which two or more names in a given namespace cannot


be unambiguously resolved. Using

import string

instead of

from string import *

prevents naming collisions.

attribute
A variable defined inside a module (or class or instance – as we will see
later). Module attributes are accessed by using the dot operator ( .).
dot operator
The dot operator ( .) permits access to attributes and functions of a
module (or attributes and methods of a class or instance – as we will
see later).
method

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
214

Function-like attribute of an object. Methods are invoked (called) on an


object using the dot operator. For example:

>>> s = "this is a string."


>>> s.upper()
'THIS IS A STRING.'
>>>

We say that the method, upper is invoked on the string, s. s is


implicitely the first argument to upper.

volatile memory
Memory which requires an electrical current to maintain state. The
main memory or RAM of a computer is volatile. Information stored in
RAM is lost when the computer is turned off.
non-volatile memory
Memory that can maintain its state without power. Hard drives, flash
drives, and rewritable compact disks (CD-RW) are each examples of
non-volatile memory.
file
A named entity, usually stored on a hard drive, floppy disk, or CD-ROM,
that contains a stream of characters.
mode
A distinct method of operation within a computer program. Files in
Python can be openned in one of three modes: read ('r'), write ('w'),
and append ('a').
path
A sequence of directory names that specifies the exact location of a
file.
text file
A file that contains printable characters organized into lines separated
by newline characters.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
215

continue statement
A statement that causes the current iteration of a loop to end. The flow
of execution goes to the top of the loop, evaluates the condition, and
proceeds accordingly.
file system
A method for naming, accessing, and organizing files and the data they
contain.
directory
A named collection of files, also called a folder. Directories can contain
files and other directories, which are refered to as subdirectories of the
directory that contains them.
path

The name and location of a file within a file system. For example:

/usr/share/dict/words

indicates a file named words found in the dict subdirectory of the


share subdirectory of the usr directory.

delimiter
A sequence of one or more characters used to specify the boundary
between separate parts of text.
command line
The sequence of characters read into the command interpreter in a
command line interface (see the Wikipedia article on command line
interface for more information).
command line argument
A value passed to a program along with the program’s invocation at
the command prompt of a command line interface (CLI).
Jython
An implementation of the Python programming language written in

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
216

Java. (see the Jython home page at http://www.jython.org for more


information.)
argv
argv is short for argument vector and is a variable in the sys module
which stores a list of command line arguments passed to a program at
run time.

10.13. Exercises

1. Complete the following:

• Start the pydoc server with the command pydoc -g at the


command prompt.

• Click on the open browser button in the pydoc tk window.

• Find the calendar module and click on it.

• While looking at the Functions section, try out the following in a


Python shell:

>>> import calendar


>>> year = calendar.calendar(2008)
>>> print year # What happens here?

• Experiment with calendar.isleap. What does it expect as an


argument? What does it return as a result? What kind of a
function is this?

Make detailed notes about what you learned from this exercise.

2. If you don’t have Tkinter installed on your computer, then pydoc -g


will return an error, since the graphics window that it opens requires

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
217

Tkinter. An alternative is to start the web server directly:

$ pydoc -p 7464

This starts the pydoc web server on port 7464. Now point your web
browser at:

http://localhost:7464

and you will be able to browse the Python libraries installed on your
system. Use this approach to start pydoc and take a look at the math
module.

• How many functions are in the math module?


• What does math.ceil do? What about math.floor? ( hint: both
floor and ceil expect floating point arguments.)
• Describe how we have been computing the same value as
math.sqrt without using the math module.
• What are the two data contstants in the math module?

Record detailed notes of your investigation in this exercise.

3. Use pydoc to investigate the copy module. What does deepcopy do?
In which exercises from last chapter would deepcopy have come in
handy?

4. Create a module named mymodule1.py. Add attributes myage set to


your current age, and year set to the current year. Create another
module named mymodule2.py. Add attributes myage set to 0, and
year set to the year you were born. Now create a file named
namespace_test.py. Import both of the modules above and write the
following statement:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
218

print (mymodule2.myage - mymodule1.myage) ==


(mymodule2.year - mymodule1.year)

When you will run namespace_test.py you will see either True or
False as output depending on whether or not you’ve already had your
birthday this year.

5. Add the following statement to mymodule1.py, mymodule2.py, and


namespace_test.py from the previous exercise:

print "My name is %s" % __name__

Run namespace_test.py. What happens? Why? Now add the following


to the bottom of mymodule1.py:

if __name__ == '__main__':
print "This won't run if I'm imported."

Run mymodule1.py and namespace_test.py again. In which case do


you see the new print statement?

6. In a Python shell try the following:

>>> import this

What does Tim Peter’s have to say about namespaces?

7. Use pydoc to find and test three other functions from the string
module. Record your findings.

8. Rewrite matrix_mult from the last chapter using what you have
learned about list methods.

9. The dir function, which we first saw in Chapter 7, prints out a list of
the attributes of an object passed to it as an argument. In other
words, dir returns the contents of the namespace of its argument.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
219

Use dir(str) and dir(list) to find at least three string and list methods
which have not been introduced in the examples in the chapter. You
should ignore anything that begins with double underscore (__) for
the time being. Be sure to make detailed notes of your findings,
including names of the new methods and examples of their use. (
hint: Print the docstring of a function you want to explore. For
example, to find out how str.join works, print str.join.__doc__)

10.Give the Python interpreter’s response to each of the following from


a continuous interpreter session:

• >>> s = "If we took the bones out, it wouldn't be


crunchy, would it?"
>>> s.split()

• >>> type(s.split())

• >>> s.split('o')

• >>> s.split('i')

• >>> '0'.join(s.split('o'))

Be sure you understand why you get each result. Then apply what
you have learned to fill in the body of the function below using the
split and join methods of str objects:

def myreplace(old, new, s):


"""
Replace all occurances of old with new in the string s.

>>> myreplace(',', ';', 'this, that, and, some, other,


thing')
'this; that; and; some; other; thing'
>>> myreplace(' ', '**', 'Words will now be separated
by stars.')
'Words**will**now**be**separated**by**stars.'
"""

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
220

Your solution should pass all doctests.

11.Create a module named wordtools.py with the following at the


bottom:

if __name__ == '__main__':
import doctest
doctest.testmod()

Explain how this statement makes both using and testing this module
convenient. What will be the value of __name__ when wordtools.py is
imported from another module? What will it be when it is run as a
main program? In which case will the doctests run? Now add bodies
to each of the following functions to make the doctests pass:

def cleanword(word):
"""
>>> cleanword('what?')
'what'
>>> cleanword('"now!"')
'now'
>>> cleanword('?+="word!,@$()"')
'word'
"""

def has_dashdash(s):
"""
>>> has_dashdash('distance--but')
True
>>> has_dashdash('several')
False
>>> has_dashdash('critters')
False
>>> has_dashdash('spoke--fancy')
True
>>> has_dashdash('yo-yo')
False
"""

def extract_words(s):
"""
>>> extract_words('Now is the time! "Now", is the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
221

time? Yes, now.')


['now', 'is', 'the', 'time', 'now', 'is', 'the', 'time', 'yes',
'now']
>>> extract_words('she tried to curtsey as she spoke--
fancy')
['she', 'tried', 'to', 'curtsey', 'as', 'she', 'spoke', 'fancy']
"""

def wordcount(word, wordlist):


"""
>>> wordcount('now', ['now', 'is', 'time', 'is', 'now', 'is',
'is'])
['now', 2]
>>> wordcount('is', ['now', 'is', 'time', 'is', 'now', 'is',
'the', 'is'])
['is', 4]
>>> wordcount('time', ['now', 'is', 'time', 'is', 'now', 'is',
'is'])
['time', 1]
>>> wordcount('frog', ['now', 'is', 'time', 'is', 'now', 'is',
'is'])
['frog', 0]
"""

def wordset(wordlist):
"""
>>> wordset(['now', 'is', 'time', 'is', 'now', 'is', 'is'])
['is', 'now', 'time']
>>> wordset(['I', 'a', 'a', 'is', 'a', 'is', 'I', 'am'])
['I', 'a', 'am', 'is']
>>> wordset(['or', 'a', 'am', 'is', 'are', 'be', 'but', 'am'])
['a', 'am', 'are', 'be', 'but', 'is', 'or']
"""

def longestword(wordset):
"""
>>> longestword(['a', 'apple', 'pear', 'grape'])
5
>>> longestword(['a', 'am', 'I', 'be'])
2
>>> longestword(['this', 'that',
'supercalifragilisticexpialidocious'])
34
"""

Save this module so you can use the tools it contains in your

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
222

programs.

12.unsorted_fruits.txt contains a list of 26 fruits, each one with a name


that begins with a different letter of the alphabet. Write a program
named sort_fruits.py that reads in the fruits from unsorted_fruits.txt
and writes them out in alphabetical order to a file named
sorted_fruits.txt.

13.Answer the following questions about countletters.py:

• Explain in detail what the three lines do:

infile = open('alice_in_wonderland.txt', 'r')


text = infile.read()
infile.close()

What is would type(text) return after these lines have been


executed?

• What does the expression 128 * [0] evaluate to? Read about
ASCII in Wikipedia and explain why you think the variable,
counts is assigned to 128 * [0] in light of what you read.

• What does
for letter in text:
counts[ord(letter)] += 1

do to counts?

• Explain the purpose of the display function. Why does it check


for values 10, 13, and 32? What is special about those values?

• Describe in detail what the lines

outfile = open('alice_counts.dat', 'w')


outfile.write("%-12s%s\n" % ("Character", "Count"))
outfile.write("=================\n")

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
223

do. What will be in alice_counts.dat when they finish executing?

• Finally, explain in detail what

for i in range(len(counts)):
if counts[i]:
outfile.write("%-12s%d\n" % (display(i),
counts[i]))

does. What is the purpose of if counts[i]?


14.Write a program named mean.py that takes a sequence of numbers
on the command line and returns the mean of their values.:
$ python mean.py 3 4
3.5
$ python mean.py 3 4 5
4.0
$ python mean.py 11 15 94.5 22
35.625

A session of your program running on the same input should produce


the same output as the sample session above.
15.Write a program named median.py that takes a sequence of
numbers on the command line and returns the median of their
values.:
$ python median.py 3 7 11
7
$ python median.py 19 85 121
85
$ python median.py 11 15 16 22
15.5

A session of your program running on the same input should produce


the same output as the sample session above.
16.Modify the countletters.py program so that it takes the file to open
as a command line argument. How will you handle the naming of the
output file?

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
224

11. Recursion and exceptions

11.1. Tuples and mutability

So far, you have seen two compound types: strings, which are made up of
characters; and lists, which are made up of elements of any type. One of
the differences we noted is that the elements of a list can be modified, but
the characters in a string cannot. In other words, strings are immutable
and lists are mutable.

A tuple, like a list, is a sequence of items of any type. Unlike lists,


however, tuples are immutable. Syntactically, a tuple is a comma-
separated sequence of values:

>>> tup = 2, 4, 6, 8, 10

Although it is not necessary, it is conventional to enclose tuples in


parentheses:

>>> tup = (2, 4, 6, 8, 10)

To create a tuple with a single element, we have to include the final


comma:

>>> tup = (5,)


>>> type(tup)
<type 'tuple'>

Without the comma, Python treats (5) as an integer in parentheses:

>>> tup = (5)


>>> type(tup)
<type 'int'>

Syntax issues aside, tuples support the same sequence operations as

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
225

strings and lists. The index operator selects an element from a tuple.

>>> tup = ('a', 'b', 'c', 'd', 'e')


>>> tup[0]
'a'

And the slice operator selects a range of elements.

>>> tup[1:3]
('b', 'c')

But if we try to use item assignment to modify one of the elements of the
tuple, we get an error:

>>> tup[0] = 'X'


TypeError: 'tuple' object does not support item assignment

Of course, even if we can’t modify the elements of a tuple, we can replace


it with a different tuple:

>>> tup = ('X',) + tup[1:]


>>> tup
('X', 'b', 'c', 'd', 'e')

Alternatively, we could first convert it to a list, modify it, and convert it


back into a tuple:

>>> tup = ('X', 'b', 'c', 'd', 'e')


>>> tup = list(tup)
>>> tup
['X', 'b', 'c', 'd', 'e']
>>> tup[0] = 'a'
>>> tup = tuple(tup)
>>> tup
('a', 'b', 'c', 'd', 'e')

11.2. Tuple assignment

Once in a while, it is useful to swap the values of two variables. With

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
226

conventional assignment statements, we have to use a temporary variable.


For example, to swap a and b:

temp = a
a=b
b = temp

If we have to do this often, this approach becomes cumbersome. Python


provides a form of tuple assignment that solves this problem neatly:

a, b = b, a

The left side is a tuple of variables; the right side is a tuple of values. Each
value is assigned to its respective variable. All the expressions on the right
side are evaluated before any of the assignments. This feature makes tuple
assignment quite versatile.

Naturally, the number of variables on the left and the number of values on
the right have to be the same:

>>> a, b, c, d = 1, 2, 3
ValueError: need more than 3 values to unpack

11.3. Tuples as return values

Functions can return tuples as return values. For example, we could write a
function that swaps two parameters:

def swap(x, y):


return y, x

Then we can assign the return value to a tuple with two variables:

a, b = swap(a, b)

In this case, there is no great advantage in making swap a function. In fact,

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
227

there is a danger in trying to encapsulate swap, which is the following


tempting mistake:

def swap(x, y): # incorrect version


x, y = y, x

If we call this function like this:

swap(a, b)

then a and x are aliases for the same value. Changing x inside swap makes
x refer to a different value, but it has no effect on a in __main__. Similarly,
changing y has no effect on b.

This function runs without producing an error message, but it doesn’t do


what we intended. This is an example of a semantic error.

11.4. Pure functions and modifiers revisited

In Pure functions and modifiers we discussed pure functions and modifiers


as related to lists. Since tuples are immutable we can not write modifiers
on them.

Here is a modifier that inserts a new value into the middle of a list:

#
# seqtools.py
#

def insert_in_middle(val, lst):


middle = len(lst)/2
lst[middle:middle] = [val]

We can run it to see that it works:

>>> from seqtools import *


>>> my_list = ['a', 'b', 'd', 'e']

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
228

>>> insert_in_middle('c', my_list)


>>> my_list
['a', 'b', 'c', 'd', 'e']

If we try to use it with a tuple, however, we get an error:

>>> my_tuple = ('a', 'b', 'd', 'e')


>>> insert_in_middle('c', my_tuple)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "seqtools.py", line 7, in insert_in_middle
lst[middle:middle] = [val]
TypeError: 'tuple' object does not support item assignment
>>>

The problem is that tuples are immutable, and don’t support slice
assignment. A simple solution to this problem is to make insert_in_middle a
pure function:

def insert_in_middle(val, tup):


middle = len(tup)/2
return tup[:middle] + (val,) + tup[middle:]

This version now works for tuples, but not for lists or strings. If we want a
version that works for all sequence types, we need a way to encapsulate
our value into the correct sequence type. A small helper function does the
trick:

def encapsulate(val, seq):


if type(seq) == type(""):
return str(val)
if type(seq) == type([]):
return [val]
return (val,)

Now we can write insert_in_middle to work with each of the built-in


sequence types:

def insert_in_middle(val, seq):


middle = len(seq)/2

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
229

return seq[:middle] + encapsulate(val, seq) + seq[middle:]

The last two versions of insert_in_middle are pure functions. They don’t
have any side effects. Adding encapsulate and the last version of
insert_in_middle to the seqtools.py module, we can test it:

>>> from seqtools import *


>>> my_string = 'abde'
>>> my_list = ['a', 'b', 'd', 'e']
>>> my_tuple = ('a', 'b', 'd', 'e')
>>> insert_in_middle('c', my_string)
'abcde'
>>> insert_in_middle('c', my_list)
['a', 'b', 'c', 'd', 'e']
>>> insert_in_middle('c', my_tuple)
('a', 'b', 'c', 'd', 'e')
>>> my_string
'abde'

The values of my_string, my_list, and my_tuple are not changed. If we want
to use insert_in_middle to change them, we have to assign the value
returned by the function call back to the variable:

>>> my_string = insert_in_middle('c', my_string)


>>> my_string
'abcde'

11.5. Recursive data structures

All of the Python data types we have seen can be grouped inside lists and
tuples in a variety of ways. Lists and tuples can also be nested, providing
myriad possibilities for organizing data. The organization of data for the
purpose of making it easier to use is called a data structure.

It’s election time and we are helping to compute the votes as they come in.
Votes arriving from individual wards, precincts, municipalities, counties,
and states are sometimes reported as a sum total of votes and sometimes

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
230

as a list of subtotals of votes. After considering how best to store the


tallies, we decide to use a nested number list, which we define as follows:

A nested number list is a list whose elements are either:

1. numbers
2. nested number lists

Notice that the term, nested number list is used in its own definition.
Recursive definitions like this are quite common in mathematics and
computer science. They provide a concise and powerful way to describe
recursive data structures that are partially composed of smaller and
simpler instances of themselves. The definition is not circular, since at
some point we will reach a list that does not have any lists as elements.

Now suppose our job is to write a function that will sum all of the values in
a nested number list. Python has a built-in function which finds the sum of
a sequence of numbers:

>>> sum([1, 2, 8])


11
>>> sum((3, 5, 8.5))
16.5
>>>

For our nested number list, however, sum will not work:

>>> sum([1, 2, [11, 13], 8])


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>>

The problem is that the third element of this list, [11, 13], is itself a list,
which can not be added to 1, 2, and 8.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
231

11.6. Recursion

To sum all the numbers in our recursive nested number list we need to
traverse the list, visiting each of the elements within its nested structure,
adding any numeric elements to our sum, and repeating this process with
any elements which are lists.

Modern programming languages generally support recursion, which


means that functions can call themselves within their definitions. Thanks to
recursion, the Python code needed to sum the values of a nested number
list is surprisingly short:

def recursive_sum(nested_num_list):
sum = 0
for element in nested_num_list:
if type(element) == type([]):
sum = sum + recursive_sum(element)
else:
sum = sum + element
return sum

The body of recursive_sum consists mainly of a for loop that traverses


nested_num_list. If element is a numerical value (the else branch), it is
simply added to sum. If element is a list, then recursive_sum is called
again, with the element as an argument. The statement inside the function
definition in which the function calls itself is known as the recursive call.

Recursion is truly one of the most beautiful and elegant tools in computer
science.

A slightly more complicated problem is finding the largest value in our


nested number list:

def recursive_max(nested_num_list):
"""
>>> recursive_max([2, 9, [1, 13], 8, 6])

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
232

13
>>> recursive_max([2, [[100, 7], 90], [1, 13], 8, 6])
100
>>> recursive_max([2, [[13, 7], 90], [1, 100], 8, 6])
100
>>> recursive_max([[[13, 7], 90], 2, [1, 100], 8, 6])
100
"""
largest = nested_num_list[0]
while type(largest) == type([]):
largest = largest[0]

for element in nested_num_list:


if type(element) == type([]):
max_of_elem = recursive_max(element)
if largest < max_of_elem:
largest = max_of_elem
else: # element is not a list
if largest < element:
largest = element

return largest

Doctests are included to provide examples of recursive_max at work.

The added twist to this problem is finding a numerical value for initializing
largest. We can’t just use nested_num_list[0], since that my be either a
number or a list. To solve this problem we use a while loop that assigns
largest to the first numerical value no matter how deeply it is nested.

The two examples above each have a base case which does not lead to a
recursive call: the case where the element is a number and not a list.
Without a base case, you have infinite recursion, and your program will
not work. Python stops after reaching a maximum recursion depth and
returns a runtime error.

Write the following in a file named infinite_recursion.py:

#
# infinite_recursion.py

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
233

#
def recursion_depth(number):
print "Recursion depth number %d." % number
recursion_depth(number + 1)

recursion_depth(0)

At the unix command prompt in the same directory in which you saved
your program, type the following:

python infinite_recursion.py

After watching the messages flash by, you will be presented with the end
of a long traceback that ends in with the following:

...
File "infinite_recursion.py", line 3, in recursion_depth
recursion_depth(number + 1)
RuntimeError: maximum recursion depth exceeded

We would certainly never want something like this to happen to a user of


one of our programs, so before finishing the recursion discussion, let’s see
how errors like this are handled in Python.

11.7. Exceptions

Whenever a runtime error occurs, it creates an exception. The program


stops running at this point and Python prints out the traceback, which ends
with the exception that occured.

For example, dividing by zero creates an exception:

>>> print 55/0


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
234

So does accessing a nonexistent list item:

>>> a = []
>>> print a[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>>

Or trying to make an item assignment on a tuple:

>>> tup = ('a', 'b', 'd', 'd')


>>> tup[2] = 'c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>

In each case, the error message on the last line has two parts: the type of
error before the colon, and specifics about the error after the colon.

Sometimes we want to execute an operation that might cause an


exception, but we don’t want the program to stop. We can handle the
exception using the try and except statements.

For example, we might prompt the user for the name of a file and then try
to open it. If the file doesn’t exist, we don’t want the program to crash; we
want to handle the exception:

filename = raw_input('Enter a file name: ')


try:
f = open (filename, "r")
except:
print 'There is no file named', filename

The try statement executes the statements in the first block. If no


exceptions occur, it ignores the except statement. If any exception occurs,
it executes the statements in the except branch and then continues.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
235

We can encapsulate this capability in a function: exists takes a filename


and returns true if the file exists, false if it doesn’t:

def exists(filename):
try:
f = open(filename)
f.close()
return True
except:
return False

You can use multiple except blocks to handle different kinds of exceptions
(see the Errors and Exceptions lesson from Python creator Guido van
Rossum’s Python Tutorial for a more complete discussion of exceptions).

If your program detects an error condition, you can make it raise an


exception. Here is an example that gets input from the user and checks
that the number is non-negative.

#
# learn_exceptions.py
#
def get_age():
age = input('Please enter your age: ')
if age < 0:
raise ValueError, '%s is not a valid age' % age
return age

The raise statement takes two arguments: the exception type, and specific
information about the error. ValueError is the built-in exception which most
closely matches the kind of error we want to raise. The complete listing of
built-in exceptions is found in section 2.3 of the Python Library Reference,
again by Python’s creator, Guido van Rossum.

If the function that called get_age handles the error, then the program can
continue; otherwise, Python prints the traceback and exits:

>>> get_age()

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
236

Please enter your age: 42


42
>>> get_age()
Please enter your age: -2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "learn_exceptions.py", line 4, in get_age
raise ValueError, '%s is not a valid age' % age
ValueError: -2 is not a valid age
>>>

The error message includes the exception type and the additional
information you provided.

Using exception handling, we can now modify infinite_recursion.py so that


it stops when it reaches the maximum recursion depth allowed:

#
# infinite_recursion.py
#
def recursion_depth(number):
print "Recursion depth number %d." % number
try:
recursion_depth(number + 1)
except:
print "Maximum recursion depth exceeded."

recursion_depth(0)

Run this version and observe the results.

11.8. Tail recursion

When a recursive call occurs as the last line of a function definition, it is


refered to as tail recursion.

Here is a version of the countdown function from chapter 6 written using


tail recursion:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
237

def countdown(n):
if n == 0:
print "Blastoff!"
else:
print n
countdown(n-1)

Any computation that can be made using iteration can also be made using
recursion.

Several well known mathamatical functions are defined recursively.


Factorial, for example, is given the special operator, !, and is defined by:

0! = 1
n! = n(n-1)

We can easily code this into Python:

def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

Another well know recursive relation in mathematics is the fibonacci


sequence, which is defined by:

fibonacci(0) = 1
fibonacci(1) = 1
fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)

This can also be written easily in Python:

def fibonacci (n):


if n == 0 or n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)

Both factorial and fibonacci are examples of tail recursion.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
238

Tail recursion is considered a bad practice in languages like Python,


however, since it uses more system resources than the equivalent iterative
solution.

Calling factorial(1000) will exceed the maximum recursion depth. And try
running fibonacci(35) and see how long it takes to complete (be patient, it
will complete).

You will be asked to write an iterative version of factorial as an exercise,


and we will see a better way to handle fibonacci in the next chapter.

11.9. List comprehensions

A list comprehension is a syntactic construct that enables lists to be


created from other lists using a compact, mathematical syntax:

>>> numbers = [1, 2, 3, 4]


>>> [x**2 for x in numbers]
[1, 4, 9, 16]
>>> [x**2 for x in numbers if x**2 > 8]
[9, 16]
>>> [(x, x**2, x**3) for x in numbers]
[(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]
>>> files = ['bin', 'Data', 'Desktop', '.bashrc', '.ssh', '.vimrc']
>>> [name for name in files if name[0] != '.']
['bin', 'Data', 'Desktop']
>>> letters = ['a', 'b', 'c']
>>> [n*letter for n in numbers for letter in letters]
['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc', 'aaaa', 'bbbb', 'cccc']
>>>

The general syntax for a list comprehension expression is:

[expr for item1 in seq1 for item2 in seq2 ... for itemx in seqx if
condition]

This list expression has the same effect as:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
239

output_sequence = []
for item1 in seq1:
for item2 in seq2:
...
for itemx in seqx:
if condition:
output_sequence.append(expr)

As you can see, the list comprehension is much more compact.

11.10. Mini case study: tree

The following program implements a subset of the behavior of the Unix


tree program.

#!/usr/bin/env python

import os
import sys

def getroot():
if len(sys.argv) == 1:
path = ''
else:
path = sys.argv[1]

if os.path.isabs(path):
tree_root = path
else:
tree_root = os.path.join(os.getcwd(), path)

return tree_root

def getdirlist(path):
dirlist = os.listdir(path)
dirlist = [name for name in dirlist if name[0] != '.']
dirlist.sort()
return dirlist

def traverse(path, prefix='|--', s='.\n', f=0, d=0):

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
240

dirlist = getdirlist(path)

for num, file in enumerate(dirlist):


lastprefix = prefix[:-3] + '``--'
dirsize = len(dirlist)

if num < dirsize - 1:


s += '%s %s\n' % (prefix, file)
else:
s += '%s %s\n' % (lastprefix, file)
path2file = os.path.join(path, file)

if os.path.isdir(path2file):
d += 1
if getdirlist(path2file):
s, f, d = traverse(path2file, '| ' + prefix, s, f, d)
else:
f += 1

return s, f, d

if __name__ == '__main__':
root = getroot()
tree_str, files, dirs = traverse(root)

if dirs == 1:
dirstring = 'directory'
else:
dirstring = 'directories'
if files == 1:
filestring = 'file'
else:
filestring = 'files'

print tree_str
print '%d %s, %d %s' % (dirs, dirstring, files, filestring)

You will be asked to explore this program in several of the exercises below.

11.11. Glossary

immutable data type


A data type which cannot be modified. Assignments to elements or

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
241

slices of immutable types cause a runtime error.


mutable data type
A data type which can be modified. All mutable types are compound
types. Lists and dictionaries (see next chapter) are mutable data types;
strings and tuples are not.
tuple
A data type that contains a sequence of elements of any type, like a
list, but is immutable. Tuples can be used wherever an immutable type
is required, such as a key in a dictionary (see next chapter).
tuple assignment
An assignment to all of the elements in a tuple using a single
assignment statement. Tuple assignment occurs in parallel rather than
in sequence, making it useful for swapping values.
data structure
An organization of data for the purpose of making it easier to use.
recursive definition
A definition which defines something in terms of itself. To be useful it
must include base cases which are not recursive. In this way it differs
from a circular definition. Recursive definitions often provide an
elegant way to express complex data structures.
recursion
The process of calling the function that is currently executing.
recursive call
The statement in a recursive function with is a call to itself.
base case
A branch of the conditional statement in a recursive function that does
not result in a recursive call.
infinite recursion
A function that calls itself recursively without ever reaching the base
case. Eventually, an infinite recursion causes a runtime error.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
242

exception
An error that occurs at runtime.
handle an exception
To prevent an exception from terminating a program using the try and
except statements.
raise
To signal an exception using the raise statement.
tail recursion
A recursive call that occurs as the last statement (at the tail) of a
function definition. Tail recursion is considered bad practice in Python
programs since a logically equivalent function can be written using
iteration which is more efficient (see the Wikipedia article on tail
recursion for more information).
list comprehension
A syntactic construct which enables lists to be generated from other
lists using a syntax analogous to the mathematical set-builder
notation.

11.12. Exercises

1. def swap(x, y): # incorrect version


print "before swap statement: id(x):", id(x), "id(y):",
id(y)
x, y = y, x
print "after swap statement: id(x):", id(x), "id(y):", id(y)

a, b = 0, 1
print "before swap function call: id(a):", id(a), "id(b):", id(b)
swap(a, b)
print "after swap function call: id(a):", id(a), "id(b):", id(b)

Run this program and describe the results. Use the results to explain
why this version of swap does not work as intended. What will be the
values of a and b after the call to swap?

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
243

2. Create a module named seqtools.py. Add the functions encapsulate


and insert_in_middle from the chapter. Add doctests which test that
these two functions work as intended with all three sequence types.

3. Add each of the following functions to seqtools.py:

def make_empty(seq):
"""
>>> make_empty([1, 2, 3, 4])
[]
>>> make_empty(('a', 'b', 'c'))
()
>>> make_empty("No, not me!")
''
"""

def insert_at_end(val, seq):


"""
>>> insert_at_end(5, [1, 3, 4, 6])
[1, 3, 4, 6, 5]
>>> insert_at_end('x', 'abc')
'abcx'
>>> insert_at_end(5, (1, 3, 4, 6))
(1, 3, 4, 6, 5)
"""

def insert_in_front(val, seq):


"""
>>> insert_in_front(5, [1, 3, 4, 6])
[5, 1, 3, 4, 6]
>>> insert_in_front(5, (1, 3, 4, 6))
(5, 1, 3, 4, 6)
>>> insert_in_front('x', 'abc')
'xabc'
"""

def index_of(val, seq, start=0):


"""
>>> index_of(9, [1, 7, 11, 9, 10])
3
>>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5))
3
>>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5), 4)
6

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
244

>>> index_of('y', 'happy birthday')


4
>>> index_of('banana', ['apple', 'banana', 'cherry',
'date'])
1
>>> index_of(5, [2, 3, 4])
-1
>>> index_of('b', ['apple', 'banana', 'cherry', 'date'])
-1
"""

def remove_at(index, seq):


"""
>>> remove_at(3, [1, 7, 11, 9, 10])
[1, 7, 11, 10]
>>> remove_at(5, (1, 4, 6, 7, 0, 9, 3, 5))
(1, 4, 6, 7, 0, 3, 5)
>>> remove_at(2, "Yomrktown")
'Yorktown'
"""

def remove_val(val, seq):


"""
>>> remove_val(11, [1, 7, 11, 9, 10])
[1, 7, 9, 10]
>>> remove_val(15, (1, 15, 11, 4, 9))
(1, 11, 4, 9)
>>> remove_val('what', ('who', 'what', 'when', 'where',
'why', 'how'))
('who', 'when', 'where', 'why', 'how')
"""

def remove_all(val, seq):


"""
>>> remove_all(11, [1, 7, 11, 9, 11, 10, 2, 11])
[1, 7, 9, 10, 2]
>>> remove_all('i', 'Mississippi')
'Msssspp'
"""

def count(val, seq):


"""
>>> count(5, (1, 5, 3, 7, 5, 8, 5))
3
>>> count('s', 'Mississippi')
4

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
245

>>> count((1, 2), [1, 5, (1, 2), 7, (1, 2), 8, 5])


2
"""

def reverse(seq):
"""
>>> reverse([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
>>> reverse(('shoe', 'my', 'buckle', 2, 1))
(1, 2, 'buckle', 'my', 'shoe')
>>> reverse('Python')
'nohtyP'
"""

def sort_sequence(seq):
"""
>>> sort_sequence([3, 4, 6, 7, 8, 2])
[2, 3, 4, 6, 7, 8]
>>> sort_sequence((3, 4, 6, 7, 8, 2))
(2, 3, 4, 6, 7, 8)
>>> sort_sequence("nothappy")
'ahnoppty'
"""

if __name__ == "__main__":
import doctest
doctest.testmod()

As usual, work on each of these one at a time until they pass all of
the doctests.

4. Write a function, recursive_min, that returns the smallest value in a


nested number list:

def recursive_min(nested_num_list):
"""
>>> recursive_min([2, 9, [1, 13], 8, 6])
1
>>> recursive_min([2, [[100, 1], 90], [10, 13], 8, 6])
1
>>> recursive_min([2, [[13, -7], 90], [1, 100], 8, 6])
-7
>>> recursive_min([[[-13, 7], 90], 2, [1, 100], 8, 6])
-13

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
246

"""

Your function should pass the doctests.

5. Write a function recursive_count that returns the number of


occurances of target in nested_number_list:

def recursive_count(target, nested_num_list):


"""
>>> recursive_count(2, [2, 9, [2, 1, 13, 2], 8, [2, 6]])
4
>>> recursive_count(7, [[9, [7, 1, 13, 2], 8], [7, 6]])
2
>>> recursive_count(15, [[9, [7, 1, 13, 2], 8], [2, 6]])
0
>>> recursive_count(5, [[5, [5, [1, 5], 5], 5], [5, 6]])
6
"""

As usual, your function should pass the doctests.

6. Write a function flatten that returns a simple list of numbers


containing all the values in a nested_number_list:

def flatten(nested_num_list):
"""
>>> flatten([2, 9, [2, 1, 13, 2], 8, [2, 6]])
[2, 9, 2, 1, 13, 2, 8, 2, 6]
>>> flatten([[9, [7, 1, 13, 2], 8], [7, 6]])
[9, 7, 1, 13, 2, 8, 7, 6]
>>> flatten([[9, [7, 1, 13, 2], 8], [2, 6]])
[9, 7, 1, 13, 2, 8, 2, 6]
>>> flatten([[5, [5, [1, 5], 5], 5], [5, 6]])
[5, 5, 1, 5, 5, 5, 5, 6]
"""

Run your function to confirm that the doctests pass.

7. Write a function named readposint that prompts the user for a


positive integer and then checks the input to confirm that it meets
the requirements. A sample session might look like this:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
247

>>> num = readposint()


Please enter a positive integer: yes
yes is not a positive integer. Try again.
Please enter a positive integer: 3.14
3.14 is not a positive integer. Try again.
Please enter a positive integer: -6
-6 is not a positive integer. Try again.
Please enter a positive integer: 42
>>> num
42
>>> num2 = readposint("Now enter another one: ")
Now enter another one: 31
>>> num2
31
>>>

Use Python’s exception handling mechanisms in confirming that the


user’s input is valid.

8. Give the Python interpreter’s response to each of the following:

1. >>> nums = [1, 2, 3, 4]


>>> [x**3 for x in nums]

2. >>> nums = [1, 2, 3, 4]


>>> [x**2 for x in nums if x**2 != 4]

3. >>> nums = [1, 2, 3, 4]


>>> [(x, y) for x in nums for y in nums]

4. >>> nums = [1, 2, 3, 4]


>>> [(x, y) for x in nums for y in nums if x != y]

You should anticipate the results before you try them in the
interpreter.

9. Use either pydoc or the on-line documentation at http://pydoc.org to


find out what sys.getrecursionlimit() and sys.setrecursionlimit(n) do.
Create several experiments like what was done in
infinite_recursion.py to test your understanding of how these module
functions work.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
248

10.Rewrite the factorial function using iteration instead of recursion.


Call your new function with 1000 as an argument and make note of
how fast it returns a value.

11.Write a program named litter.py that creates an empty file named


trash.txt in each subdirectory of a directory tree given the root of the
tree as an argument (or the current directory as a default). Now write
a program named cleanup.py that removes all these files. Hint: Use
the tree program from the mini case study as a basis for these two
recursive programs.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
249

12. Dictionaries
All of the compound data types we have studied in detail so far — strings,
lists, and tuples—are sequence types, which use integers as indices to
access the values they contain within them.

Dictionaries are a different kind of compound type. They are Python’s


built-in mapping type. They map keys, which can be any immutable
type, to values, which can be any type, just like the values of a list or tuple.

As an example, we will create a dictionary to translate English words into


Spanish. For this dictionary, the keys are strings.

One way to create a dictionary is to start with the empty dictionary and
add key-value pairs. The empty dictionary is denoted {}:

>>> eng2sp = {}
>>> eng2sp['one'] = 'uno'
>>> eng2sp['two'] = 'dos'

The first assignment creates a dictionary named eng2sp; the other


assignments add new key-value pairs to the dictionary. We can print the
current value of the dictionary in the usual way:

>>> print eng2sp


{'two': 'dos', 'one': 'uno'}

The key-value pairs of the dictionary are seperated by commas. Each pair
contains a key and a value separated by a colon.

The order of the pairs may not be what you expected. Python uses
complex algorithms to determine where the key-value pairs are stored in a

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
250

dictionary. For our purposes we can think of this ordering as


unpredicatable.

Another way to create a dictionary is to provide a list of key-value pairs


using the same syntax as the previous output:

>>> eng2sp = {'one': 'uno', 'two': 'dos', 'three': 'tres'}

It doesn’t matter what order we write the pairs. The values in a dictionary
are accessed with keys, not with indices, so there is no need to care about
ordering.

Here is how we use a key to look up the corresponding value:

>>> print eng2sp['two']


'dos'

The key 'two' yields the value 'dos'.

12.1. Dictionary operations

The del statement removes a key-value pair from a dictionary. For


example, the following dictionary contains the names of various fruits and
the number of each fruit in stock:

>>> inventory = {'apples': 430, 'bananas': 312, 'oranges': 525,


'pears': 217}
>>> print inventory
{'oranges': 525, 'apples': 430, 'pears': 217, 'bananas': 312}

If someone buys all of the pears, we can remove the entry from the
dictionary:

>>> del inventory['pears']


>>> print inventory
{'oranges': 525, 'apples': 430, 'bananas': 312}

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
251

Or if we’re expecting more pears soon, we might just change the value
associated with pears:

>>> inventory['pears'] = 0
>>> print inventory
{'oranges': 525, 'apples': 430, 'pears': 0, 'bananas': 312}

The len function also works on dictionaries; it returns the number of key-
value pairs:

>>> len(inventory)
4

12.2. Dictionary methods

Dictionaries have a number of useful built-in methods.

The keys method takes a dictionary and returns a list of its keys.

>>> eng2sp.keys()
['three', 'two', 'one']

As we saw earlier with strings and lists, dictionary methods use dot
notation, which specifies the name of the method to the right of the dot
and the name of the object on which to apply the method immediately to
the left of the dot. The parentheses indicate that this method takes no
parameters.

A method call is called an invocation; in this case, we would say that we


are invoking the keys method on the object eng2sp. As we will see in a few
chapters when we talk about object oriented programming, the object on
which a method is invoked is actually the first argument to the method.

The values method is similar; it returns a list of the values in the dictionary:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
252

>>> eng2sp.values()
['tres', 'dos', 'uno']

The items method returns both, in the form of a list of tuples — one for
each key-value pair:

>>> eng2sp.items()
[('three', 'tres'), ('two', 'dos'), ('one', 'uno')]

The has_key method takes a key as an argument and returns True if the
key appears in the dictionary and False otherwise:

>>> eng2sp.has_key('one')
True
>>> eng2sp.has_key('deux')
False

This method can be very useful, since looking up a non-existant key in a


dictionary causes a runtime error:

>>> eng2esp['dog']
Traceback (most recent call last):
File "", line 1, in
KeyError: 'dog'
>>>

12.3. Aliasing and copying

Because dictionaries are mutable, you need to be aware of aliasing.


Whenever two variables refer to the same object, changes to one affect the
other.

If you want to modify a dictionary and keep a copy of the original, use the
copy method. For example, opposites is a dictionary that contains pairs of
opposites:

>>> opposites = {'up': 'down', 'right': 'wrong', 'true': 'false'}

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
253

>>> alias = opposites


>>> copy = opposites.copy()

alias and opposites refer to the same object; copy refers to a fresh copy of
the same dictionary. If we modify alias, opposites is also changed:

>>> alias['right'] = 'left'


>>> opposites['right']
'left'

If we modify copy, opposites is unchanged:

>>> copy['right'] = 'privilege'


>>> opposites['right']
'left'

12.4. Sparse matrices

We previously used a list of lists to represent a matrix. That is a good


choice for a matrix with mostly nonzero values, but consider a sparse
matrix like this one:

The list representation contains a lot of zeroes:

matrix = [[0, 0, 0, 1, 0],


[0, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 3, 0]]

An alternative is to use a dictionary. For the keys, we can use tuples that
contain the row and column numbers. Here is the dictionary representation
of the same matrix:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
254

matrix = {(0, 3): 1, (2, 1): 2, (4, 3): 3}

We only need three key-value pairs, one for each nonzero element of the
matrix. Each key is a tuple, and each value is an integer.

To access an element of the matrix, we could use the [] operator:

matrix[(0, 3)]
1

Notice that the syntax for the dictionary representation is not the same as
the syntax for the nested list representation. Instead of two integer indices,
we use one index, which is a tuple of integers.

There is one problem. If we specify an element that is zero, we get an


error, because there is no entry in the dictionary with that key:

>>> matrix[(1, 3)]


KeyError: (1, 3)

The get method solves this problem:

>>> matrix.get((0, 3), 0)


1

The first argument is the key; the second argument is the value get should
return if the key is not in the dictionary:

>>> matrix.get((1, 3), 0)


0

get definitely improves the semantics of accessing a sparse matrix. Shame


about the syntax.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
255

12.5. Hints

If you played around with the fibonacci function from the last chapter, you
might have noticed that the bigger the argument you provide, the longer
the function takes to run. Furthermore, the run time increases very quickly.
On one of our machines, fibonacci(20) finishes instantly, fibonacci(30)
takes about a second, and fibonacci(40) takes roughly forever.

To understand why, consider this call graph for fibonacci with n = 4:

A call graph shows a set function frames, with lines connecting each frame
to the frames of the functions it calls. At the top of the graph, fibonacci
with n = 4 calls fibonacci with n = 3 and n = 2. In turn, fibonacci with n = 3
calls fibonacci with n = 2 and n = 1. And so on.

Count how many times fibonacci(0) and fibonacci(1) are called. This is an
inefficient solution to the problem, and it gets far worse as the argument
gets bigger.

A good solution is to keep track of values that have already been computed
by storing them in a dictionary. A previously computed value that is stored

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
256

for later use is called a hint. Here is an implementation of fibonacci using


hints:

previous = {0: 0, 1: 1}

def fibonacci(n):
if previous.has_key(n):
return previous[n]
else:
new_value = fibonacci(n-1) + fibonacci(n-2)
previous[n] = new_value
return new_value

The dictionary named previous keeps track of the Fibonacci numbers we


already know. We start with only two pairs: 0 maps to 1; and 1 maps to 1.

Whenever fibonacci is called, it checks the dictionary to determine if it


contains the result. If it’s there, the function can return immediately
without making any more recursive calls. If not, it has to compute the new
value. The new value is added to the dictionary before the function returns.

Using this version of fibonacci, our machines can compute fibonacci(100) in


an eyeblink.

>>> fibonacci(100)
354224848179261915075L

The L at the end of the number indicates that it is a long integer.

12.6. Long integers

Python provides a type called long that can handle any size integer (limited
only by the amount of memory you have on your computer).

There are three ways to create a long value. The first one is to compute an
arithmetic expression too large to fit inside an int. We already saw this in

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
257

the fibonacci(100) example above. Another way is to write an integer with


a capital L at the end of your number:

>>> type(1L)

The third is to call long with the value to be converted as an argument.


long, just like int and float, can convert int``s, ``floats, and even strings of
digits to long integers:

>>> long(7)
7L
>>> long(3.9)
3L
>>> long('59')
59L

12.7. Counting letters

In Chapter 7, we wrote a function that counted the number of occurrences


of a letter in a string. A more general version of this problem is to form a
histogram of the letters in the string, that is, how many times each letter
appears.

Such a histogram might be useful for compressing a text file. Because


different letters appear with different frequencies, we can compress a file
by using shorter codes for common letters and longer codes for letters that
appear less frequently.

Dictionaries provide an elegant way to generate a histogram:

>>> letter_counts = {}
>>> for letter in "Mississippi":
... letter_counts[letter] = letter_counts.get (letter, 0) + 1
...
>>> letter_counts
{'M': 1, 's': 4, 'p': 2, 'i': 4}

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
258

We start with an empty dictionary. For each letter in the string, we find the
current count (possibly zero) and increment it. At the end, the dictionary
contains pairs of letters and their frequencies.

It might be more appealing to display the histogram in alphabetical order.


We can do that with the items and sort methods:

>>> letter_items = letter_counts.items()


>>> letter_items.sort()
>>> print letter_items
[('M', 1), ('i', 4), ('p', 2), ('s', 4)]

12.8. Case Study: Robots

12.8.1. The game

In this case study we will write a version of the classic console based game,
robots.

Robots is a turn-based game in which the protagonist, you, are trying to


stay alive while being chased by stupid, but relentless robots. Each robot
moves one square toward you each time you move. If they catch you, you
are dead, but if they collide they die, leaving a pile of dead robot junk in
their wake. If other robots collide with the piles of junk, they die.

The basic strategy is to position yourself so that the robots collide with
each other and with piles of junk as they move toward you. To make the
game playable, you also are given the ability to teleport to another location
on the screen – 3 times safely and randomly thereafter, so that you don’t
just get forced into a corner and loose every time.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
259

12.8.2. Setting up the world, the player, and the main loop

Let’s start with a program that places the player on the screen and has a
function to move her around in response to keys pressed:

#
# robots.py
#
from gasp import *

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
GRID_WIDTH = SCREEN_WIDTH/10 - 1
GRID_HEIGHT = SCREEN_HEIGHT/10 - 1

def place_player():
x = random.randint(0, GRID_WIDTH)
y = random.randint(0, GRID_HEIGHT)
return {'shape': Circle((10*x+5, 10*y+5), 5, filled=True), 'x': x,
'y': y}

def move_player(player):
update_when('key_pressed')
if key_pressed('escape'):
return True
elif key_pressed('4'):
if player['x'] > 0: player['x'] -= 1
elif key_pressed('7'):
if player['x'] > 0: player['x'] -= 1
if player['y'] < GRID_HEIGHT: player['y'] += 1
elif key_pressed('8'):
if player['y'] < GRID_HEIGHT: player['y'] += 1
elif key_pressed('9'):
if player['x'] < GRID_WIDTH: player['x'] += 1
if player['y'] < GRID_HEIGHT: player['y'] += 1
elif key_pressed('6'):
if player['x'] < GRID_WIDTH: player['x'] += 1
elif key_pressed('3'):
if player['x'] < GRID_WIDTH: player['x'] += 1
if player['y'] > 0: player['y'] -= 1
elif key_pressed('2'):
if player['y'] > 0: player['y'] -= 1
elif key_pressed('1'):

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
260

if player['x'] > 0: player['x'] -= 1


if player['y'] > 0: player['y'] -= 1
else:
return False

move_to(player['shape'], (10*player['x']+5, 10*player['y']+5))

return False

def play_game():
begin_graphics(SCREEN_WIDTH, SCREEN_HEIGHT,
title="Robots")
player = place_player()
finished = False
while not finished:
finished = move_player(player)
end_graphics()

if __name__ == '__main__':
play_game()

Programs like this one that involve interacting with the user through
events such as key presses and mouse clicks are called event-driven
programs.

The main event loop at this stage is simply:

while not finished:


finished = move_player(player)

The event handling is done inside the move_player function.


update_when('key_pressed') waits until a key has been pressed before
moving to the next statement. The multi-way branching statement then
handles the all keys relevent to game play.

Pressing the escape key causes move_player to return True, making not
finished false, thus exiting the main loop and ending the game. The 4, 7, 8,
9, 6, 3, 2, and 1 keys all cause the player to move in the appropriate

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
261

direction, if she isn’t blocked by the edge of a window.

12.8.3. Adding a robot

Now let’s add a single robot that heads toward the player each time the
player moves.

Add the following place_robot function between place_player and


move_player:

def place_robot():
x = random.randint(0, GRID_WIDTH)
y = random.randint(0, GRID_HEIGHT)
return {'shape': Box((10*x, 10*y), 10, 10), 'x': x, 'y': y}

Add move_robot immediately after move_player:

def move_robot(robot, player):


if robot['x'] < player['x']: robot['x'] += 1
elif robot['x'] > player['x']: robot['x'] -= 1

if robot['y'] < player['y']: robot['y'] += 1


elif robot['y'] > player['y']: robot['y'] -= 1

move_to(robot['shape'], (10*robot['x'], 10*robot['y']))

We need to pass both the robot and the player to this function so that it
can compare their locations and move the robot toward the player.

Now add the line robot = place_robot() in the main body of the program
immediately after the line player = place_player(), and add the
move_robot(robot, player) call inside the main loop immediately after
finished = move_player(player).

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
262

12.8.4. Checking for Collisions

We now have a robot that moves relentlessly toward our player, but once it
catches her it just follows her around wherever she goes. What we want to
happen is for the game to end as soon as the player is caught. The
following function will determine if that has happened:

def collided(robot, player):


return player['x'] == robot['x'] and player['y'] == robot['y']

Place this new function immediately below the move_player function. Now
let’s modify play_game to check for collisions:

def play_game():
begin_graphics(SCREEN_WIDTH, SCREEN_HEIGHT)
player = place_player()
robot = place_robot()
defeated = False

while not defeated:


quit = move_player(player)
if quit:
break
move_robot(robot, player)
defeated = collided(robot, player)

if defeated:
remove_from_screen(player['shape'])
remove_from_screen(robot['shape'])
Text("They got you!", (240, 240), size=32)
sleep(3)

end_graphics()

We rename the variable finished to defeated, which is now set to the result
of collided. The main loop runs as long as defeated is false. Pressing the
key still ends the program, since we check for quit and break out of the
main loop if it is true. Finally, we check for defeated immediately after the
main loop and display an appropriate message if it is true.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
263

12.8.5. Adding more robots

There are several things we could do next:

• give the player the ability to teleport to another location to escape


pursuit.
• provide safe placement of the player so that it never starts on top of
a robot.
• add more robots.

Adding the ability to teleport to a random location is the easiest task, and it
has been left to you to complete as an exercise.

How we provide safe placement of the player will depend on how we


represent multiple robots, so it makes sense to tackle adding more robots
first.

To add a second robot, we could just create another variable named


something like robot2 with another call to place_robot. The problem with
this approach is that we will soon want lots of robots, and giving them all
their own names will be cumbersome. A more elegant solution is to place
all the robots in a list:

def place_robots(numbots):
robots = []
for i in range(numbots):
robots.append(place_robot())
return robots

Now instead of calling place_robot in play_game, call place_robots, which


returns a single list containing all the robots:

robots = place_robots(2)

With more than one robot placed, we have to handle moving each one of

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
264

them. We have already solved the problem of moving a single robot,


however, so traversing the list and moving each one in turn does the trick:

def move_robots(robots, player):


for robot in robots:
move_robot(robot, player)

Add move_robots immediately after move_robot, and change play_game to


call move_robots instead of move_robot.

We now need to check each robot to see if it has collided with the player:

def check_collisions(robots, player):


for robot in robots:
if collided(robot, player):
return True
return False

Add check_collisions immediately after collided and change the line in


play_game that sets defeated to call check_collisions instead of collided.

Finally, we need to loop over robots to remove each one in turn if defeated
becomes true. Adding this has been left as an exercise.

12.8.6. Winning the game

The biggest problem left in our game is that there is no way to win. The
robots are both relentless and indestructible. With careful maneuvering
and a bit of luck teleporting, we can reach the point where it appears there
is only one robot chasing the player (all the robots will actually just be on
top of each other). This moving pile of robots will continue chasing our
hapless player until it catches it, either by a bad move on our part or a
teleport that lands the player directly on the robots.

When two robots collide they are supposed to die, leaving behind a pile of

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
265

junk. A robot (or the player) is also supposed to die when it collides with a
pile of junk. The logic for doing this is quite tricky. After the player and
each of the robots have moved, we need to:

1. Check whether the player has collided with a robot or a pile of junk. If
so, set defeated to true and break out of the game loop.
2. Check each robot in the robots list to see if it has collided with a pile
of junk. If it has, disgard the robot (remove it from the robots list).
3. Check each of the remaining robots to see if they have collided with
another robot. If they have, disgard all the robots that have collided
and place a pile of junk at the locations they occupied.
4. Check if any robots remain. If not, end the game and mark the player
the winner.

Let’s take on each of these tasks in turn.

12.8.7. Adding junk

Most of this work will take place inside our check_collisions function. Let’s
start by modifying collided, changing the names of the parameters to
reflect its more general use:

def collided(thing1, thing2):


return thing1['x'] == thing2['x'] and thing1['y'] == thing2['y']

We now introduce a new empty list named junk immediately after the call
to place_robots:

junk = []

and modify check_collisions to incorporate the new list:

def check_collisions(robots, junk, player):


# check whether player has collided with anything

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
266

for thing in robots + junk:


if collided(thing, player):
return True
return False

Be sure to modify the call to check_collisions (currently defeated =


check_collisions(robots, player)) to include junk as a new argument.

Again, we need to fix the logic after if defeated: to remove the new junk
from the screen before displaying the They got you! message:

for thing in robots + junk:


remove_from_screen(thing['shape'])

Since at this point junk is always an empty list, we haven’t changed the
behavior of our program. To test whether our new logic is actually working,
we could introduce a single junk pile and run our player into it, at which
point the game should remove all items from the screen and display the
ending message.

It will be helpful to modify our program temporarily to change the random


placement of robots and player to predetermined locations for testing. We
plan to use solid boxes to represent junk piles. We observe that placing a
robot is very similar to placing a junk pile, and modify place_robot to do
both:

def place_robot(x, y, junk=False):


return {'shape': Box((10*x, 10*y), 10, 10, filled=junk), 'x': x, 'y':
y}

Notice that x and y are now parameters, along with a new parameter that
we will use to set filled to true for piles of junk.

Our program is now broken, since the call in place_robots to place_robot


does not pass arguments for x and y. Fixing this and setting up the
program for testing is left to you as an exercise.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
267

12.8.8. Removing robots that hit junk

To remove robots that collide with piles of junk, we add a nested loop to
check_collisions between each robot and each pile of junk. Our first
attempt at this does not work:

def check_collisions(robots, junk, player):


# check whether player has collided with anything
for thing in robots + junk:
if collided(thing, player):
return True

# remove robots that have collided with a pile of junk


for robot in robots:
for pile in junk:
if collided(robot, pile):
robots.remove(robot)

return False

Running this new code with the program as setup in exercise 11, we find a
bug. It appears that the robots continue to pass through the pile of junk as
before.

Actually, the bug is more subtle. Since we have two robots on top of each
other, when the collision of the first one is detected and that robot is
removed, we move the second robot into the first position in the list and it
is missed by the next iteration. It is generally dangerous to modify a list
while you are iterating over it. Doing so can introduce a host of difficult to
find errors into your program.

The solution in this case is to loop over the robots list backwards, so that
when we remove a robot from the list all the robots whose list indeces
change as a result are robots we have already evaluated.

As usual, Python provides an elegant way to do this. The built-in function,

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
268

reversed provides for backward iteration over a sequence. Replacing:

for robot in robots:

with:

for robot in reversed(robots):

will make our program work the way we intended.

12.8.9. Turning robots into junk and enabling the player to win

We now want to check each robot to see if it has collided with any other
robots. We will remove all robots that have collided, leaving a single pile of
junk in their wake. If we reach a state where there are no more robots, the
player wins.

Once again we have to be careful not to introduce bugs related to


removing things from a list over which we are iterating.

Here is the plan:

1. Check each robot in robots (an outer loop, traversing forward).


2. Compare it with every robot that follows it (an inner loop, traversing
backward).
3. If the two robots have collided, add a piece of junk at their location,
mark the first robot as junk, and remove the second one.
4. Once all robots have been checked for collisions, traverse the robots
list once again in reverse, removing all robots marked as junk.
5. Check to see if any robots remain. If not, declare the player the
winner.

Adding the following to check_collisions will accomplish most of what we

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
269

need to do:

# remove robots that collide and leave a pile of junk


for index, robot1 in enumerate(robots):
for robot2 in reversed(robots[index+1:]):
if collided(robot1, robot2):
robot1['junk'] = True
junk.append(place_robot(robot1['x'], robot1['y'], True))
remove_from_screen(robot2['shape'])
robots.remove(robot2)

for robot in reversed(robots):


if robot['junk']:
remove_from_screen(robot['shape'])
robots.remove(robot)

We make use of the enumerate function we saw in Chapter 9 to get both


the index and value of each robot as we traverse forward. Then a reverse
traversal of the slice of the remaining robots, reversed(robots[index+1:]),
sets up the collision check.

Whenever two robots collide, our plan calls for adding a piece of junk at
that location, marking the first robot for later removal (we still need it to
compare with the other robots), and immediately removing the second
one. The body of the if collided(robot1, robot2): conditional is designed to
do just that, but if you look carefully at the line:

robot1['junk'] = True

you should notice a problem. robot1['junk'] will result in a systax error,


since our robot dictionary does not yet contain a 'junk' key. To fix this we
modify place_robot to accomodate the new key:

def place_robot(x, y, junk=False):


return {'shape': Box((10*x, 10*y), 10, 10, filled=junk),
'x': x, 'y': y, 'junk': junk}

It is not at all unusual for data structures to change as program

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
270

development proceeds. Stepwise refinement of both program data and


logic is a normal part of the structured programming process.

After robot1 is marked as junk, we add a pile of junk to the junk list at the
same location with junk.append(place_robot(robot1['x'], robot1['y'], True)),
and then remove robot2 from the game by first removing its shape from
the graphics window and then removing it from the robots list.

The next loop traverses backward over the robots list removing all the
robots previously marked as junk. Since the player wins when all the robots
die, and the robot list will be empty when it no longer contains live robots,
we can simply check whether robots is empty to determine whether or not
the player has won.

This can be done in check_collisions immediately after we finish checking


robot collisions and removing dead robots by adding:

if not robots:
return ...

Hmmm... What should we return? In its current state, check_collisions is a


boolean function that returns true when the player has collided with
something and lost the game, and false when the player has not lost and
the game should continue. That is why the variable in the play_game
function that catches the return value is called defeated.

Now we have three possible states:

1. robots is not empty and the player has not collided with anything –
the game is still in play
2. the player has collided with something – the robots win
3. the player has not collided with anything and robots is empty – the
player wins

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
271

In order to handle this with as few changes as possible to our present


program, we will take advantage of the way that Python permits sequence
types to live double lives as boolean values. We will return an empty string
– which is false – when game play should continue, and either "robots_win"
or "player_wins" to handle the other two cases. check_collisions should now
look like this:

def check_collisions(robots, junk, player):


# check whether player has collided with anything
for thing in robots + junk:
if collided(thing, player):
return "robots_win"

# remove robots that have collided with a pile of junk


for robot in reversed(robots):
for pile in junk:
if collided(robot, pile):
robots.remove(robot)

# remove robots that collide and leave a pile of junk


for index, robot1 in enumerate(robots):
for robot2 in reversed(robots[index+1:]):
if collided(robot1, robot2):
robot1['junk'] = True
junk.append(place_robot(robot1['x'], robot1['y'], True))
remove_from_screen(robot2['shape'])
robots.remove(robot2)

for robot in reversed(robots):


if robot['junk']:
remove_from_screen(robot['shape'])
robots.remove(robot)

if not robots:
return "player_wins"

return ""

A few corresponding changes need to be made to play_game to use the


new return values. These are left as an exercise.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
272

12.9. Glossary

dictionary
A collection of key-value pairs that maps from keys to values. The keys
can be any immutable type, and the values can be any type.
mapping type
A mapping type is a data type comprised of a collection of keys and
associated values. Python’s only built-in mapping type is the
dictionary. Dictionaries implement the associative array abstract data
type.
key
A data item that is mapped to a value in a dictionary. Keys are used to
look up values in a dictionary.
key-value pair
One of the pairs of items in a dictionary. Values are looked up in a
dictionary by key.
hint
Temporary storage of a precomputed value to avoid redundant
computation.
event
A signal such as a keyboard press, mouse click, or message from
another program.
event-driven program
<fill in definition here>
event loop
A programming construct that waits for events and processes them.
overflow
A numerical result that is too large to be represented in a numerical
format.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
273

12.10. Exercises

1. Write a program that reads in a string on the command line and


returns a table of the letters of the alphabet in alphabetical order
which occur in the string together with the number of times each
letter occurs. Case should be ignored. A sample run of the program
would look this this:

$ python letter_counts.py "ThiS is String with Upper and


lower case Letters."
a 2
c 1
d 1
e 5
g 1
h 2
i 4
l 2
n 2
o 1
p 2
r 4
s 5
t 5
u 1
w 2
$

2. Give the Python interpreter’s response to each of the following from a


continuous interpreter session:

1. >>> d = {'apples': 15, 'bananas': 35, 'grapes': 12}


>>> d['banana']

2. >>> d['oranges'] = 20
>>> len(d)

3. >>> d.has_key('grapes')

4. >>> d['pears']

5. >>> d.get('pears', 0)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
274

6. >>> fruits = d.keys()


>>> fruits.sort()
>>> print fruits

7. >>> del d['apples']


>>> d.has_key('apples')

Be sure you understand why you get each result. Then apply what
you have learned to fill in the body of the function below:

def add_fruit(inventory, fruit, quantity=0):


"""
Adds quantity of fruit to inventory.

>>> new_inventory = {}
>>> add_fruit(new_inventory, 'strawberries', 10)
>>> new_inventory.has_key('strawberries')
True
>>> new_inventory['strawberries']
10
>>> add_fruit(new_inventory, 'strawberries', 25)
>>> new_inventory['strawberries']
"""

Your solution should pass the doctests.

3. Write a program called alice_words.py that creates a text file named


alice_words.txt containing an alphabetical listing of all the words
found in alice_in_wonderland.txt together with the number of times
each word occurs. The first 10 lines of your output file should look
something like this:

Word Count
=======================
a 631
a-piece 1
abide 1
able 1
about 94
above 3
absence 1

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
275

absurd 2

How many times does the word, alice, occur in the book?

4. What is the longest word in Alice in Wonderland ? How many


charactes does it have?

5. Copy the code from the Setting up the world, the player, and the
main loop section into a file named robots.py and run it. You should
be able to move the player around the screen using the numeric
keypad and to quit the program by pressing the escape key.

6. Laptops usually have smaller keyboards than desktop computers that


do not include a seperate numeric keypad. Modify the robots program
so that it uses ‘a’, ‘q’, ‘w’, ‘e’, ‘d’, ‘c’, ‘x’, and ‘z’ instead of ‘4’, ‘7’, ‘8’,
‘9’, ‘6’, ‘3’, ‘2’, and ‘1’ so that it will work on a typical laptop
keyboard.

7. Add all the code from the Adding a robot section in the places
indicated. Make sure the program works and that you now have a
robot following around your player.

8. Add all the code from the Checking for Collisions section in the places
indicated. Verify that the program ends when the robot catches the
player after displaying a They got you! message for 3 seconds.

9. Modify the move_player function to add the ability for the player to
jump to a random location whenever the 0 key is pressed. (hint:
place_player already has the logic needed to place the player in a
random location. Just add another conditional branch to move_player
that uses this logic when key_pressed('0') is true.) Test the program
to verify that your player can now teleport to a random place on the
screen to get out of trouble.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
276

10.Make all the changes to your program indicated in Adding more


robots. Be sure to loop over the robots list, removing each robot in
turn, after defeated becomes true. Test your program to verify that
there are now two robots chasing your player. Let a robot catch you
to test whether you have correctly handled removing all the robots.
Change the argument from 2 to 4 in robots = place_robots(2) and
confirm that you have 4 robots.

11.Make the changes to your program indicated in Adding ``junk``. Fix


place_robots by moving the random generation of values for x and y
to the appropriate location and passing these values as arguments in
the call to place_robot. Now we are ready to make temporary
modifications to our program to remove the randomness so we can
control it for testing. We can start by placing a pile of junk in the
center of our game board. Change:
junk = []
to:
junk = [place_robot(GRID_WIDTH/2, GRID_HEIGHT/2,
junk=True)]
Run the program and confirm that there is a black box in the center
of the board.Now change place_player so that it looks like this:
def place_player():
# x = random.randint(0, GRID_WIDTH)
# y = random.randint(0, GRID_HEIGHT)
x, y = GRID_WIDTH/2 + 3, GRID_HEIGHT/2
return {'shape': Circle((10*x+5, 10*y+5), 5,
filled=True), 'x': x, 'y': y}
Finally, temporarily comment out the random generation of x and y
values in place_robots and the creation of numbots robots. Replace
this logic with code to create two robots in fixed locations:
def place_robots(numbots):
robots = []
# for i in range(numbots):
# x = random.randint(0, GRID_WIDTH)
# y = random.randint(0, GRID_HEIGHT)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
277

# robots.append(place_robot(x, y))
robots.append(place_robot(GRID_WIDTH/2 - 4,
GRID_HEIGHT/2 + 2))
robots.append(place_robot(GRID_WIDTH/2 - 4,
GRID_HEIGHT/2 - 2))
return robots
When you start your program now, it should look like this:

When you run this program and either stay still (by pressing the 5
repeatedly) or move away from the pile of junk, you can confirm that
the robots move through it unharmed. When you move into the junk
pile, on the other hand, you die.
12.Make the following modifications to play_game to integrate with the
changes made in Turning robots into junk and enabling the player to
win:
1. Rename defeated to winner and initialize it to the empty string
instead of False.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
278

13. Classes and objects

13.1. Object-oriented programming

Python is an object-oriented programming language, which means


that it provides features that support object-oriented programming (
OOP).

Object-oriented programming has its roots in the 1960s, but it wasn’t until
the mid 1980s that it became the main programming paradigm used in
the creation of new software. It was developed as a way to handle the
rapidly increasing size and complexity of software systems, and to make it
easier to modify these large and complex systems over time.

Up to this point we have been writing programs using a procedural


programming paradigm. In procedural programming the focus is on
writing functions or procedures which operate on data. In object-oriented
programming the focus is on the creation of objects which contain both
data and functionality together.

13.2. User-defined compound types

A class in essence defines a new data type. We have been using several
of Python’s built-in types throughout this book, we are now ready to create
our own user-defined type: the Point.

Consider the concept of a mathematical point. In two dimensions, a point is


two numbers (coordinates) that are treated collectively as a single object.
In mathematical notation, points are often written in parentheses with a
comma separating the coordinates. For example, (0, 0) represents the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
279

origin, and (x, y) represents the point x units to the right and y units up
from the origin.

A natural way to represent a point in Python is with two numeric values.


The question, then, is how to group these two values into a compound
object. The quick and dirty solution is to use a list or tuple, and for some
applications that might be the best choice.

An alternative is to define a new user-defined compound type, also called a


class. This approach involves a bit more effort, but it has advantages that
will be apparent soon.

A class definition looks like this:

class Point:
pass

Class definitions can appear anywhere in a program, but they are usually
near the beginning (after the import statements). The syntax rules for a
class definition are the same as for other compound statements. There is a
header which begins with the keyword, class, followed by the name of the
class, and ending with a colon.

This definition creates a new class called Point. The pass statement has no
effect; it is only necessary because a compound statement must have
something in its body.

By creating the Point class, we created a new type, also called Point. The
members of this type are called instances of the type or objects.
Creating a new instance is called instantiation. To instantiate a Point
object, we call a function named (you guessed it) Point:

>>> type(Point)
<type 'classobj'>

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
280

>>> p = Point()
>>> type(p)
<type 'instance'>

The variable p is assigned a reference to a new Point object. A function like


Point that creates new objects is called a constructor.

13.3. Attributes

Like real world objects, object instances have both form and function. The
form consists of data elements contained within the instance.

We can add new data elements to an instance using dot notation:

>>> p.x = 3
>>> p.y = 4

This syntax is similar to the syntax for selecting a variable from a module,
such as math.pi or string.uppercase. Both modules and instances create
their own namespaces, and the syntax for accessing names contained in
each, called attributes, is the same. In this case the attribute we are
selecting is a data item from an instance.

The following state diagram shows the result of these assignments:

The variable p refers to a Point object, which contains two attributes. Each
attribute refers to a number.

We can read the value of an attribute using the same syntax:

>>> print p.y


4

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
281

>>> x = p.x
>>> print x
3

The expression p.x means, Go to the object p refers to and get the value of
x. In this case, we assign that value to a variable named x. There is no
conflict between the variable x and the attribute x. The purpose of dot
notation is to identify which variable you are referring to unambiguously.

You can use dot notation as part of any expression, so the following
statements are legal:

print '(%d, %d)' % (p.x, p.y)


distance_squared = p.x * p.x + p.y * p.y

The first line outputs (3, 4); the second line calculates the value 25.

13.4. The initialization method and self

Since our Point class is intended to represent two dimensional


mathematical points, all point instances ought to have x and y attributes,
but that is not yet so with our Point objects.

>>> p2 = Point()
>>> p2.x
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: Point instance has no attribute 'x'
>>>

To solve this problem we add an initialization method to our class.

class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
282

13.5. Instances as parameters

You can pass an instance as a parameter in the usual way. For example:

def print_point(p):
print '(%s, %s)' % (str(p.x), str(p.y))

print_point takes a point as an argument and displays it in the standard


format. If you call print_point(blank), the output is (3, 4).

13.6. Sameness

The meaning of the word same seems perfectly clear until you give it some
thought, and then you realize there is more to it than you expected.

For example, if you say, Chris and I have the same car, you mean that his
car and yours are the same make and model, but that they are two
different cars. If you say, Chris and I have the same mother, you mean that
his mother and yours are the same person.

When you talk about objects, there is a similar ambiguity. For example, if
two Points are the same, does that mean they contain the same data
(coordinates) or that they are actually the same object?

To find out if two references refer to the same object, use the == operator.
For example:

>>> p1 = Point()
>>> p1.x = 3
>>> p1.y = 4
>>> p2 = Point()
>>> p2.x = 3
>>> p2.y = 4
>>> p1 == p2
False

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
283

Even though p1 and p2 contain the same coordinates, they are not the
same object. If we assign p1 to p2, then the two variables are aliases of the
same object:

>>> p2 = p1
>>> p1 == p2
True

This type of equality is called shallow equality because it compares only


the references, not the contents of the objects.

To compare the contents of the objects — deep equality — we can write a


function called same_point:

def same_point(p1, p2):


return (p1.x == p2.x) and (p1.y == p2.y)

Now if we create two different objects that contain the same data, we can
use same_point to find out if they represent the same point.

>>> p1 = Point()
>>> p1.x = 3
>>> p1.y = 4
>>> p2 = Point()
>>> p2.x = 3
>>> p2.y = 4
>>> same_point(p1, p2)
True

Of course, if the two variables refer to the same object, they have both
shallow and deep equality.

13.7. Rectangles

Let’s say that we want a class to represent a rectangle. The question is,
what information do we have to provide in order to specify a rectangle? To
keep things simple, assume that the rectangle is oriented either vertically

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
284

or horizontally, never at an angle.

There are a few possibilities: we could specify the center of the rectangle
(two coordinates) and its size (width and height); or we could specify one
of the corners and the size; or we could specify two opposing corners. A
conventional choice is to specify the upper-left corner of the rectangle and
the size.

Again, we’ll define a new class:

class Rectangle:
pass

And instantiate it:

box = Rectangle()
box.width = 100.0
box.height = 200.0

This code creates a new Rectangle object with two floating-point attributes.
To specify the upper-left corner, we can embed an object within an object!

box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0

The dot operator composes. The expression box.corner.x means, Go to the


object box refers to and select the attribute named corner; then go to that
object and select the attribute named x.

The figure shows the state of this object:

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
285

13.8. Instances as return values

Functions can return instances. For example, find_center takes a Rectangle


as an argument and returns a Point that contains the coordinates of the
center of the Rectangle:

def find_center(box):
p = Point()
p.x = box.corner.x + box.width/2.0
p.y = box.corner.y - box.height/2.0
return p

To call this function, pass box as an argument and assign the result to a
variable:

>>> center = find_center(box)


>>> print_point(center)
(50.0, 100.0)

13.9. Objects are mutable

We can change the state of an object by making an assignment to one of


its attributes. For example, to change the size of a rectangle without
changing its position, we could modify the values of width and height:

box.width = box.width + 50
box.height = box.height + 100

13.10. Copying

Aliasing can make a program difficult to read because changes made in


one place might have unexpected effects in another place. It is hard to
keep track of all the variables that might refer to a given object.

Copying an object is often an alternative to aliasing. The copy module

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
286

contains a function called copy that can duplicate any object:

>>> import copy


>>> p1 = Point()
>>> p1.x = 3
>>> p1.y = 4
>>> p2 = copy.copy(p1)
>>> p1 == p2
False
>>> same_point(p1, p2)
True

Once we import the copy module, we can use the copy method to make a
new Point. p1 and p2 are not the same point, but they contain the same
data.

To copy a simple object like a Point, which doesn’t contain any embedded
objects, copy is sufficient. This is called shallow copying.

For something like a Rectangle, which contains a reference to a Point, copy


doesn’t do quite the right thing. It copies the reference to the Point object,
so both the old Rectangle and the new one refer to a single Point.

If we create a box, b1, in the usual way and then make a copy, b2, using
copy, the resulting state diagram looks like this:

This is almost certainly not what we want. In this case, invoking grow_rect
on one of the Rectangles would not affect the other, but invoking
move_rect on either would affect both! This behavior is confusing and
error-prone.

Fortunately, the copy module contains a method named deepcopy that


copies not only the object but also any embedded objects. You will not be
surprised to learn that this operation is called a deep copy.

>>> b2 = copy.deepcopy(b1)

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
287

Now b1 and b2 are completely separate objects.

We can use deepcopy to rewrite grow_rect so that instead of modifying an


existing Rectangle, it creates a new Rectangle that has the same location
as the old one but new dimensions:

def grow_rect(box, dwidth, dheight):


import copy
new_box = copy.deepcopy(box)
new_box.width = new_box.width + dwidth
new_box.height = new_box.height + dheight
return new_box

13.11. Glossary

class
A user-defined compound type. A class can also be thought of as a
template for the objects that are instances of it.
instantiate
To create an instance of a class.
instance
An object that belongs to a class.
object
A compound data type that is often used to model a thing or concept in
the real world.
constructor
A method used to create new objects.
attribute
One of the named data items that makes up an instance.
shallow equality
Equality of references, or two references that point to the same object.
deep equality
Equality of values, or two references that point to objects that have the

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
288

same value.
shallow copy
To copy the contents of an object, including any references to
embedded objects; implemented by the copy function in the copy
module.
deep copy
To copy the contents of an object as well as any embedded objects,
and any objects embedded in them, and so on; implemented by the
deepcopy function in the copy module.
13.12. Exercises
1. Create and print a Point object, and then use id to print the object’s
unique identifier. Translate the hexadecimal form into decimal and
confirm that they match.
2. Rewrite the distance function from chapter 5 so that it takes two
Points as parameters instead of four numbers.
3. Write a function named move_rect that takes a Rectangle and two
parameters named dx and dy. It should change the location of the
rectangle by adding dx to the x coordinate of corner and adding dy to
the y coordinate of corner.
4. Rewrite move_rect so that it creates and returns a new Rectangle
instead of modifying the old one.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
289

Configuring Ubuntu for Python


Development
Note: the following instructions assume that you are connected to
the Internet and that you have both the main and universe
package repositories enabled. All unix shell commands are
assumed to be running from your home directory ($HOME). Finally,
any command that begins with sudo assums that you have
administrative rights on your machine. If you do not — please ask
your system administrator about installing the software you need.

What follows are instructions for setting up an Ubuntu 9.10 (Karmic) home
environment for use with this book. I use Ubuntu GNU/Linux for both
development and testing of the book, so it is the only system about which I
can personally answer setup and configuration questions.

In the spirit of software freedom and open collaboration, please contact me


if you would like to maintain a similar appendix for your own favorite
system. I’d be more than happy to link to it or put it on the Open Book
Project site, provided you agree to answer user feedback concerning it.

Thanks!

Jeffrey Elkner
Governor’s Career and Technical Academy in Arlington
Arlington, Virginia

Vim

Vim can be used very effectively for Python development, but Ubuntu only
comes with the vim-tiny package installed by default, so it doesn’t support

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
290

color syntax highlighting or auto-indenting.

To use Vim, do the following:

1. From the unix command prompt, run:

$ sudo apt-get install vim-gnome

2. Create a file in your home directory named .vimrc that contains the
following:

syntax enable
filetype indent on
set et
set sw=4
set smarttab
map :w\|!python %

When you edit a file with a .py extension, you should now have color
systax highlighting and auto indenting. Pressing the key should run your
program, and bring you back to the editor when the program completes.

To learn to use vim, run the following command at a unix command


prompt:

$ vimtutor

GASP

Several of the case studies use GASP (Graphics API for Students for
Python), which is the only additional library needed to use this book.

To install GASP on Ubuntu 9.04 (Jaunty) or later, run the following


command at a unix command prompt:

$ sudo apt-get install python-gasp

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
291

or use the synaptic package manager.

Getting GASP from Launchpad

To install the latest version of GASP into your home directory, run the
following commands at a unix command prompt:

$ sudo apt-get install bzr


$ bzr branch lp:gasp-code

$HOME environment

The following creates a useful environment in your home directory for


adding your own Python libraries and executable scripts:

1. From the command prompt in your home directory, create bin and
lib/python subdirectories by running the following commands:

$ mkdir bin lib


$ mkdir lib/python

2. Add the following lines to the bottom of your .bashrc in your home
directory:

PYTHONPATH=$HOME/lib/python
EDITOR=vim

export PYTHONPATH EDITOR

This will set your prefered editor to Vim, add your own lib/python
subdirectory for your Python libraries to your Python path, and add
your own bin directory as a place to put executable scripts. You need
to logout and log back in before your local bin directory will be in your
search path.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
292

Making a python script executable and runnable from anywhere

On unix systems, Python scripts can be made executable using the


following process:

1. Add this line as the first line in the script:

#!/usr/bin/env python

2. At the unix command prompt, type the following to make myscript.py


executable:

$ chmod +x myscript.py

3. Move myscript.py into your bin directory, and it will be runnable from
anywhere.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
293

Graphics API for Students of


Python: GASP

Introduction

Describe gasp here...

Coordinates

(0, 0) is at the bottom left of the window. The window is 640 pixels by 480,
by default. (You can make it a different size if you want to.) Coordinates
are given in units of one pixel.

All functions that take coordinates take them as a tuple (x, y).

Circle((300, 200), 10) # :) This is good


Circle(300, 200, 10) # :( This is bad

Colors

To access the color module GASP has to offer. Call color.* where * is the
color you wish to call. For example: ` color.BLACK ` This is the color black.
Check out the gasp color refrence chart to see all of the availble color
options.

The Essentials

from gasp import *

begin_graphics()

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
294

... # all of your code

end_graphics()

These are the essentials. ` from gasp import * ` imports the gasp module,
begin_graphics() starts the graphics window, and end_graphics() quits the
graphics window and ends the program. It’s dead simple, but also dead
necessary.

Graphics Functions

begin_graphics()

begin_graphics(width=800, height=600, title="My Game",


background=color.YELLOW)

This creates a graphics window with the dimensions 800x600, a title of My


Game , and a background color of yellow. With no arguments you get a
white 640x480 graphics window titled Gasp .

width
The width of the window in pixels.
height
The windows height in pixels.
title
A string that will be the title of the window.
background
It is the background of the graphics window. It can either be a color or
an image

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
295

end_graphics()

endgraphics()

Ends a graphics window.

clear_screen()

clear_screen()

Clears everything off of the graphics window. It looks like a new graphcs
window as if you just called begin_graphics().

remove_from_screen()

remove_from_screen(obj)

removes those objects from the screen

obj
A screen object of a list of screen_objects you would like to remove
from the screen

Screen Objects

The objects that you will be displayed in your graphics window. You can
manipulate these objects using the screen object methods

Plot

Plot(pos, color=color.black, size=1)

It puts a dot on the screen.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
296

pos
The coordinate on the screen that you wish to plot.
color
The color you wish the dot to be.
size
An integer that determinse the size the of the dot

Line

Line(start, end, color=color.black)

Creates a line on the screen.

start
The starting coordinate of the line.
end
The coordinate at which the line will end.
color
The color of the line

Box

Box(center, width, height, filled=False, color=color.black,


thickness=1)

This creates a Box on the screen

center
A coorinate where the center of your box will be.
width
The width in pixels of the box.
height

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
297

The height of the box in pixels.


filled
A boolean value that determines if your box will be filled
color
The color of your box.
thickness
The thickness in pixels of your box’s lines.

Polygon

Polygon(points, filled=False, color=color.black, thickness=1)

Creates a polygon on the screen

points
A list of coorinates that is each point on the polygon. The must be
more than two items in the list
filled
A boolean value. If it is False the polygon will not be filled. Else, the
polygon will not be filled
color
The color of the polygon’s lines
thickness
An integer that determines the thickness of the lines.

Circle

Circle(center, radius, filled=False, color=color.black, thickness=1)

Draws a circle, its center is a set of coordinates, and the radius is in pixels.
It defaults to not being filled and the color black.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
298

center
The circle’s center coordinate.
width
An integer that is the radius of the circle
filled
A boolean value that determines if your circle will be filled
color
The color of your circle.
thickness
The thickness in pixels of the circles lines.

Arc

Arc(center, radius, start_angle, end_angle, filled=False,


color=color.black, thickness=1)

Creates an arc on the screen.

center
A coordinate that is the center of the arc.
radius
An integer that is the distance between the center and the outer edge
of the arc.
start_angle
The start angle in degrees of the arc
end_angle
The end angle in degrees of your arc
filled
A boolean value that if True it fills the arc
color
The color the arc

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
299

thickness
The thickness in pixels of the arc

Oval

Oval(center, width, height, filled=False, color=color.black,


thickness=1)

Puts an oval on the screen wherever you want.

center
The center coordinate of the Oval
width
The width in pixels of the oval
height
The height of the oval in pixels
filled
A boolean value determining if the oval will be filles or not.
color
The oval’s color
thickness
The thickness of the ovals lines

Image

Image(file_path, center, width=None, height=None):

Loads an image onto the screen. If you only pass a width and not a height
it automatically scales the height to fit the width you passed it. It behaves
likewise when you pass just a height.

file_path

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
300

The path to the image


center
The center coordinates of the image
width
The width of the image in pixels. If width equals None then it defaults
to the image file’s width
height
The height of the image in pixels. If no height is passed it defaults to
the image file’s height

Screen Object Methods

The methods that manipulates screen objects

move_to()

move_to(obj, pos)

Move a screen object to a pos

obj
A screen object you wish to move.
pos
The coordinate on the screen that the object will move to

move_by()

move_by(obj, dx, dy)

Move a screen object relative to it’s position

obj

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
301

The screen object you wish to move


dx
How much the object will move in the ‘x’ direction. Positive or
negative.
dy
How much the object will move in the ‘y’ direction. A pixel value.

rotate_to()

rotate_to(obj, angle)

Rotate an object to an angle

obj
The screen object that will be rotated
angle
The angle in degrees that the object will be rotated to

rotate_by()

rotate_by(obj, angle)

Rotate an object a certain degree.

obj
The screen object you wish to rotate
angle
The degree that the object will be rotate. Can be positive or negative.

Text

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
302

Text()

Text(text, pos, color=color.black, size=12)

Puts text on the screen

text
A string of the text that will be displayed
pos
The center coordinate of the text
color
The color of the text
size
The font size

Mouse

mouse_position()

mouse_position()

Returns the current mouse coordinate

mouse_buttons()

mouse_buttons()

returns a dictionary of the buttons state. There is a ‘left’, ‘middle’, and


‘right’ key.

Keyboard

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
303

keys_pressed()

keys_pressed()

returns a list of all of the keys pressed at that moment.

Gasp Tools

screen_shot

screen_shot(filename)

Saves a screenshot of the current graphics screen to a png file.

filename
The file path relative to the current directory that the image will be
written to.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
304

Customizing and Contributing


to the Book
Note: the following instructions assume that you are connected to
the Internet and that you have both the main and universe
package repositories enabled. All unix shell commands are
assumed to be running from your home directory ($HOME). Finally,
any command that begins with sudo assums that you have
administrative rights on your machine. If you do not — please ask
your system administrator about installing the software you need.

This book is free as in freedom, which means you have the right to modify
it to suite your needs, and to redistribute your modifications so that our
whole community can benefit.

That freedom lacks meaning, however, if you the tools needed to make a
custom version or to contribute corrections and additions are not within
your reach. This appendix attempts to put those tools in your hands.

Thanks!

Jeffrey Elkner
Governor’s Career and Technical Academy in Arlington
Arlington, Virginia

Getting the Source

This book is marked up in ReStructuredText using a document generation


system called Sphinx.

The source code is located on the Launchpad website at

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
305

http://bazaar.launchpad.net/~thinkcspy/thinkcspy/english2e/files.

The easiest way to get the source code on an Ubuntu 9.10 computer is:

1. run sudo apt-get install bzr on your system to install bzr.


2. run bzr branch lp:thinkcspy.

The last command above will download the book source from Launchpad
into a directory named thinkcspy which contains the Sphinx source and
configuration information needed to build the book.

Making the HTML Version

To generate the html version of the book:

1. run sudo apt-get install python-sphinx to install the Sphinx


documentation system.
2. cd thinkcspy - change into the thinkcspy directory containing the
book source.
3. make html.

The last command will run sphinx and create a directory named build
containing the html verion of the text.

Note: Sphinx supports building other output types as well, such as


PDF. This requires that LaTeX be present on your system. Since I
only personally use the html version, I will not attempt to
document that process here.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.
306

Copyright Notice
Copyright (C) Jeffrey Elkner, Allen B. Downey and Chris Meyers. Permission
is granted to copy, distribute and/or modify this document under the terms
of the GNU Free Documentation License, Version 1.3 or any later version
published by the Free Software Foundation; with Invariant Sections being
Forward, Preface, and Contributor List, no Front-Cover Texts, and no Back-
Cover Texts. A copy of the license is included in the section entitled “GNU
Free Documentation License”.

How to Think Like a Computer Scientist: Learning with Python v2nd Edition documentation
© Copyright 2009, Jeffrey Elkner, Allen B. Downey and Chris Meyers. Created using Sphinx 0.6.2.

You might also like