# Python Crash Course. A Hands-On, Project-Based Introduction to Programming (Matthes) 1 ed (2015)

### До загрузки: 30 сек.

Благодарим, что скачиваете у нас :)

Если, что - то:

• Поделится ссылкой:
• Документ найден в свободном доступе.
• Загрузка документа - бесплатна.
• Если нарушены ваши права, свяжитесь с нами.
 Формат: pdf Найдено: 13.06.2020 Добавлено: 30.09.2020 Размер: 5.64 Мб

A HANDS-ON , PROJECT-BASED
INTRODUCTION TO PROGRAMMING
ERIC M AT THES
PY THON
CR ASH COURSE
PY THON
CR ASH COURSE
SHELVE IN:
PROGRAMMING LANGUAGES/
PYTHON $39 .9 5 ($ 45 .9 5 C DN )
FAST!
LEARN PYTHON—
FAST!
LEARN PYTHON—
PY THON CR ASH COURSE
PY THON CR ASH COURSE
MATTHES
COVERS PYTHON 2 AND 3
Python Crash Course is a fast-paced, thorough intro-
duction to programming with Python that will have you
writing programs, solving problems, and making things
that work in no time.
In the first half of the book, you’ll learn about basic
programming concepts, such as lists, dictionaries,
classes, and loops, and practice writing clean and
readable code with exercises for each topic. You’ll
also learn how to make your programs interactive
a project. In the second half of the book, you’ll put
your new knowledge into practice with
three substantial
visualizations with Python’s super-handy libraries, and a
simple web app you can deploy online.
As you work through Python Crash Course, you’ll learn
how to:
• Use powerful Python libraries and tools, including
matplotlib, NumPy, and Pygal •
Make 2D games that respond to keypresses and
mouse clicks, and that grow more difficult as the
game progresses
• Work with data to generate interactive visualizations
• Create and customize simple web apps and deploy
them safely online
• Deal with mistakes and errors so you can solve your
own programming problems
If you’ve been thinking seriously about digging into
programming, Python Crash Course will get you up to
speed and have you writing real programs fast. Why
wait any longer? Start your engines and code!
Eric Matthes is a high school science and math teacher
living in Alaska, where he teaches an introductory
Python course. He has been writing programs since he
was five years old.
w ww.n o sta rc h .c o m
T
H E F INE ST IN GEEK E N TERTA IN M EN T

“I LIE FLAT.”
This book uses RepKove r— a durable binding that won’t snap shut

Python Crash Course

Python
Crash Course
a h ands- o n, Project-Based
Introduction to Programming
by Eric Matthes
San Francisco

ed in any form or by any means,
electronic or mechanical, including photocopying, recording, or by any i
nformation storage or retrieval
system, without the prior written permission of the copyright owner and
the publisher.
First printing
19 18 17 16 15 1 2 3 4 5 6 7 8 9
ISBN-10: 1-59327-603-6
ISBN-13: 978-1-59327-603-4
Publisher: William Pollock
Production Editor: Riley Hoffman
Cover Illustration: Josh Ellingson
Interior Design: Octopod Studios
Developmental Editors: William Pollock, Liz Chadwick, and Leslie Shen
Technical Reviewer: Kenneth Love
Copyeditor: Anne Marie Walker
Compositor: Riley Hoffman
For information on distribution, translations, or bulk sales, please con
tact No Starch Press, Inc. directly:
No Starch Press, Inc.
245 8th Street, San Francisco, CA 94103
phone: 415.863.9900; info@nostarch.com
www.nostarch.com
Matthes, Eric, 1972-
Python crash course : a hands-on, project-based introduction to progra
mming / by Eric Matthes.
pages cm
Includes index.
Summary: "A project-based introduction to programming in Python, with
exercises. Covers general
programming concepts, Python fundamentals, and problem solving. Includes
three projects - how to
create a simple video game, use data visualization techniques to make gr
aphs and charts, and build
an interactive web application"-- Provided by publisher.
ISBN 978-1-59327-603-4 -- ISBN 1-59327-603-6
1. Python (Computer program language) I. Title.
QA76.73.P98M38 2015
005.13'3--dc23
2015018135
No Starch Press and the No Starch Press logo are registered trademarks o
f No Starch Press, Inc. Other
product and company names mentioned herein may be the trademarks of thei
r respective owners. Rather
than use a trademark symbol with every occurrence of a trademarked name,
we are u sing the names only
in an editorial fashion and to the benefit of the trademark owner, with
no intention of infringement of the
The information in this book is distributed on an “As Is” basis, w
ithout warranty. While every precaution
has been taken in the preparation of this work, neither the author nor N
o Starch Press, Inc. shall have any
liability to any person or entity with respect to any loss or damage cau
sed or alleged to be caused directly or
indirectly by the information contained in it.

Eric Matthes is a high school science and math teacher living in Alaska,

where he teaches an introductory Python course. He has been writing
programs since he was five years old. Eric currently focuses on writing
soft-
ware that addresses inefficiencies in education and brings the benefits
of
open source software to the field of education. In his spare time he enj
oys
climbing mountains and spending time with his family.
Kenneth Love has been a Python programmer and teacher for many years.
He has given talks and tutorials at conferences, done professional train
-
ings, been a Python and Django freelancer, and now teaches for an online

education company. Kenneth is also the co-creator of the django-braces
package, which provides several handy mixins for Django’s class-based

views. You can keep up with him on Twitter at @kennethlove.

For my father, who always made time to
and for Ever, who is just beginning to ask me his questions

BrIef Contents
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxvii
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxix
Part I: BasICs..................................... 1
Chapter 1: Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Chapter 2: Variables and Simple Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Chapter 3: Introducing Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Chapter 4: Working with Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Chapter 5: if Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Chapter 6: Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Chapter 7: User Input and while Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Chapter 8: Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Chapter 9: Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Chapter 10: Files and Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Chapter 11: Testing Your Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Part II: ProjeCts ................................ 231
Project 1: Alien Invasion
Chapter 12: A Ship That Fires Bullets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Chapter 13: Aliens! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Chapter 14: Scoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

x Brief Contents
Project 2: Data Visualization
Chapter 15: Generating Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
Chapter 16: Downloading Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Chapter 17: Working with APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
Project 3: Web Applications
Chapter 18: Getting Started with Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
Chapter 19: User Accounts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
Chapter 20: Styling and Deploying an App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
Afterword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
Appendix A: Installing Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
Appendix B: Text Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Appendix C: Getting Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
Appendix D: Using Git for Version Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515

Contents In DetaIl
aCknowledgments xxvii
IntroduCtIon xxix
Who Is This Book For? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxx
What Can You Expect to Learn? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxx
Why Python? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxxi
Part I: BasICs 1
1
gettIng started 3
Setting Up Your Programming Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Python 2 and Python 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Running Snippets of Python Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Hello World! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Python on Different Operating Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Python on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Python on OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Python on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Troubleshooting Installation Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Running Python Programs from a Terminal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
On Linux and OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
On Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Exercise 1-1: python.org ...................................... 17
Exercise 1-2: Hello World Typos ................................. 17
Exercise 1-3: Infinite Skills ..................................... 17
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2
VarIaBles and sImPle data tyPes 19
What Really Happens When You Run hello_world .py . . . . . . . . . . . . . . . . . . . . . . . . .19
Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Naming and Using Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Avoiding Name Errors When Using Variables . . . . . . . . . . . . . . . . . . . . . . . 21
Exercise 2-1: Simple Message .................................. 23
Exercise 2-2: Simple Messages .................................. 23
Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Changing Case in a String with Methods . . . . . . . . . . . . . . . . . . . . . . . . . . .24
Combining or Concatenating Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Adding Whitespace to Strings with Tabs or Newlines . . . . . . . . . . . . . . . . . . 26
Stripping Whitespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Avoiding Syntax Errors with Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Printing in Python 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Exercise 2-3: Personal Message ................................. 29
Exercise 2-4: Name Cases ..................................... 29
Exercise 2-5: Famous Quote .................................... 29

xii Contents in Detail
Exercise 2-6: Famous Quote 2 ..................................29
Exercise 2-7: Stripping Names .................................. 29
Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Floats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Avoiding Type Errors with the str() Function . . . . . . . . . . . . . . . . . . . . . . . . . 31
Integers in Python 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Exercise 2-8: Number Eight .................................... 33
Exercise 2-9: Favorite Number .................................. 33
Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
How Do You Write Comments? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
What Kind of Comments Should You Write? . . . . . . . . . . . . . . . . . . . . . . . . 33
The Zen of Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Exercise 2-11: Zen of Python ................................... 36
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3
IntroduCIng lIsts 37
What Is a List? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Accessing Elements in a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Index Positions Start at 0, Not 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Using Individual Values from a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Exercise 3-1: Names ......................................... 40
Exercise 3-2: Greetings ....................................... 40
Exercise 3-3: Your Own List .................................... 40
Changing, Adding, and Removing Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Modifying Elements in a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Adding Elements to a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Removing Elements from a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Exercise 3-4: Guest List ....................................... 46
Exercise 3-5: Changing Guest List ................................ 46
Exercise 3-6: More Guests ..................................... 46
Exercise 3-7: Shrinking Guest List ................................ 47
Organizing a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Sorting a List Permanently with the sort() Method . . . . . . . . . . . . . . . . . . . . . .47
Sorting a List Temporarily with the sorted() Function . . . . . . . . . . . . . . . . . . . .48
Printing a List in Reverse Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Finding the Length of a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Exercise 3-8: Seeing the World .................................. 50
Exercise 3-9: Dinner Guests .................................... 50
Exercise 3-10: Every Function ................................... 50
Avoiding Index Errors When Working with Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . .50
Exercise 3-11: Intentional Error .................................. 52
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4
workIng wIth lIsts 53
Looping Through an Entire List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
A Closer Look at Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Doing More Work Within a for Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Doing Something After a for Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

Contents in Detail xiii
Avoiding Indentation Errors
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Forgetting to Indent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Forgetting to Indent Additional Lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Indenting Unnecessarily . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Indenting Unnecessarily After the Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Forgetting the Colon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Exercise 4-1: Pizzas ......................................... 60
Exercise 4-2: Animals ........................................ 60
Making Numerical Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Using the range() Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Using range() to Make a List of Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . .62
Simple Statistics with a List of Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
List Comprehensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Exercise 4-3: Counting to Twenty ................................ 64
Exercise 4-4: One Million ...................................... 64
Exercise 4-5: Summing a Million ................................. 64
Exercise 4-6: Odd Numbers .................................... 64
Exercise 4-7: Threes ......................................... 64
Exercise 4-8: Cubes .......................................... 64
Exercise 4-9: Cube Comprehension ............................... 64
Working with Part of a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Slicing a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Looping Through a Slice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Copying a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Exercise 4-10: Slices ......................................... 69
Exercise 4-11: My Pizzas, Your Pizzas ............................. 69
Exercise 4-12: More Loops ..................................... 69
Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Defining a Tuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Looping Through All Values in a Tuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Writing over a Tuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Exercise 4-13: Buffet ......................................... 71
Styling Your Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
The Style Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Indentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Line Length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Blank Lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Other Style Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Exercise 4-14: PEP 8 ......................................... 74
Exercise 4-15: Code Review .................................... 74
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5
If statements 75
A Simple Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Conditional Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Checking for Equality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Ignoring Case When Checking for Equality . . . . . . . . . . . . . . . . . . . . . . . . . 77
Checking for Inequality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Numerical Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Checking Multiple Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Checking Whether a Value Is in a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

xiv Contents in Detail
Checking Whether a Value Is Not in a List . . . . . . . . . . . . . . . . . . . . . . . . . .81
Boolean Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Exercise 5-1: Conditional Tests .................................. 82
Exercise 5-2: More Conditional Tests .............................. 82
if Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Simple if Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
if-else Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
The if-elif-else Chain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Using Multiple elif Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Omitting the else Block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Testing Multiple Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Exercise 5-3: Alien Colors #1 ................................... 88
Exercise 5-4: Alien Colors #2 ................................... 88
Exercise 5-5: Alien Colors #3 ................................... 89
Exercise 5-6: Stages of Life ..................................... 89
Exercise 5-7: Favorite Fruit ..................................... 89
Using if Statements with Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Checking for Special Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Checking That a List Is Not Empty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Using Multiple Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Exercise 5-8: Hello Admin ..................................... 93
Exercise 5-9: No Users ....................................... 93
Exercise 5-10: Checking Usernames .............................. 93
Exercise 5-11: Ordinal Numbers ................................. 93
Styling Your if Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Exercise 5-12: Styling if statements ............................... 94
Exercise 5-13: Your Ideas ...................................... 94
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6
dICtIonarIes 95
A Simple Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Working with Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Accessing Values in a Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Adding New Key-Value Pairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Starting with an Empty Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Modifying Values in a Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Removing Key-Value Pairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
A Dictionary of Similar Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Exercise 6-1: Person ........................................ 102
Exercise 6-2: Favorite Numbers ................................ 102
Exercise 6-3: Glossary ....................................... 102
Looping Through a Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Looping Through All Key-Value Pairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Looping Through All the Keys in a Dictionary . . . . . . . . . . . . . . . . . . . . . . . 104
Looping Through a Dictionary’s Keys in Order . . . . . . . . . . . . . . . . . . . . . . 106
Looping Through All Values in a Dictionary . . . . . . . . . . . . . . . . . . . . . . . . 107
Exercise 6-4: Glossary 2 ..................................... 108
Exercise 6-5: Rivers ......................................... 108
Exercise 6-6: Polling ........................................ 108
Nesting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
A List of Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
A List in a Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

Contents in Detail xv
A Dictionary in a Dictionary
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Exercise 6-7: People ......................................... 11 4
Exercise 6-8: Pets ........................................... 11 5
Exercise 6-9: Favorite Places ................................... 11 5
Exercise 6-10: Favorite Numbers ................................ 11 5
Exercise 6-11: Cities ......................................... 11 5
Exercise 6-12: Extensions ...................................... 11 5
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
7
user InPut and whIle looPs 117
How the input() Function Works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Writing Clear Prompts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Using int() to Accept Numerical Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
The Modulo Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Accepting Input in Python 2 .7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Exercise 7-1: Rental Car...................................... 121
Exercise 7-2: Restaurant Seating ................................ 121
Exercise 7-3: Multiples of Ten .................................. 121
Introducing while Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
The while Loop in Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Letting the User Choose When to Quit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Using a Flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Using break to Exit a Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Using continue in a Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Avoiding Infinite Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Exercise 7-4: Pizza Toppings .................................. 127
Exercise 7-5: Movie Tickets .................................... 127
Exercise 7-6: Three Exits ..................................... 128
Exercise 7-7: Infinity ........................................ 128
Using a while Loop with Lists and Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . .128
Moving Items from One List to Another . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Removing All Instances of Specific Values from a List . . . . . . . . . . . . . . . . . .129
Filling a Dictionary with User Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Exercise 7-8: Deli .......................................... 131
Exercise 7-9: No Pastrami .................................... 131
Exercise 7-10: Dream Vacation ................................. 131
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
8
funCtIons 133
Defining a Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .134
Passing Information to a Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Arguments and Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Exercise 8-1: Message ....................................... 135
Exercise 8-2: Favorite Book ................................... 135
Passing Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Positional Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Keyword Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Default Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

xvi Contents in Detail
Equivalent Function Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Avoiding Argument Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Exercise 8-3: T-Shirt ......................................... 141
Exercise 8-4: Large Shirts ..................................... 141
Exercise 8-5: Cities ......................................... 141
Return Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Returning a Simple Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Making an Argument Optional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Returning a Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Using a Function with a while Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Exercise 8-6: City Names ..................................... 146
Exercise 8-7: Album ......................................... 146
Exercise 8-8: User Albums .................................... 146
Passing a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Modifying a List in a Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Preventing a Function from Modifying a List . . . . . . . . . . . . . . . . . . . . . . . . 149
Exercise 8-9: Magicians ...................................... 150
Exercise 8-10: Great Magicians ................................ 150
Exercise 8-11: Unchanged Magicians ............................ 150
Passing an Arbitrary Number of Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Mixing Positional and Arbitrary Arguments . . . . . . . . . . . . . . . . . . . . . . . . 152
Using Arbitrary Keyword Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Exercise 8-12: Sandwiches .................................... 154
Exercise 8-13: User Profile .................................... 154
Exercise 8-14: Cars ......................................... 154
Storing Your Functions in Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
Importing an Entire Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
Importing Specific Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Using as to Give a Function an Alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Using as to Give a Module an Alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Importing All Functions in a Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Styling Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Exercise 8-15: Printing Models ................................. 159
Exercise 8-16: Imports ....................................... 159
Exercise 8-17: Styling Functions ................................ 159
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
9
Classes 161
Creating and Using a Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Creating the Dog Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Making an Instance from a Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Exercise 9-1: Restaurant ...................................... 166
Exercise 9-2: Three Restaurants ................................. 166
Exercise 9-3: Users ......................................... 166
Working with Classes and Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
The Car Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
Setting a Default Value for an Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Modifying Attribute Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Exercise 9-4: Number Served .................................. 171
Exercise 9-5: Login Attempts ................................... 171

Contents in Detail xvii
Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
172
The __init__() Method for a Child Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Inheritance in Python 2 .7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Defining Attributes and Methods for the Child Class . . . . . . . . . . . . . . . . . . 174
Overriding Methods from the Parent Class . . . . . . . . . . . . . . . . . . . . . . . . .175
Instances as Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Modeling Real-World Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
Exercise 9-6: Ice Cream Stand ................................. 178
Exercise 9-8: Privileges ...................................... 178
Exercise 9-9: Battery Upgrade ................................. 178
Importing Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Importing a Single Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Storing Multiple Classes in a Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Importing Multiple Classes from a Module . . . . . . . . . . . . . . . . . . . . . . . . . 181
Importing an Entire Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Importing All Classes from a Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Importing a Module into a Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
Finding Your Own Workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Exercise 9-10: Imported Restaurant .............................. 184
Exercise 9-11: Imported Admin ................................. 184
Exercise 9-12: Multiple Modules ................................ 184
The Python Standard Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Exercise 9-13: OrderedDict Rewrite .............................. 186
Exercise 9-14: Dice ......................................... 186
Exercise 9-15: Python Module of the Week ........................ 186
Styling Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
10
fIles and exCePtIons 189
Reading from a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Reading an Entire File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
File Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Reading Line by Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Making a List of Lines from a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Working with a File’s Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Large Files: One Million Digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Is Your Birthday Contained in Pi? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Exercise 10-1: Learning Python ................................. 197
Exercise 10-2: Learning C .................................... 197
Writing to a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Writing to an Empty File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Writing Multiple Lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
Appending to a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Exercise 10-3: Guest ........................................ 199
Exercise 10-4: Guest Book .................................... 199
Exercise 10-5: Programming Poll ................................ 199
Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Handling the ZeroDivisionError Exception . . . . . . . . . . . . . . . . . . . . . . . . . 200
Using try-except Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Using Exceptions to Prevent Crashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

xviii Contents in Detail
The else Block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
Handling the FileNotFoundError Exception . . . . . . . . . . . . . . . . . . . . . . . . .203
Analyzing Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Working with Multiple Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Failing Silently . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
Deciding Which Errors to Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Exercise 10-6: Addition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . 207
Exercise 10-7: Addition Calculator .............................. 208
Exercise 10-8: Cats and Dogs ................................. 208
Exercise 10-9: Silent Cats and Dogs ............................. 208
Exercise 10-10: Common Words ............................... 208
Storing Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Using json .dump() and json .load() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Saving and Reading User-Generated Data . . . . . . . . . . . . . . . . . . . . . . . . .210
Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Exercise 10-11: Favorite Number ............................... 214
Exercise 10-12: Favorite Number Remembered ..................... 214
Exercise 10-13: Verify User ................................... 214
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
11
Testing a Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Unit Tests and Test Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
A Passing Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
A Failing Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Responding to a Failed Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Adding New Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Exercise 11-1: City, Country ................................... 222
Exercise 11-2: Population ..................................... 222
Testing a Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
A Variety of Assert Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
A Class to Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Testing the AnonymousSurvey Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
The setUp() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Exercise 11-3: Employee ..................................... 228
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Part II: ProjeCts 231
ProjeCt 1: alIen InVasIon
12
a shIP that fIres Bullets 235
Planning Your Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Installing Pygame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Installing Python Packages with pip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Installing Pygame on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238

Contents in Detail xix
Installing Pygame on OS X
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Installing Pygame on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Starting the Game Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Creating a Pygame Window and Responding to User Input . . . . . . . . . . . . .241
Setting the Background Color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Creating a Settings Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Adding the Ship Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Creating the Ship Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Drawing the Ship to the Screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Refactoring: the game_functions Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
The check_events() Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
The update_screen() Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Exercise 12-1: Blue Sky ...................................... 249
Exercise 12-2: Game Character ................................ 249
Piloting the Ship . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Responding to a Keypress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Allowing Continuous Movement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
Moving Both Left and Right . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Adjusting the Ship’s Speed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Limiting the Ship’s Range . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Refactoring check_events() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
A Quick Recap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
alien_invasion .py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
settings .py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
game_functions .py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
ship .py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Exercise 12-3: Rocket ....................................... 257
Exercise 12-4: Keys ......................................... 257
Shooting Bullets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Adding the Bullet Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Creating the Bullet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
Storing Bullets in a Group . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Firing Bullets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Deleting Old Bullets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Limiting the Number of Bullets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Creating the update_bullets() Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
Creating the fire_bullet() Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
Exercise 12-5: Sideways Shooter ............................... 264
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
13
alIens! 265
Reviewing Your Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
Creating the First Alien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
Creating the Alien Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Creating an Instance of the Alien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Making the Alien Appear Onscreen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Building the Alien Fleet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Determining How Many Aliens Fit in a Row . . . . . . . . . . . . . . . . . . . . . . . . 269
Creating Rows of Aliens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Creating the Fleet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271

xx Contents in Detail
Refactoring create_fleet() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .273
Adding Rows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Exercise 13-1: Stars ......................................... 276
Exercise 13-2: Better Stars .................................... 276
Making the Fleet Move . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Moving the Aliens Right . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Creating Settings for Fleet Direction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Checking to See Whether an Alien Has Hit the Edge . . . . . . . . . . . . . . . . . .278
Dropping the Fleet and Changing Direction . . . . . . . . . . . . . . . . . . . . . . . . 278
Exercise 13-3: Raindrops ..................................... 279
Exercise 13-4: Steady Rain .................................... 279
Shooting Aliens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Detecting Bullet Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Making Larger Bullets for Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Repopulating the Fleet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
Speeding Up the Bullets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Refactoring update_bullets() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Exercise 13-5: Catch ........................................ 284
Ending the Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Detecting Alien-Ship Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Responding to Alien-Ship Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
Aliens that Reach the Bottom of the Screen . . . . . . . . . . . . . . . . . . . . . . . . .288
Game Over! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Identifying When Parts of the Game Should Run . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Exercise 13-6: Game Over .................................... 290
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
14
sCorIng 291
Adding the Play Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Creating a Button Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Drawing the Button to the Screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Starting the Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
Resetting the Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Deactivating the Play Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
Hiding the Mouse Cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
Exercise 14-1: Press P to Play .................................. 298
Exercise 14-2: Target Practice .................................. 298
Leveling Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Modifying the Speed Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Resetting the Speed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Exercise 14-3: Challenging Target Practice .........................301
Scoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Displaying the Score . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Making a Scoreboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
Updating the Score as Aliens Are Shot Down . . . . . . . . . . . . . . . . . . . . . . .304
Making Sure to Score All Hits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
Increasing Point Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Rounding the Score . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
High Scores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308

Contents in Detail xxi
Displaying the Level
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
Displaying the Number of Ships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
Exercise 14-4: All-Time High Score .............................. 317
Exercise 14-5: Refactoring .................................... 317
Exercise 14-6: Expanding Alien Invasion .......................... 317
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
ProjeCt 2: data VIsualIzatIon
15
generatIng data 321
Installing matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
On Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
On OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
On Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Testing matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
The matplotlib Gallery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Plotting a Simple Line Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
Changing the Label Type and Graph Thickness . . . . . . . . . . . . . . . . . . . . . 324
Correcting the Plot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Plotting and Styling Individual Points with scatter() . . . . . . . . . . . . . . . . . . . .326
Plotting a Series of Points with scatter() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
Calculating Data Automatically . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
Removing Outlines from Data Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
Defining Custom Colors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Using a Colormap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Saving Your Plots Automatically . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Exercise 15-1: Cubes ........................................ 331
Exercise 15-2: Colored Cubes ................................. 331
Random Walks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Creating the RandomWalk() Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
Choosing Directions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
Plotting the Random Walk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
Generating Multiple Random Walks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Styling the Walk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
Coloring the Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
Plotting the Starting and Ending Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
Cleaning Up the Axes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Adding Plot Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Altering the Size to Fill the Screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Exercise 15-3: Molecular Motion ............................... 339
Exercise 15-4: Modified Random Walks .......................... 339
Exercise 15-5: Refactoring .................................... 339
Rolling Dice with Pygal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
Installing Pygal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
The Pygal Gallery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Creating the Die Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Rolling the Die . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
Analyzing the Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341

xxii Contents in Detail
Making a Histogram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
Rolling Two Dice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Rolling Dice of Different Sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
Exercise 15-6: Automatic Labels ................................ 346
Exercise 15-7: Two D8s ...................................... 346
Exercise 15-8: Three Dice .................................... 346
Exercise 15-9: Multiplication .................................. 346
Exercise 15-10: Practicing with Both Libraries .......................346
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
16
The CSV File Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
Parsing the CSV File Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
Printing the Headers and Their Positions . . . . . . . . . . . . . . . . . . . . . . . . . . .351
Extracting and Reading Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
Plotting Data in a Temperature Chart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
The datetime Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Plotting Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Plotting a Longer Timeframe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
Plotting a Second Data Series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
Shading an Area in the Chart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
Error-Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
Exercise 16-1: San Francisco .................................. 362
Exercise 16-2: Sitka-Death Valley Comparison ...................... 362
Exercise 16-3: Rainfall ....................................... 362
Exercise 16-4: Explore ....................................... 362
Mapping Global Data Sets: JSON Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Downloading World Population Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Extracting Relevant Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Converting Strings into Numerical Values . . . . . . . . . . . . . . . . . . . . . . . . . .364
Obtaining Two-Digit Country Codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
Building a World Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
Plotting Numerical Data on a World Map . . . . . . . . . . . . . . . . . . . . . . . . . 368
Plotting a Complete Population Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
Grouping Countries by Population . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Styling World Maps in Pygal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Lightening the Color Theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
Exercise 16-5: All Countries ................................... 375
Exercise 16-6: Gross Domestic Product ........................... 375
Exercise 16-7: Choose Your Own Data ........................... 375
Exercise 16-8: Testing the country_codes Module .................... 375
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
17
workIng wIth aPIs 377
Using a Web API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Git and GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Requesting Data Using an API Call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Installing Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379

Contents in Detail xxiii
Processing an API Response
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
Working with the Response Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
Summarizing the Top Repositories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
Monitoring API Rate Limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
Visualizing Repositories Using Pygal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
Refining Pygal Charts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
Adding Custom Tooltips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Plotting the Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
Adding Clickable Links to Our Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
The Hacker News API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Exercise 17-1: Other Languages ................................ 393
Exercise 17-2: Active Discussions ............................... 393
Exercise 17-3: Testing python_repos.py ........................... 393
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
ProjeCt 3: weB aPPlICatIons
18
gettIng started wIth django 397
Setting Up a Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Writing a Spec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Creating a Virtual Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Installing virtualenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
Activating the Virtual Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
Installing Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Creating a Project in Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Creating the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
Viewing the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
Exercise 18-1: New Projects ................................... 402
Starting an App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
Defining Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
Activating Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
The Django Admin Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
Defining the Entry Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
Migrating the Entry Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
Registering Entry with the Admin Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
The Django Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
Exercise 18-2: Short Entries ................................... 412
Exercise 18-3: The Django API ................................. 412
Exercise 18-4: Pizzeria ...................................... 412
Making Pages: The Learning Log Home Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
Mapping a URL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Writing a View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
Writing a Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
Exercise 18-5: Meal Planner ................................... 416
Building Additional Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
Template Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
The Topics Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Individual Topic Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421

xxiv Contents in Detail
Exercise 18-7: Template Documentation...........................424
Exercise 18-8: Pizzeria Pages .................................. 424
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
19
user aCCounts 427
Allowing Users to Enter Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
Adding New Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
Adding New Entries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Editing Entries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
Exercise 19-1: Blog ......................................... 438
Setting Up User Accounts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
The users App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
The Login Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
Logging Out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
The Registration Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
Exercise 19-2: Blog Accounts .................................. 446
Allowing Users to Own Their Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
Restricting Access with @login_required . . . . . . . . . . . . . . . . . . . . . . . . . . .447
Connecting Data to Certain Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
Restricting Topics Access to Appropriate Users . . . . . . . . . . . . . . . . . . . . . .451
Protecting a User’s Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
Protecting the edit_entry Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
Associating New Topics with the Current User . . . . . . . . . . . . . . . . . . . . . . 453
Exercise 19-3: Refactoring .................................... 454
Exercise 19-4: Protecting new_entry ............................. 454
Exercise 19-5: Protected Blog .................................. 454
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
20
stylIng and dePloyIng an aPP 455
Styling Learning Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
The django-bootstrap3 App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
Using Bootstrap to Style Learning Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
Modifying base .html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
Styling the Home Page Using a Jumbotron . . . . . . . . . . . . . . . . . . . . . . . . .461
Styling the Login Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
Styling the new_topic Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
Styling the Topics Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
Styling the Entries on the Topic Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
Exercise 20-1: Other Forms ................................... 466
Exercise 20-2: Stylish Blog .................................... 466
Deploying Learning Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
Making a Heroku Account . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
Installing the Heroku Toolbelt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
Installing Required Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
Creating a Packages List with a requirements .txt File . . . . . . . . . . . . . . . . . .467
Specifying the Python Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
Modifying settings .py for Heroku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
Making a Procfile to Start Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
Modifying wsgi .py for Heroku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470

Contents in Detail xxv
Making a Directory for Static Files
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
Using the gunicorn Server Locally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
Using Git to Track the Project’s Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
Pushing to Heroku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
Setting Up the Database on Heroku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
Refining the Heroku Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
Securing the Live Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
Committing and Pushing Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
Creating Custom Error Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Ongoing Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
The SECRET_KEY Setting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
Deleting a Project on Heroku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
Exercise 20-3: Live Blog ...................................... 482
Exercise 20-4: More 404s .................................... 482
Exercise 20-5: Extended Learning Log ............................ 482
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
afterword 483
a
InstallIng Python 485
Python on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
Finding the Installed Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
Installing Python 3 on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
Python on OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
Finding the Installed Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
Using Homebrew to Install Python 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
Python on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
Installing Python 3 on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
Finding the Python Interpreter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
Adding Python to Your Path Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
Python Keywords and Built-in Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
Python Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
Python Built-in Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
B
text edItors 491
Geany . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
Installing Geany on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
Installing Geany on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
Running Python Programs in Geany . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
Customizing Geany Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
Sublime Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
Installing Sublime Text on OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
Installing Sublime Text on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
Installing Sublime Text on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
Running Python Programs in Sublime Text . . . . . . . . . . . . . . . . . . . . . . . . . .495
Configuring Sublime Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
Customizing Sublime Text Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496

xxvi Contents in Detail
IDLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .496
Installing IDLE on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
Installing IDLE on OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
Installing IDLE on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
Customizing IDLE Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
Emacs and vim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
C
gettIng helP 499
First Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
Try It Again . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
Take a Break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
Refer to This Book’s Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
Searching Online . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
Stack Overflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
The Official Python Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
Official Library Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
r/learnpython . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
Blog Posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
IRC (Internet Relay Chat) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
Make an IRC Account . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
Channels to Join . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
IRC Culture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
d
usIng gIt for VersIon Control 505
Installing Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Installing Git on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Installing Git on OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Installing Git on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Configuring Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Making a Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
Ignoring Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
Initializing a Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
Checking the Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
Adding Files to the Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
Making a Commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Checking the Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
The Second Commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510
Reverting a Change . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
Checking Out Previous Commits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
Deleting the Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
Index 515

aCknowle Dgments
This book would not have been possible without the wonderful and
extremely professional staff at No Starch Press. Bill Pollock invited me to
write an introductor y book, and I deeply appreciate that original offer.
Tyler Ortman helped shape my thinking in the early stages of drafting.
Liz Chadwick’s and Leslie Shen’s initial feedback on each chapter was
invaluable, and Anne Marie Walker helped to clarify many parts of the
assembling a complete book and patiently turned my work into a beautiful
finished product.
I’d like to thank Kenneth Love, the technical reviewer for Python Crash
Course . I met Kenneth at PyCon one year, and his enthusiasm for the lan -
guage and the Python community has been a constant source of profes -
sional inspiration ever since. Kenneth went beyond simple fact-checking
and reviewed the book with the goal of helping beginning programmers
develop a solid understanding of the Python language and programming
in general. That said, any inaccuracies that remain are completely my own. I’d like to thank my father for introducing me to programming at a
young age and for not being afraid that I’d break his equipment. I’d like
to thank my wife, Erin, for supporting and encouraging me through the
writing of this book, and I’d like to thank my son, Ever, whose curiosity
inspires me ever y single day.

IntroDuC tI on
Every programmer has a story about
how they learned to write their first pro -
gram. I started learning as a child when
my father was working for Digital Equipment
Corporation, one of the pioneering companies of the
modern computing era. I wrote my first program on a
kit computer my dad had assembled in our basement. The computer con -
sisted of nothing more than a bare motherboard connected to a keyboard
without a case, and it had a bare cathode ray tube for a monitor. My initial
program was a simple number guessing game, which looked something
like this:
I'm thinking of a number! Try to guess the number I'm thinking of: 25
Too low! Guess again: 50
Too high! Guess again: 42
That's it! Would you like to play again? (yes/no) no
Thanks for playing!

xxx Introduction
I’ll always remember how satisfied I felt watching my family play a game
that I created and that worked as I intended it to. That early experience had a lasting impact. There is real satisfaction
in building something with a purpose, something that solves a problem.
The software I write now meets a more significant need than my childhood
efforts, but the sense of satisfaction I get from creating a program that works
is still largely the same.
who Is t his Book f or?
The goal of this book is to bring you up to speed with Python as quickly as
possible so you can build programs that work—games, data visualizations,
and web applications—while developing a foundation in programming that
will ser ve you well for the rest of your life. Python Crash Course is written for
people of any age who have never programmed in Python before or have
never programmed at all. If you want to learn the basics of programming
quickly so you can focus on interesting projects, and you like to test your
understanding of new concepts by solving meaningful problems, this book
is for you. Python Crash Course is also perfect for middle school and high
school teachers who want to offer their students a project-based introduc -
tion to programming.
what Can y ou expect to l earn?
The purpose of this book is to make you a good programmer in general
and a good Python programmer in particular. You’ll learn efficiently and
adopt good habits as I provide you with a solid foundation in general pro -
gramming concepts. A fter working your way through Python Crash Course ,
you should be ready to move on to more advanced Python techniques, and
your next programming language will be even easier to grasp. In the first part of this book you’ll learn basic programming concepts
you need to know to write Python programs. These concepts are the same
as those you’d learn when starting out in almost any programming lan -
guage. You’ll learn about different kinds of data and the ways you can store
data in lists and dictionaries within your programs. You’ll learn to build
collections of data and work through those collections in efficient ways.
You’ll learn to use
while and if loops to test for certain conditions so you
can run specific sections of code while those conditions are true and run
other sections when they’re not—a technique that greatly helps to automate
processes. You’ll learn to accept input from users to make your programs inter -
active and to keep your programs running as long as the user is active.
You’ll explore how to write functions to make parts of your program
reusable, so you only have to write blocks of code that perform certain

Introduction xxxi
actions once, which you can then use as many times as you like. You’ll then
extend this concept to more complicated behavior with classes, making fairly
simple programs respond to a variety of situations. You’ll learn to write pro-
grams that handle common errors gracefully. A fter working through each
of these basic concepts, you’ll write a few short programs that solve some
well-defined problems. Finally, you’ll take your first step toward intermedi -
ate programming by learning how to write tests for your code so you can
All the information in Part I will prepare you for taking on larger, more
complex projects. In Part II you’ll apply what you learned in Part I to three projects. You
can do any or all of these projects in whichever order works best for you. In
the first project (Chapters 12–14) you’ll create a Space Invaders–style shoot -
ing game called A lien Invasion, which consists of levels of increasing diffi -
culty. A fter you’ve completed this project, you should be well on your way to
being able to develop your own 2D games. The second project (Chapters 15 –17) introduces you to data visualiza -
tion. Data scientists aim to make sense of the vast amount of information
available to them through a variety of visualization techniques. You’ll
work with data sets that you generate through code, data sets downloaded
A fter you’ve completed this project, you’ll be able to write programs that
sift through large data sets and make visual representations of that stored
information. In the third project (Chapters 18 –20) you’ll build a small web applica -
tion called Learning Log. This project allows you to keep a journal of ideas
and concepts you’ve learned about a specific topic. You’ll be able to keep
separate logs for different topics and allow others to create an account and
start their own journals. You’ll also learn how to deploy your project so any -
one can access it online from any where.
why Python?
Ever y year I consider whether to continue using Python or whether to move
on to a different language—perhaps one that’s newer to the programming
world. But I continue to focus on Python for many reasons. Python is an
incredibly efficient language: your programs will do more in fewer lines of
code than many other languages would require. Python’s syntax will also
and easy to extend and build upon compared to other languages. People use Python for many purposes: to make games, build web appli -
cations, solve business problems, and develop internal tools at all kinds of
interesting companies. Python is also used heavily in scientific fields for

xxxii Introduction
One of the most important reasons I continue to use Python is
because of the Python community, which includes an incredibly diverse
and welcoming group of people. Community is essential to program -
mers because programming isn’t a solitar y pursuit. Most of us, even the
already solved similar problems. Having a well-connected and supportive
community is critical in helping you solve problems, and the Python com -
munity is fully supportive of people like you who are learning Python as
your first programming language. Python is a great language to learn, so let’s get started!

Part I
BasICs
Part I of this book teaches you the basic concepts
you’ll need to write Python programs. Many of
these concepts are common to all programming
languages, so they’ll be useful throughout your life
as a programmer.
In Chapter 1 you’ll install Python on your computer and run your first
program, which prints the message Hello world! to the screen.
In Chapter 2 you’ll learn to store information in variables and work
with text and numerical values. Chapters 3 and 4 introduce lists. Lists can store as much information
as you want in one variable, allowing you to work with that data efficiently.
You’ll be able to work with hundreds, thousands, and even millions of values
in just a few lines of code. In Chapter 5 you’ll use
if statements to write code that responds one
way if certain conditions are true, and responds in a different way if those
conditions are not true. Chapter 6 shows you how to use Python’s dictionaries, which let you
make connections between different pieces of information. Like lists, dic -
tionaries can contain as much information as you need to store. In Chapter 7 you’ll learn how to accept input from users to make your
programs interactive. You’ll also learn about
while loops, which run blocks
of code repeatedly as long as certain conditions remain true. In Chapter 8 you’ll write functions, which are named blocks of code
that perform a specific task and can be run whenever you need them.

2 Par t I
Chapter 9 introduces classes, which allow you to model real-world
objects, such as dogs, cats, people, cars, rockets, and much more, so your
code can represent anything real or abstract. Chapter 10 shows you how to work with files and handle errors so your
programs won’t crash unexpectedly. You’ll store data before your program
closes, and read the data back in when the program runs again. You’ll learn
about Python’s exceptions, which allow you to anticipate errors, and make
your programs handle those errors gracefully. In Chapter 11 you’ll learn to write tests for your code to check that
your programs work the way you intend them to. As a result, you’ll be able
beginner to intermediate programmer.

1
get t Ing s tarte D
In this chapter you’ll run your first Python
program, hello_world.py . First, you’ll need
to check whether Python is installed on
your computer; if it isn’t, you’ll install it. You’ll
also install a text editor to work with your Python
programs. Text editors recognize Python code and
highlight sections as you write, making it easy to
understand the structure of your code.
s etting u p your Programming e nvironment
Python differs slightly on different operating systems, so you’ll need to keep
a few considerations in mind. Here, we’ll look at the two major versions
of Python currently in use and outline the steps to set up Python on your
system.

4 Chapter 1
Python 2 and Python 3
Today, two versions of Python are available: Python 2 and the newer
Python 3. Ever y programming language evolves as new ideas and tech-
nologies emerge, and the developers of Python have continually made the
language more versatile and powerful. Most changes are incremental and
hardly noticeable, but in some cases code written for Python 2 may not
run properly on systems with Python 3 installed. Throughout this book I’ll
point out areas of significant difference between Python 2 and Python 3, so
whichever version you use, you’ll be able to follow the instructions. If both versions are installed on your system or if you need to install
Python, use Python 3. If Python 2 is the only version on your system and
you’d rather jump into writing code instead of installing Python, you can
Running Snippets of Python Code
Python comes with an interpreter that runs in a terminal window, allow -
ing you to tr y bits of Python without having to save and run an entire
program. Throughout this book, you’ll see snippets that look like this:
u >>> print("Hello Python interpreter!")
Hello Python interpreter!
The text in bold is what you’ll type in and then execute by pressing
enter . Most of the examples in the book are small, self-contained programs
that you’ll run from your editor, because that’s how you’ll write most of your
code. But sometimes basic concepts will be shown in a series of snippets run
through a Python terminal session to demonstrate isolated concepts more
efficiently. Any time you see the three angle brackets in a code listing u ,
you’re looking at the output of a terminal session. We’ll tr y coding in the
interpreter for your system in a moment.
Hello World!
A long-held belief in the programming world has been that printing a
Hello world! message to the screen as your first program in a new language
will bring you luck. In Python, you can write the Hello World program in one line:
print("Hello world!")
Such a simple program ser ves a ver y real purpose. If it runs correctly
on your system, any Python program you write should work as well. We’ll
look at writing this program on your particular system in just a moment.

Getting Started 5
Python on different operating s ystems
Python is a cross-platform programming language, which means it runs on
all the major operating systems. Any Python program you write should run
on any modern computer that has Python installed. However, the methods
for setting up Python on different operating systems var y slightly.
In this section you’ll learn how to set up Python and run the Hello World
program on your own system. You’ll first check whether Python is installed
on your system and install it if it’s not. Then you’ll install a simple text edi -
tor and save an empt y P ython file called hello_world.py. Finally, you’ll run
the Hello World program and troubleshoot anything that didn’t work. I’ll
walk you through this process for each operating system, so you’ll have a
beginner-friendly P ython programming environment.
Python on Linux
Linux systems are designed for programming, so Python is already installed
on most Linux computers. The people who write and maintain Linux expect
you to do your own programming at some point and encourage you to do
so. For this reason there’s ver y little you have to install and ver y few settings
you have to change to start programming.
Open a terminal window by running the Terminal application on your
system (in Ubuntu, you can press
ctr l -a lt -T). To find out whether Python
is installed, enter
python with a lowercase p . You should see output telling
you which version of Python is installed and a
>>> prompt where you can
start entering Python commands, like this:
$python Python 2.7.6 (default, Mar 22 2014, 22:59:38) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> This output tells you that Python 2.7.6 is currently the default version of Python installed on this computer. When you’ve seen this output, press ctr l -D or enter exit() to leave the Python prompt and return to a terminal prompt. To check for Python 3, you might have to specify that version; so even if the output displayed Python 2.7 as the default version, tr y the command python3 :$ python3
Python 3.5.0 (default, Sep 17 2015, 13:05:18)
[GCC 4.8.4] on linux
>>>

6 Chapter 1
This output means you also have Python 3 installed, so you’ll be
able to use either version. Whenever you see the
python command in this
book, enter
installed, but if for some reason yours didn’t or if your system came with
Python 2 and you want to install Python 3, refer to Appendix A.
Installing a t ext Editor
Geany is a simple text editor: it’s easy to install, will let you run almost all
your programs directly from the editor instead of through a terminal, uses
syntax highlighting to color your code, and runs your code in a terminal
window so you’ll get used to using terminals. Appendix B provides informa -
tion on other text editors, but I recommend using Geany unless you have a
good reason to use a different editor. You can install Geany in one line on most Linux systems:
$sudo apt-get install geany If this doesn’t work, see the instructions at http://geany.org/Download/ ThirdPartyPackages/ . r unning the Hello World Program To start your first program, open Geany . Press the Super key (often called the Windows key) and search for Geany on your system. Make a shortcut by dragging the icon to your taskbar or desktop. Then make a folder some - where on your system for your projects and call it python_work . (It’s best to use lowercase letters and underscores for spaces in file and folder names because these are Python naming conventions.) Go back to Geany and save an empty Python file ( File4Save A s ) called hello_world.py in your python_ work folder. The extension .py tells Geany your file will contain a Python program. It also tells Geany how to run your program and highlight the text in a helpful way. A fter you’ve saved your file, enter the following line: print("Hello Python world!") If multiple versions of Python are installed on your system, you need to make sure Geany is configured to use the correct version. Go to Build 4Set Build Commands . You should see the words Compile and Execute with a com- mand next to each. Geany assumes the correct command for each is python , but if your system uses the python3 command, you’ll need to change this. If the command python3 worked in a terminal session, change the Compile and Execute commands so Geany will use the Python 3 inter - preter. Your Compile command should look like this: python3 -m py_compile "%f" Getting Started 7 You need to type this command exactly as it’s shown. Make sure the spaces and capitalization match what is shown here. Your Execute command should look like this: python3 "%f" Again, make sure the spacing and capitalization match what is shown here. Figure 1-1 shows how these commands should look in Geany’s con - figuration menu. Figure 1-1: Here, Geany is configured to use Python 3 on Linux. Now run hello_world.py by selecting Build4Execute in the menu, by clicking the Execute icon (which shows a set of gears), or by pressing F5. A terminal window should pop up with the following output: Hello Python world! ------------------ (program exited with code: 0) Press return to continue If you don’t see this, check ever y character on the line you entered. Did you accidentally capitalize print ? Did you forget one or both of the quota - tion marks or parentheses? Programming languages expect ver y specific syntax, and if you don’t provide that, you’ll get errors. If you can’t get the program to run, see “Troubleshooting Installation Issues” on page 15. 8 Chapter 1 running Python in a t erminal Session You can tr y running snippets of Python code by opening a terminal and typing python or python3 , as you did when checking your version. Do this again, but this time enter the following line in the terminal session: >>> print("Hello Python interpreter!") Hello Python interpreter! >>> You should see your message printed directly in the current terminal window. Remember that you can close the Python interpreter by pressing ctr l -D or by typing the command exit() . Python on OS X Python is already installed on most OS X systems. Once you know Python is installed, you’ll need to install a text editor and make sure it’s configured cor rect l y. Checking Whether Python Is Installed Open a terminal window by going to Applications 4Utilities 4Te r m i n a l . You can also press comm and -spacebar, type terminal , and then press enter . To find out whether Python is installed, enter python with a lowercase p. You should see output telling you which version of Python is installed on your system and a >>> prompt where you can start entering Python commands, like this:$ python
Python 2.7.5 (default, Mar 9 2014, 22:15:05)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
>>>
This output tells you that Python 2.7.5 is currently the default version
installed on this computer. When you’ve seen this output, press
ctr l -D
or enter
prompt. To check for Python 3, tr y the command
python3 . You might get an error
message, but if the output shows you have Python 3 installed, you’ll be able
to use Python 3 without having to install it. If
whenever you see the
python command in this book, make sure you use python3
instead. If for some reason your system didn’t come with Python or if you
only have Python 2 and you want to install Python 3 now, see Appendix A.

Getting Started 9
running Python in a t erminal Session
You can tr y running snippets of Python code by opening a terminal and
typing
python or python3 , as you did when checking your version. Do this
again, but this time enter the following line in the terminal session:
>>> print("Hello Python interpreter!")
Hello Python interpreter!
>>>
You should see your message printed directly in the current terminal
window. Remember that you can close the Python interpreter by pressing
ctr l -D or by typing the command exit() .
Installing a t ext Editor
Sublime Text is a simple text editor: it’s easy to install on OS X, will let you
run almost all of your programs directly from the editor instead of through
a terminal, uses syntax highlighting to color your code, and runs your code
in a terminal session embedded in the Sublime Text window to make it easy
to see the output. Appendix B provides information on other text editors,
but I recommend using Sublime Text unless you have a good reason to use
a different editor. You can download an installer for Sublime Text from http://sublimetext
Text has a ver y liberal licensing policy: you can use the editor for free as
long as you want, but the author requests that you purchase a license if you
like it and want continual use. A fter the installer has been downloaded,
open it and then drag the Sublime Text icon into your Applications folder.
Configuring Sublime t ext for Python 3
If you use a command other than
python to start a Python terminal session,
you’ll need to configure Sublime Text so it knows where to find the correct
version of Python on your system. Issue the following command to find out
the full path to your Python interpreter:
$type -a python3 python3 is /usr/local/bin/python3 Now open Sublime Text, and go to To o l s 4Build System 4New Build System , which will open a new configuration file for you. Delete what you see and enter the following: Python3 { .sublime-build "cmd": ["/usr/local/bin/python3", "-u", "$file"],
}

10 Chapter 1
This code tells Sublime Text to use your system’s python3 command
when running the currently open file. Make sure you use the path you
found when issuing the command
type -a python3 in the previous step. Save
the file as Python3.sublime-build in the default director y that Sublime Text
opens when you choose Save.
r unning the Hello World Program
To start your first program, launch Sublime Text by opening the
Applications folder and double-clicking the Sublime Text icon. You can
also press
comm a nd -spacebar and enter sublime text in the search bar
that pops up. Make a folder called python_work somewhere on your system for your
projects. (It’s best to use lowercase letters and underscores for spaces in file
and folder names, because these are Python naming conventions.) Save an
empty Python file ( File4Save A s ) called hello_world.py in your python_work
folder. The extension .py tells Sublime Text that your file will contain a
Python program and tells it how to run your program and highlight the
text in a helpful way. A fter you’ve saved your file, enter the following line:
print("Hello Python world!")
If the command python works on your system, you can run your pro -
gram by selecting To o l s 4Build in the menu or by pressing
ctr l -B. If
you configured Sublime Text to use a command other than
python , select
To o l s 4Build System and then select P ython 3 . This sets Python 3 as the
default version of Python, and you’ll be able to select To o l s 4Build or just
press
comm and -B to run your programs from now on.
A terminal screen should appear at the bottom of the Sublime Text win -
dow, showing the following output:
Hello Python world!
[Finished in 0.1s]
If you don’t see this, check ever y character on the line you entered. Did
you accidentally capitalize
print ? Did you forget one or both of the quota -
tion marks or parentheses? Programming languages expect ver y specific
syntax, and if you don’t provide that, you’ll get errors. If you can’t get the
program to run, see “Troubleshooting Installation Issues” on page 15.
Python on Windows
Windows doesn’t always come with Python, so you’ll probably need to down -

Getting Started 11
Installing Python
First, check whether Python is installed on your system. Open a command
window by entering
command into the Start menu or by holding down the
shift key while right-clicking on your desktop and selecting Open com-
mand w indow here . In the terminal window, enter
python in lowercase. If
you get a Python prompt (
>>> ), Python is installed on your system. However,
you’ll probably see an error message telling you that
python is not a recog -
nized command. In that case, download a Python installer for Windows. Go to h t t p ://
system. A fter you’ve downloaded the file, run the installer. Make sure you
check the option Add Python to PATH, which will make it easier to config -
ure your system correctly. Figure 1-2 shows this option checked.
Figure 1-2: Make sure you check the box labeled Add Python to PATH.
Starting a Python t erminal Session
Setting up your text editor will be straightfor ward if you first set up your
system to run Python in a terminal session. Open a command window and
enter
python in lowercase. If you get a Python prompt ( >>> ), Windows has
found the version of Python you just installed:
C:\> python
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 22:15:05) [MSC v.1900
32 bit
(Intel)] on win32
>>>

12 Chapter 1
If this worked, you can move on to the next section, “Running Python
in a Terminal Session.” However, you may see output that looks more like this:
C:\> python
'python' is not recognized as an internal or external command, operable
program or batch file.
In this case you need to tell Windows how to find the Python version
python command is usually saved in your C
drive, so open Windows Explorer and open your C drive. Look for a folder
starting with the name Python , open that folder, and find the python file (in
lowercase). For example, I have a P ython35 folder with a file named python
inside it, so the path to the
python command on my system is C :\ P ython35\
python . Other w ise, enter
python into the search box in Windows Explorer to
show you exactly where the
python command is stored on your system.
When you think you know the path, test it by entering that path into a
terminal window. Open a command window and enter the full path you just
found:
C:\> C:\Python35\python
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 22:15:05) [MSC v.1900
32 bit
(Intel)] on win32
>>>
If this worked, you know how to access Python on your system.
r unning Python in a t erminal Session
Enter the following line in your Python session, and make sure you see the
output Hello Python world!
>>> print("Hello Python world!")
Hello Python world!
>>>
Any time you want to run a snippet of Python code, open a command
window and start a Python terminal session. To close the terminal session,
press
ctr l -Z and then press enter , or enter the command exit() .
Installing a t ext Editor
Geany is a simple text editor: it’s easy to install, will let you run almost all of
your programs directly from the editor instead of through a terminal, uses
syntax highlighting to color your code, and runs your code in a terminal
window so you’ll get used to using terminals. Appendix B provides informa -
tion on other text editors, but I recommend using Geany unless you have a
good reason to use a different editor.

Getting Started 13
setup.exe installer or something similar. Run the installer and accept all the
default s. To start your first program, open Geany: press the Windows key and
search for Geany on your system. You should make a shortcut by dragging
the icon to your taskbar or desktop. Make a folder called python_work some-
where on your system for your projects. (It’s best to use lowercase letters and
underscores for spaces in file and folder names, because these are Python
naming conventions.) Go back to Geany and save an empty Python file
( File 4Save A s ) called hello_world.py in your python_work folder. The exten-
sion .py tells Geany that your file will contain a Python program. It also tells
Geany how to run your program and to highlight the text in a helpful way. A fter you’ve saved your file, type the following line:
print("Hello Python world!")
If the command python worked on your system, you won’t have to config -
ure Geany; skip the next section and move on to “Running the Hello World
Program” on page 14. If you needed to enter a path like C :\ Python35\ python
to start a Python interpreter, follow the directions in the next section to
Configuring Geany
To configure Geany, go to Build 4Set Build Commands . You should see
the words Compile and Execute with a command next to each. The Compile
python in lowercase, but Geany doesn’t
know where your system stored the
python command. You need to add the
path you used in the terminal session. In the Compile and Execute commands, add the drive your
python
command is on and the folder where the
python command is stored. Your
Compile command should look something like this:
C:\Python35\python -m py_compile "%f"
Your path might be a little different, but make sure the spaces and
capitalization match what is shown here. Your Execute command should look something like this:
C:\Python35\python "%f"
Again, make sure the spacing and capitalization in your Execute com -
mand matches what is shown here. Figure 1-3 shows how these commands
should look in Geany’s configuration menu.

14 Chapter 1
Figure 1-3: Here, Geany is configured to use Python 3 on Windows.
A fter you’ve set these commands correctly, click OK.
r unning the Hello World Program
You should now be able to run your program successfully. Run hello_world.py
by selecting Build 4Execute in the menu, by clicking the Execute icon
(which shows a set of gears), or by pressing F5. A terminal window should
pop up with the following output:
Hello Python world!
------------------
(program exited with code: 0)
If you don’t see this, check ever y character on the line you entered. Did
you accidentally capitalize
print ? Did you forget one or both of the quota -
tion marks or parentheses? Programming languages expect ver y specific
syntax, and if you don’t provide that, you’ll get errors. If you can’t get the
program to run, see the next section for help.

Getting Started 15
troubleshooting Installation Issues
Hopefully, setting up your programming environment was successful, but
if you’ve been unable to run hello_world.py , here are a few remedies you
can try:
• When a program contains a significant error, Python displays a trace -
back . Python looks through the file and tries to report the problem. The
traceback might give you a clue as to what issue is preventing the pro -
gram from running.
• Step away from your computer, take a short break, and then tr y again.
Remember that syntax is ver y important in programming, so even a
missing colon, a mismatched quotation mark, or mismatched paren -
theses can prevent a program from running properly. Reread the rel -
evant parts of this chapter, look over what you’ve done, and see if you
can find the mistake.
• Start over again. You probably don’t need to uninstall anything, but it
might make sense to delete your hello_world.py file and create it again
from scratch.
or a different one, and watch what they do carefully. You might have
missed one small step that someone else happens to catch.
If you ask around, you might find that you know someone who uses
P ython.
• The setup instructions in this chapter are also available online, through
https://www.nostarch.com/pythoncrashcourse/ . The online version of these
instructions may work better for you.
• Ask for help online. Appendix C provides a number of resources and
areas online, like forums and live chat sites, where you can ask for solu -
tions from people who’ve already worked through the issue you’re cur -
rently facing.
Don’t worr y about bothering experienced programmers. Ever y
programmer has been stuck at some point, and most programmers are
clearly what you’re tr ying to do, what you’ve already tried, and the results
you’re getting, there’s a good chance someone will be able to help you. As
mentioned in the Introduction, the Python community is ver y beginner
f r iend l y. Python should run well on any modern computer, so find a way to ask
for help if you’re having trouble so far. Early issues can be frustrating, but
they’re well worth sorting out. Once you get hello_world.py running, you can
start to learn Python, and your programming work will become more inter -
esting and satisfying.

16 Chapter 1
running Python Programs from a t erminal
Most of the programs you write in your text editor you’ll run directly
from the editor, but sometimes it’s useful to run programs from a terminal
instead. For example, you might want to run an existing program without
opening it for editing.
You can do this on any system with Python installed if you know how
to access the director y where you’ve stored your program file. To tr y this,
make sure you’ve saved the hello_world.py file in the python_work folder on
On Linux and OS X
Running a Python program from a terminal session is the same on Linux
and OS X. The terminal command
cd, for change directory , is used to navi-
gate through your file system in a terminal session. The command
ls, for
list , shows you all the nonhidden files that exist in the current director y.
Open a new terminal window and issue the following commands to run
hello_world.py :
u ~$cd Desktop/python_work/ v ~/Desktop/python_work$ ls
hello_world.py
w ~/Desktop/python_work$python hello_world.py Hello Python world! At u we use the cd command to navigate to the python_work folder, which is in the Desktop folder. Next, we use the ls command to make sure hello_world.py is in this folder v . Then, we run the file using the command python hello_world.py w. It’s that simple. You just use the python (or python3 ) command to run Python programs. On Windows The terminal command cd, for change directory , is used to navigate through your file system in a command window. The command dir , for directory , shows you all the files that exist in the current director y. Open a new terminal window and issue the following commands to run hello_world.py : u C:\> cd Desktop\python_work v C:\Desktop\python_work> dir hello_world.py w C:\Desktop\python_work> python hello_world.py Hello Python world! At u we use the cd command to navigate to the python_work folder, which is in the Desktop folder. Next, we use the dir command to make sure hello_world.py is in this folder v . Then, we run the file using the command python hello_world.py w. Getting Started 17 If you haven’t configured your system to use the simple command python , you may need to use the longer version of this command: C:\$ cd Desktop\python_work
C:\Desktop\python_work$dir hello_world.py C:\Desktop\python_work$ C:\Python35\python hello_world.py
Hello Python world!
Most of your programs will run fine directly from your editor, but as
your work becomes more complex, you might write programs that you’ll
need to run from a terminal.
t ry It y ourself
The exercises in this chapter are exploratory in nature . Starting in Chapter 2,
the challenges you’ll solve will be based on what you’ve learned .
topics that interest you . As you become familiar with Python, different parts of
the site will be more useful to you .
1-2 . Hello World Typos: Open the hello_world.py file you just created . Make a
typo somewhere in the line and run the program again . Can you make a typo
that generates an error? Can you make sense of the error message? Can you
make a typo that doesn’t generate an error? Why do you think it didn’t make
an error?
1-3. Infinite Skills: If you had infinite programming skills, what would you build?
You’re about to learn how to program . If you have an end goal in mind, you’ll
have an immediate use for your new skills; now is a great time to draft descrip -
tions of what you’d like to create . It’s a good habit to keep an “ideas” notebook
that you can refer to whenever you want to start a new project . Take a few
minutes now to describe three programs you’d like to create .
summary
In this chapter you learned a bit about Python in general, and you installed
Python to your system if it wasn’t already there. You also installed a text edi -
tor to make it easier to write Python code. You learned to run snippets of
Python code in a terminal session, and you ran your first actual program,
hello_world.py . You probably learned a bit about troubleshooting as well.
In the next chapter you’ll learn about the different kinds of data you
can work with in your Python programs, and you’ll learn to use variables
as well.

2
VarIaBles an D
sI mPle Data t yPes
In this chapter you’ll learn about the dif -
ferent kinds of data you can work with in
your Python programs. You’ll also learn how
to store your data in variables and how to use
what r eally happens when y ou run hello_world.py
Let’s take a closer look at what Python does when you run hello_world.py . As
it turns out, Python does a fair amount of work, even when it runs a simple
program:
hello_world.py print("Hello Python world!")

20 Chapter 2
When you run this code, you should see this output:
Hello Python world!
When you run the file hello_world.py, the ending .py indicates that
the file is a Python program. Your editor then runs the file through the
Python interpreter, which reads through the program and determines what
each word in the program means. For example, when the interpreter sees
the word
print , it prints to the screen whatever is inside the parentheses.
your program in different ways. For example, it recognizes that
print is the
name of a function and displays that word in blue. It recognizes that “Hello
Python world!” is not Python code and displays that phrase in orange. This
feature is called syntax highlighting and is quite useful as you start to write
Va r i a b l e s
Let’s tr y using a variable in hello_world.py . Add a new line at the beginning
of the file, and modify the second line:
message = "Hello Python world!"
print(message)
Run this program to see what happens. You should see the same output
you saw prev iously:
Hello Python world!
We’ve added a variable named message . Ever y variable holds a value , which
is the information associated with that variable. In this case the value is the
text “Hello Python world!” Adding a variable makes a little more work for the P ython interpreter.
When it processes the first line, it associates the text “Hello Python world!”
with the variable
message . When it reaches the second line, it prints the value
associated with
message to the screen.
Let’s expand on this program by modifying hello_world.py to print a sec-
ond message. Add a blank line to hello_world.py , and then add two new lines
of code:
message = "Hello Python world!"
print(message)

message = "Hello Python Crash Course world!"
print(message)

Variables and Simple Data Types 21
Now when you run hello_world.py, you should see two lines of output:
Hello Python world!
Hello Python Crash Course world!
You can change the value of a variable in your program at any time,
and Python will always keep track of its current value.
Naming and Using Variables
When you’re using variables in Python, you need to adhere to a few rules
and guidelines. Breaking some of these rules will cause errors; other guide -
to keep the following variable rules in mind:
• Variable names can contain only letters, numbers, and underscores.
They can start with a letter or an underscore, but not with a number.
For instance, you can call a variable message_1 but not 1_message.
• Spaces are not allowed in variable names, but underscores can be used
to separate words in variable names. For example, g reeting_message works,
but greeting message will cause errors.
• Avoid using Python key words and function names as variable names;
that is, do not use words that Python has reser ved for a particular pro -
grammatic purpose, such as the word
print . (See “Python Key words
and Built-in Functions” on page 489.)
• Variable names should be short but descriptive. For example, name is
better than n , student_name is better than s_n , and name_length is better
than length_of_persons_name .
• Be careful when using the lowercase letter l and the uppercase letter O
because they could be confused with the numbers 1 and 0 .
It can take some practice to learn how to create good variable names,
especially as your programs become more interesting and complicated. As
you write more programs and start to read through other people’s code,
you’ll get better at coming up with meaningful names.
note The Python variables you’re using at this point should be lowercase. You won’t get
errors if you use uppercase letters, but it’s a good idea to avoid using them for now.
Avoiding Name Errors When Using Variables
Ever y programmer makes mistakes, and most make mistakes ever y day.
Although good programmers might create errors, they also know how to
respond to those errors efficiently. Let’s look at an error you’re likely to
make early on and learn how to fix it.

22 Chapter 2
We’ll write some code that generates an error on purpose. Enter the
following code, including the misspelled word mesage shown in bold:
message = "Hello Python Crash Course reader!"
print(mesage)
When an error occurs in your program, the Python interpreter does its
best to help you figure out where the problem is. The interpreter provides
a traceback when a program cannot run successfully. A traceback is a record
of where the interpreter ran into trouble when tr ying to execute your code.
Here’s an example of the traceback that Python provides after you’ve acci -
dentally misspelled a variable’s name:
Traceback (most recent call last):
u File "hello_world.py", line 2, in
v print(mesage)
w NameError: name 'mesage' is not defined
The output at u reports that an error occurs in line 2 of the file
hello_world.py . The interpreter shows this line to help us spot the error
quickly v and tells us what kind of error it found w . In this case it found a
name error and reports that the variable being printed,
mesage , has not been
defined. Python can’t identify the variable name provided. A name error
usually means we either forgot to set a variable’s value before using it, or
we made a spelling mistake when entering the variable’s name. Of course, in this example we omitted the letter s in the variable name
message in the second line. The Python interpreter doesn’t spellcheck your
code, but it does ensure that variable names are spelled consistently. For
example, watch what happens when we spell message incorrectly in another
place in the code as well:
mesage = "Hello Python Crash Course reader!"
print(mesage)
In this case, the program runs successfully!
Computers are strict, but they disregard good and bad spelling. As a
result, you don’t need to consider English spelling and grammar rules when
you’re tr ying to create variable names and writing code. Many programming errors are simple, single-character typos in one
line of a program. If you’re spending a long time searching for one of these
errors, know that you’re in good company. Many experienced and talented
programmers spend hours hunting down these kinds of tiny errors. Tr y to
laugh about it and move on, knowing it will happen frequently throughout

Variables and Simple Data Types 23
note The best way to understand new programming concepts is to try using them in your
programs. If you get stuck while working on an exercise in this book, try doing some -
thing else for a while. If you’re still stuck, review the relevant part of that chapter. If
you still need help, see the suggestions in Appendix C.
t ry It y ourself
Write a separate program to accomplish each of these exercises . Save
each program with a filename that follows standard Python conventions,
using lowercase letters and underscores, such as simple_message.py and
simple_messages.py .
2 -1. Simple Message: Store a message in a variable, and then print that
message .
2-2 . Simple Messages: Store a message in a variable, and print that message .
Then change the value of your variable to a new message, and print the new
message .
strings
Because most programs define and gather some sort of data, and then do
something useful with it, it helps to classify different types of data. The first
data type we’ll look at is the string. Strings are quite simple at first glance,
but you can use them in many different ways.
A string is simply a series of characters. Anything inside quotes is con -
sidered a string in Python, and you can use single or double quotes around
"This is a string."
'This is also a string.'
This flexibility allows you to use quotes and apostrophes within your
strings:
'I told my friend, "Python is my favorite language!"'
"The language 'Python' is named after Monty Python, not the snake."
"One of Python's strengths is its diverse and supportive community."
Let’s explore some of the ways you can use strings.

24 Chapter 2
Changing Case in a String with Methods
One of the simplest tasks you can do with strings is change the case of the
words in a string. Look at the following code, and tr y to determine what’s
happening:
print(name.title())
Save this file as name.py , and then run it. You should see this output:
In this example, the lowercase string "ada lovelace" is stored in the vari-
able
name . The method title() appears after the variable in the print() state -
ment. A method is an action that Python can perform on a piece of data. The
dot (
.) after name in name.title() tells Python to make the title() method
act on the variable
name . Ever y method is followed by a set of parentheses,
because methods often need additional information to do their work.
That information is provided inside the parentheses. The
title() function
doesn’t need any additional information, so its parentheses are empty.
title() displays each word in titlecase, where each word begins with a
capital letter. This is useful because you’ll often want to think of a name as a
piece of information. For example, you might want your program to recog -
nize the input values
them as
Several other useful methods are available for dealing with case as well.
For example, you can change a string to all uppercase or all lowercase letters
like this:
print(name.upper())
print(name.lower())
This will display the following:
The lower() method is particularly useful for storing data. Many times
you won’t want to trust the capitalization that your users provide, so you’ll
convert strings to lowercase before storing them. Then when you want to
display the information, you’ll use the case that makes the most sense for
each string.

Variables and Simple Data Types 25
Combining or Concatenating Strings
It’s often useful to combine strings. For example, you might want to store
a first name and a last name in separate variables, and then combine them
when you want to display someone’s full name:
last_name = "lovelace"
u full_name = first_name + " " + last_name
print(full_name)
Python uses the plus symbol ( +) to combine strings. In this example,
we use
+ to create a full name by combining a first_name , a space, and a
last_name u , giving this result:
This method of combining strings is called concatenation . You can use
concatenation to compose complete messages using the information you’ve
stored in a variable. Let’s look at an example:
last_name = "lovelace"
full_name = first_name + " " + last_name
u print("Hello, " + full_name.title() + "!")
Here, the full name is used at u in a sentence that greets the user, and
the
title() method is used to format the name appropriately. This code
returns a simple but nicely formatted greeting:
You can use concatenation to compose a message and then store the
entire message in a variable:
last_name = "lovelace"
full_name = first_name + " " + last_name
u message = "Hello, " + full_name.title() + "!"
v print(message)
This code displays the message “Hello, Ada Lovelace!” as well, but stor -
ing the message in a variable at u makes the final
print statement at v
much simpler.

26 Chapter 2
Adding Whitespace to Strings with Tabs or Newlines
In programming, whitespace refers to any nonprinting character, such as
spaces, tabs, and end-of-line symbols. You can use whitespace to organize
\t as shown
at u :
>>> print("Python")
Python
u >>> print("\tPython") Python
To add a newline in a string, use the character combination \n:
>>> print("Languages:\nPython\nC\nJavaScript")
Languages:
Python
C
JavaScript
You can also combine tabs and newlines in a single string. The string
"\n\t" tells Python to move to a new line, and start the next line with a tab.
The following example shows how you can use a one-line string to generate
four lines of output:
>>> print("Languages:\n\tPython\n\tC\n\tJavaScript")
Languages:
Python
C
JavaScript
Newlines and tabs will be ver y useful in the next two chapters when you
start to produce many lines of output from just a few lines of code.
Stripping Whitespace
Extra whitespace can be confusing in your programs. To programmers
'python' and 'python ' look pretty much the same. But to a program, they
are two different strings. Python detects the extra space in
'python ' and
considers it significant unless you tell it other wise. It’s important to think about whitespace, because often you’ll want to
compare two strings to determine whether they are the same. For example,
one important instance might involve checking people’s usernames when
they log in to a website. Extra whitespace can be confusing in much simpler
situations as well. Fortunately, Python makes it easy to eliminate extraneous
whitespace from data that people enter. Python can look for extra whitespace on the right and left sides of a
string. To ensure that no whitespace exists at the right end of a string, use
the
rstrip() method.

Variables and Simple Data Types 27
u >>> favorite_language = 'python '
v >>> favorite_language 'python '
w >>> favorite_language.rstrip() 'python'
x >>> favorite_language 'python '
The value stored in favorite_language at u contains extra whitespace
at the end of the string. When you ask Python for this value in a terminal
session, you can see the space at the end of the value v . When the
rstrip()
method acts on the variable
favorite_language at w , this extra space is
removed. However, it is only removed temporarily. If you ask for the value
of
favorite_language again, you can see that the string looks the same as
when it was entered, including the extra whitespace x .
To remove the whitespace from the string permanently, you have to
store the stripped value back into the variable:
>>> favorite_language = 'python '
u >>> favorite_language = favorite_language.rstrip() >>> favorite_language
'python'
To remove the whitespace from the string, you strip the whitespace
from the right side of the string and then store that value back in the origi -
nal variable, as shown at u . Changing a variable’s value and then storing
the new value back in the original variable is done often in programming.
This is how a variable’s value can change as a program is executed or in
response to user input. You can also strip whitespace from the left side of a string using the
lstrip() method or strip whitespace from both sides at once using strip() :
u >>> favorite_language = ' python '
v >>> favorite_language.rstrip()
' python'
w >>> favorite_language.lstrip() 'python '
x >>> favorite_language.strip() 'python'
In this example, we start with a value that has whitespace at the begin -
ning and the end u . We then remove the extra space from the right side
at v , from the left side at w , and from both sides at x . Experimenting with
strings. In the real world, these stripping functions are used most often to
clean up user input before it’s stored in a program.

28 Chapter 2
Avoiding Syntax Errors with Strings
One kind of error that you might see with some regularity is a syntax error.
A syntax error occurs when Python doesn’t recognize a section of your pro -
gram as valid Python code. For example, if you use an apostrophe within
single quotes, you’ll produce an error. This happens because Python inter -
prets ever ything between the first single quote and the apostrophe as a
string. It then tries to interpret the rest of the text as Python code, which
causes errors. Here’s how to use single and double quotes correctly. Save this program
as apostrophe.py and then run it:
apostrophe.py message = "One of Python's strengths is its diverse community."
print(message)
The apostrophe appears inside a set of double quotes, so the Python
interpreter has no trouble reading the string correctly:
One of Python's strengths is its diverse community.
However, if you use single quotes, Python can’t identify where the string
should end:
message = 'One of Python's strengths is its diverse community.'
print(message)
You’ll see the following output:
File "apostrophe.py", line 1
message = 'One of Python's strengths is its diverse community.'
^u
SyntaxError: invalid syntax
In the output you can see that the error occurs at u right after the
second single quote. This syntax error indicates that the interpreter doesn’t
recognize something in the code as valid Python code. Errors can come
from a variety of sources, and I’ll point out some common ones as they arise.
You might see syntax errors often as you learn to write proper Python code.
Syntax errors are also the least specific kind of error, so they can be difficult
and frustrating to identify and correct. If you get stuck on a particularly stub -
born error, see the suggestions in Appendix C.
quickly as you write your programs. If you see Python code highlighted as if it’s
English or English highlighted as if it’s Python code, you probably have a mis -
matched quotation mark somewhere in your file.

Variables and Simple Data Types 29
Printing in Python 2
The print statement has a slightly different syntax in Python 2:
>>> python2.7
>>> print "Hello Python 2.7 world!"
Hello Python 2.7 world!
Parentheses are not needed around the phrase you want to print
in Python 2. Technically,
print is a function in Python 3, which is why it
needs parentheses. Some Python 2
print statements do include paren -
theses, but the behavior can be a little different than what you’ll see in
Python 3. Basically, when you’re looking at code written in Python 2,
expect to see some
print statements with parentheses and some without.
t ry It y ourself
Save each of the following exercises as a separate file with a name like
name_cases.py . If you get stuck, take a break or see the suggestions in
Appendix C .
2-3. Personal Message: Store a person’s name in a variable, and print a mes -
sage to that person . Your message should be simple, such as, “Hello Eric,
would you like to learn some Python today?”
2-4. Name Cases: Store a person’s name in a variable, and then print that per -
son’s name in lowercase, uppercase, and titlecase .
2-5. Famous Quote: Find a quote from a famous person you admire . Print the
quote and the name of its author . Your output should look something like the
following, including the quotation marks:
Albert Einstein once said, “A person who never made a
mistake never tried anything new.”
2-6. Famous Quote 2: Repeat Exercise 2-5, but this time store the famous per -
son’s name in a variable called
famous_person . Then compose your message
and store it in a new variable called
message . Print your message .
2 - 7. Stripping Names: Store a person’s name, and include some whitespace
characters at the beginning and end of the name . Make sure you use each
character combination,
"\t" and "\n" , at least once .
Print the name once, so the whitespace around the name is displayed .
Then print the name using each of the three stripping functions,
lstrip() ,
rstrip() , and strip() .

30 Chapter 2
numbers
Numbers are used quite often in programming to keep score in games, rep -
resent data in visualizations, store information in web applications, and so
on. Python treats numbers in several different ways, depending on how they
are being used. Let’s first look at how Python manages integers, because
they are the simplest to work with.
Integers
You can add ( +), subtract ( -), multiply ( *), and divide ( /) integers in Python.
>>> 2 + 3
5
>>> 3 - 2
1
>>> 2 * 3
6
>>> 3 / 2
1.5
In a terminal session, Python simply returns the result of the operation.
Python uses two multiplication symbols to represent exponent s :
>>> 3 ** 2
9
>>> 3 ** 3
27
>>> 10 ** 6
1000000
Python supports the order of operations too, so you can use multiple
operations in one expression. You can also use parentheses to modify the
order of operations so Python can evaluate your expression in the order
you specify. For example:
>>> 2 + 3*4
14
>>> (2 + 3) * 4
20
The spacing in these examples has no effect on how Python evaluates
the expressions; it simply helps you more quickly spot the operations that
have priority when you’re reading through the code.
Floats
Python calls any number with a decimal point a float . This term is used in
most programming languages, and it refers to the fact that a decimal point
can appear at any position in a number. Ever y programming language must

Variables and Simple Data Types 31
be carefully designed to properly manage decimal numbers so numbers
behave appropriately no matter where the decimal point appears.For the most part, you can use decimals without worr ying about how
they behave. Simply enter the numbers you want to use, and Python will
most likely do what you expect:
>>> 0.1 + 0.1
0.2
>>> 0.2 + 0.2
0.4
>>> 2 * 0.1
0.2
>>> 2 * 0.2
0.4
But be aware that you can sometimes get an arbitrar y number of deci -
>>> 0.2 + 0.1
0.30000000000000004
>>> 3 * 0.1
0.30000000000000004
This happens in all languages and is of little concern. Python tries to
find a way to represent the result as precisely as possible, which is sometimes
difficult given how computers have to represent numbers internally. Just
ignore the extra decimal places for now; you’ll learn ways to deal with the
extra places when you need to in the projects in Part II.
Avoiding Type Errors with the str() Function
Often, you’ll want to use a variable’s value within a message. For example,
say you want to wish someone a happy birthday. You might write code
like this:
birthday.py age = 23
message = "Happy " + age + "rd Birthday!"
print(message)
You might expect this code to print the simple birthday greeting, Happy
23rd birthday!
But if you run this code, you’ll see that it generates an error:
Traceback (most recent call last):
File "birthday.py", line 2, in
message = "Happy " + age + "rd Birthday!"
u TypeError: Can't convert 'int' object to str implicitly
This is a type error . It means Python can’t recognize the kind of informa -
tion you’re using. In this example Python sees at u that you’re using a vari -
able that has an integer value (
int ), but it’s not sure how to interpret that

32 Chapter 2
value. Python knows that the variable could represent either the numerical
value 23 or the characters 2 and 3. When you use integers within strings
like this, you need to specify explicitly that you want Python to use the inte -
ger as a string of characters. You can do this by wrapping the variable in the
str() function, which tells Python to represent non-string values as strings:
age = 23
message = "Happy " + str(age) + "rd Birthday!"
print(message)
Python now knows that you want to convert the numerical value 23 to
a string and display the characters
2 and 3 as part of the birthday message.
Now you get the message you were expecting, without any errors:
Happy 23rd Birthday!
Working with numbers in Python is straightfor ward most of the time.
If you’re getting unexpected results, check whether Python is interpreting
your numbers the way you want it to, either as a numerical value or as a
string value.
Integers in Python 2
Python 2 returns a slightly different result when you divide two integers:
>>> python2.7
>>> 3 / 2
1
Instead of 1.5 , Python returns 1. Division of integers in Python 2 results
in an integer with the remainder truncated. Note that the result is not a
rounded integer; the remainder is simply omitted. To avoid this behavior in Python 2, make sure that at least one of the
numbers is a float. By doing so, the result will be a float as well:
>>> 3 / 2
1
>>>
3.0 / 2
1.5
>>>
3 / 2.0
1.5
>>>
3.0 / 2.0
1.5
This division behavior is a common source of confusion when people
who are used to Python 3 start using Python 2, or vice versa. If you use or
create code that mixes integers and floats, watch out for irregular behavior.

Variables and Simple Data Types 33
try It y ourself
2-8. Number Eight: Write addition, subtraction, multiplication, and division
operations that each result in the number 8 . Be sure to enclose your operations
in
print statements to see the results . You should create four lines that look
like this:
print(5 + 3)
Your output should simply be four lines with the number 8 appearing once
on each line .
2 - 9. Favorite Number: Store your favorite number in a variable . Then, using
that variable, create a message that reveals your favorite number . Print that
message .
Comments are an extremely useful feature in most programming languages.
Ever ything you’ve written in your programs so far is Python code. As your
programs become longer and more complicated, you should add notes within
solving. A comment allows you to write notes in English within your programs.
In Python, the hash mark ( #) indicates a comment. Anything following a
hash mark in your code is ignored by the Python interpreter. For example:
comment.py # Say hello to everyone.
print("Hello Python people!")
Python ignores the first line and executes the second line.
Hello Python people!
What Kind of Comments Should You Write?
The main reason to write comments is to explain what your code is sup -
posed to do and how you are making it work. When you’re in the middle of
working on a project, you understand how all of the pieces fit together. But
when you return to a project after some time away, you’ll likely have forgot -
ten some of the details. You can always study your code for a while and fig -
ure out how segments were supposed to work, but writing good comments
can save you time by summarizing your overall approach in clear English.

34 Chapter 2
If you want to become a professional programmer or collaborate with
other programmers, you should write meaningful comments. Today, most
software is written collaboratively, whether by a group of employees at one
company or a group of people working together on an open source project.
Skilled programmers expect to see comments in code, so it’s best to start
comments in your code is one of the most beneficial habits you can form as
a new programmer. When you’re determining whether to write a comment, ask yourself if
you had to consider several approaches before coming up with a reasonable
way to make something work; if so, write a comment about your solution.
It’s much easier to delete extra comments later on than it is to go back
and write comments for a sparsely commented program. From now on, I’ll
use comments in examples throughout this book to help explain sections
of code.
try It y ourself
2 -10 . Adding Comments: Choose two of the programs you’ve written, and
add at least one comment to each . If you don’t have anything specific to write
the current date at the top of each program file . Then write one sentence
describing what the program does .
the zen of Python
For a long time, the programming language Perl was the mainstay of the
Internet. Most interactive websites in the early days were powered by Perl
scripts. The Perl community’s motto at the time was, “There’s more than
one way to do it.” People liked this mind-set for a while, because the flex -
ibility written into the language made it possible to solve most problems
in a variety of ways. This approach was acceptable while working on your
own projects, but eventually people realized that the emphasis on flexibility
made it difficult to maintain large projects over long periods of time. It was
difficult, tedious, and time-consuming to review code and tr y to figure out
what someone else was thinking when they were solving a complex problem. Experienced Python programmers will encourage you to avoid com -
plexity and aim for simplicity whenever possible. The Python community’s
philosophy is contained in “The Zen of Python” by Tim Peters. You can
access this brief set of principles for writing good Python code by enter -
ing
import this into your interpreter. I won’t reproduce the entire “Zen of

Variables and Simple Data Types 35
Python” here, but I’ll share a few lines to help you understand why they
should be important to you as a beginning Python programmer.
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Python programmers embrace the notion that code can be beautiful
and elegant. In programming, people solve problems. Programmers have
always respected well-designed, efficient, and even beautiful solutions to
someone might look over your shoulder one day and say, “Wow, that’s some
beautiful code!”
Simple is better than complex.
If you have a choice between a simple and a complex solution, and both
work, use the simple solution. Your code will be easier to maintain, and it
will be easier for you and others to build on that code later on.
Complex is better than complicated.
Real life is messy, and sometimes a simple solution to a problem is unat -
tainable. In that case, use the simplest solution that works.
Even when your code is complex, aim to make it readable. When you’re
working on a project that involves complex coding, focus on writing infor -
There should be one-- and preferably only one --obvious way to do it.
If two Python programmers are asked to solve the same problem, they
should come up with fairly compatible solutions. This is not to say there’s
no room for creativity in programming. On the contrar y! But much of pro -
gramming consists of using small, common approaches to simple situations
within a larger, more creative project. The nuts and bolts of your programs
should make sense to other Python programmers.
Now is better than never.
You could spend the rest of your life learning all the intricacies of
Python and of programming in general, but then you’d never complete any
projects. Don’t tr y to write perfect code; write code that works, and then
decide whether to improve your code for that project or move on to some -
t hing new.

36 Chapter 2
As you continue to the next chapter and start digging into more
involved topics, tr y to keep this philosophy of simplicity and clarity in
mind. Experienced programmers will respect your code more and will
be happy to give you feedback and collaborate with you on interesting
projects.
t ry It y ourself
2 -11 . Zen of Python: Enter import this into a Python terminal session and skim
summary
In this chapter you learned to work with variables. You learned to use
descriptive variable names and how to resolve name errors and syn -
tax errors when they arise. You learned what strings are and how to
display strings using lowercase, uppercase, and titlecase. You started
using whitespace to organize output neatly, and you learned to strip
unneeded whitespace from different parts of a string. You started working
to watch out for when working with numerical data. You also learned to
write explanator y comments to make your code easier for you and others
simple as possible, whenever possible. In Chapter 3 you’ll learn to store collections of information in variables
called lists . You’ll learn to work through a list, manipulating any informa -
tion in that list.

3
IntroDuCIng lI sts
In this chapter and the next you’ll learn
what lists are and how to start working with
the elements in a list. Lists allow you to store
sets of information in one place, whether you
have just a few items or millions of items. Lists are
one of Python’s most powerful features readily acces -
sible to new programmers, and they tie together many
important concepts in programming.
what Is a l ist?
A list is a collection of items in a particular order. You can make a list that
includes the letters of the alphabet, the digits from 0 –9, or the names of
all the people in your family. You can put anything you want into a list, and

38 Chapter 3
the items in your list don’t have to be related in any particular way. Because
a list usually contains more than one element, it’s a good idea to make the
name of your list plural, such as
letters , digits , or names .
In Python, square brackets (
[]) indicate a list, and individual elements
in the list are separated by commas. Here’s a simple example of a list that
contains a few kinds of bicycles:
bicycles.py bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles)
If you ask Python to print a list, Python returns its representation of the
list, including the square brackets:
['trek', 'cannondale', 'redline', 'specialized']
Because this isn’t the output you want your users to see, let’s learn how
to access the individual items in a list.
Accessing Elements in a List
Lists are ordered collections, so you can access any element in a list by
telling Python the position, or index , of the item desired. To access an ele -
ment in a list, write the name of the list followed by the index of the item
enclosed in square brackets. For example, let’s pull out the first bicycle in the list
bicycles :
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
u print(bicycles[0])
The syntax for this is shown at u . When we ask for a single item from a
list, Python returns just that element without square brackets or quotation
marks:
trek
This is the result you want your users to see—clean, neatly formatted
output. You can also use the string methods from Chapter 2 on any element in
a list. For example, you can format the element
'trek' more neatly by using
the
title() method:
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[0].title())
This example produces the same output as the preceding example
except
'Trek' is capitalized.

Introducing Lists 39
Index Positions Start at 0, Not 1
Python considers the first item in a list to be at position 0, not position 1.
This is true of most programming languages, and the reason has to do with
how the list operations are implemented at a lower level. If you’re receiving
unexpected results, determine whether you are making a simple off-by-one
er r or.The second item in a list has an index of 1. Using this simple counting
system, you can get any element you want from a list by subtracting one
from its position in the list. For instance, to access the fourth item in a list,
you request the item at index 3. The following asks for the bicycles at index
1 and index 3:
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[1])
print(bicycles[3])
This code returns the second and fourth bicycles in the list:
cannondale
specialized
Python has a special syntax for accessing the last element in a list. By ask -
ing for the item at index
-1, Python always returns the last item in the list:
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[-1])
This code returns the value 'specialized' . This syntax is quite useful,
because you’ll often want to access the last items in a list without knowing
exactly how long the list is. This convention extends to other negative index
values as well. The index -
2 returns the second item from the end of the list,
the index -
3 returns the third item from the end, and so forth.
Using Individual Values from a List
You can use individual values from a list just as you would any other vari -
able. For example, you can use concatenation to create a message based on
a value from a list. Let’s tr y pulling the first bicycle from the list and composing a message
using that value.
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
u message = "My first bicycle was a " + bicycles[0].title() + "."
print(message)

40 Chapter 3
At u, we build a sentence using the value at bicycles[0] and store it in
the variable
message . The output is a simple sentence about the first bicycle
in the list:
My first bicycle was a Trek.
t ry It y ourself
Try these short programs to get some firsthand experience with Python’s lists .
You might want to create a new folder for each chapter’s exercises to keep
them organized .
3 -1. Names: Store the names of a few of your friends in a list called
names . Print
each person’s name by accessing each element in the list, one at a time .
3 -2 . Greetings: Start with the list you used in Exercise 3-1, but instead of just
printing each person’s name, print a message to them . The text of each mes-
sage should be the same, but each message should be personalized with the
person’s name .
3-3. Your Own List: Think of your favorite mode of transportation, such as a
motorcycle or a car, and make a list that stores several examples . Use your list
to print a series of statements about these items, such as “I would like to own a
Honda motorcycle .”
Changing, a dding, and r emoving elements
Most lists you create will be dynamic, meaning you’ll build a list and
then add and remove elements from it as your program runs its course. For
example, you might create a game in which a player has to shoot aliens out
of the sky. You could store the initial set of aliens in a list and then remove
an alien from the list each time one is shot down. Each time a new alien
appears on the screen, you add it to the list. Your list of aliens will decrease
and increase in length throughout the course of the game.
Modifying Elements in a List
The syntax for modifying an element is similar to the syntax for accessing
an element in a list. To change an element, use the name of the list followed
by the index of the element you want to change, and then provide the new
value you want that item to have.

Introducing Lists 41
For example, let’s say we have a list of motorcycles, and the first item in
the list is
'honda' . How would we change the value of this first item?

motorcycles.py u motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
v motorcycles[0] = 'ducati' print(motorcycles)
The code at u defines the original list, with 'honda' as the first element.
The code at v changes the value of the first item to
'ducati' . The output
shows that the first item has indeed been changed, and the rest of the list
stays the same:
['honda', 'yamaha', 'suzuki']
['ducati', 'yamaha', 'suzuki']
You can change the value of any item in a list, not just the first item.
You might want to add a new element to a list for many reasons. For
example, you might want to make new aliens appear in a game, add new
data to a visualization, or add new registered users to a website you’ve
built. Python provides several ways to add new data to existing lists.
a ppending Elements to the End of a List
The simplest way to add a new element to a list is to append the item to the
list. When you append an item to a list, the new element is added to the end
of the list. Using the same list we had in the previous example, we’ll add the
new element
'ducati' to the end of the list:
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
u motorcycles.append('ducati') print(motorcycles)
The append() method at u adds 'ducati' to the end of the list without
affecting any of the other elements in the list:
['honda', 'yamaha', 'suzuki']
['honda', 'yamaha', 'suzuki', 'ducati']

42 Chapter 3
The append() method makes it easy to build lists dynamically. For
using a series of
append() statements. Using an empty list, let’s add the ele -
ment s
'honda' , 'yamaha' , and 'suzuki' to the list:
motorcycles = []
motorcycles.append('honda')
motorcycles.append('yamaha')
motorcycles.append('suzuki')
print(motorcycles)
The resulting list looks exactly the same as the lists in the previous
examples :
['honda', 'yamaha', 'suzuki']
Building lists this way is ver y common, because you often won’t know
the data your users want to store in a program until after the program is
running. To put your users in control, start by defining an empty list that
will hold the users’ values. Then append each new value provided to the list
you just created.
Inserting Elements into a List
You can add a new element at any position in your list by using the
insert()
method. You do this by specifying the index of the new element and the
value of the new item.
motorcycles = ['honda', 'yamaha', 'suzuki']
u motorcycles.insert(0, 'ducati') print(motorcycles)
In this example, the code at u inserts the value 'ducati' at the begin -
ning of the list. The
insert() method opens a space at position 0 and stores
the value
'ducati' at that location. This operation shifts ever y other value
in the list one position to the right:
['ducati', 'honda', 'yamaha', 'suzuki']
Removing Elements from a List
Often, you’ll want to remove an item or a set of items from a list. For
example, when a player shoots down an alien from the sky, you’ll most
likely want to remove it from the list of active aliens. Or when a user

Introducing Lists 43
decides to cancel their account on a web application you created, you’ll
want to remove that user from the list of active users. You can remove an
item according to its position in the list or according to its value.
removing an Item Using the del Statement
If you know the position of the item you want to remove from a list, you can
use the
del st atement.
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
u del motorcycles[0] print(motorcycles)
The code at u uses del to remove the first item, 'honda' , from the list of
motorcycles:
['honda', 'yamaha', 'suzuki']
['yamaha', 'suzuki']
You can remove an item from any position in a list using the del state -
ment if you know its index. For example, here’s how to remove the second
item,
'yamaha' , in the list:
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
del motorcycles[1]
print(motorcycles)
The second motorcycle is deleted from the list:
['honda', 'yamaha', 'suzuki']
['honda', 'suzuki']
In both examples, you can no longer access the value that was removed
from the list after the
del statement is used.
r emoving an Item Using the pop() Method
Sometimes you’ll want to use the value of an item after you remove it from a
list. For example, you might want to get the x and y position of an alien that
was just shot down, so you can draw an explosion at that position. In a web
application, you might want to remove a user from a list of active members
and then add that user to a list of inactive members. The
pop() method removes the last item in a list, but it lets you work
with that item after removing it. The term pop comes from thinking of a
list as a stack of items and popping one item off the top of the stack. In
this analog y, the top of a stack corresponds to the end of a list.

44 Chapter 3
Let’s pop a motorcycle from the list of motorcycles:
u motorcycles = ['honda', 'yamaha', 'suzuki']print(motorcycles)
v popped_motorcycle = motorcycles.pop()
w print(motorcycles)
x print(popped_motorcycle)
We start by defining and printing the list motorcycles at u . At v we pop
a value from the list and store that value in the variable
popped_motorcycle .
We print the list at w to show that a value has been removed from the list.
Then we print the popped value at x to prove that we still have access to
the value that was removed. The output shows that the value
'suzuki' was removed from the end of
the list and is now stored in the variable
popped_motorcycle :
['honda', 'yamaha', 'suzuki']
['honda', 'yamaha']
suzuki
How might this pop() method be useful? Imagine that the motorcycles
in the list are stored in chronological order according to when we owned
them. If this is the case, we can use the
pop() method to print a statement
about the last motorcycle we bought:
motorcycles = ['honda', 'yamaha', 'suzuki']
last_owned = motorcycles.pop()
print("The last motorcycle I owned was a " + last_owned.title() + "."
)
The output is a simple sentence about the most recent motorcycle we
owned:
The last motorcycle I owned was a Suzuki.
Popping Items from any Position in a List
You can actually use
pop() to remove an item in a list at any position by
including the index of the item you want to remove in parentheses.
motorcycles = ['honda', 'yamaha', 'suzuki']
u first_owned = motorcycles.pop(0)
v print('The first motorcycle I owned was a ' + first_owned.title() +
'.')

Introducing Lists 45
We start by popping the first motorcycle in the list at u, and then we
print a message about that motorcycle at v . The output is a simple sentence
describing the first motorcycle I ever owned:
The first motorcycle I owned was a Honda.
Remember that each time you use pop() , the item you work with is no
longer stored in the list. If you’re unsure whether to use the
del statement or the pop() method,
here’s a simple way to decide: when you want to delete an item from a list
and not use that item in any way, use the
del statement; if you want to use an
item as you remove it, use the
pop() method.
r emoving an Item by Value
Sometimes you won’t know the position of the value you want to remove
from a list. If you only know the value of the item you want to remove, you
can use the
remove() method.
For example, let’s say we want to remove the value
'ducati' from the list of
motorcycles.
motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']
print(motorcycles)
u motorcycles.remove('ducati') print(motorcycles)
The code at u tells Python to figure out where 'ducati' appears in the
list and remove that element:
['honda', 'yamaha', 'suzuki', 'ducati']
['honda', 'yamaha', 'suzuki']
You can also use the remove() method to work with a value that’s being
removed from a list. Let’s remove the value
'ducati' and print a reason for
removing it from the list:
u motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']
print(motorcycles)
v too_expensive = 'ducati'
w motorcycles.remove(too_expensive) print(motorcycles)
x print("\nA " + too_expensive.title() + " is too expensive for me.")

A fter defining the list at u , we store the value 'ducati' in a variable
called
too_expensive v . We then use this variable to tell Python which value

46 Chapter 3
to remove from the list at w. At x the value 'ducati' has been removed from
the list but is still stored in the variable
too_expensive , allowing us to print a
'ducati' from the list of motorcycles:
['honda', 'yamaha', 'suzuki', 'ducati']
['honda', 'yamaha', 'suzuki']
A Ducati is too expensive for me.
note The remove() method deletes only the first occurrence of the value you specify. If there’s
a possibility the value appears more than once in the list, you’ll need to use a loop to
determine if all occurrences of the value have been removed. You’ll learn how to do
this in Chapter 7.
t ry It y ourself
The following exercises are a bit more complex than those in Chapter 2, but
they give you an opportunity to use lists in all of the ways described .
3-4. Guest List: If you could invite anyone, living or deceased, to dinner, who
would you invite? Make a list that includes at least three people you’d like to
invite to dinner . Then use your list to print a message to each person, inviting
them to dinner .
3-5. Changing Guest List: You just heard that one of your guests can’t make the
dinner, so you need to send out a new set of invitations . You’ll have to think of
someone else to invite .
print statement at the
end of your program stating the name of the guest who can’t make it .
• Modify your list, replacing the name of the guest who can’t make it with
the name of the new person you are inviting .
• Print a second set of invitation messages, one for each person who is still
3-6. More Guests: You just found a bigger dinner table, so now more space is
available . Think of three more guests to invite to dinner .
print
statement to the end of your program informing people that you found a
bigger dinner table .
• Use
insert() to add one new guest to the beginning of your list .
• Use
insert() to add one new guest to the middle of your list .
• Use
append() to add one new guest to the end of your list .
• Print a new set of invitation messages, one for each person in your list .

Introducing Lists 47
3 - 7. Shrinking Guest List: You just found out that your new dinner table won’t
arrive in time for the dinner, and you have space for only two guests .
message saying that you can invite only two people for dinner .
• Use
pop() to remove guests from your list one at a time until only two
names remain in your list . Each time you pop a name from your list, print
a message to that person letting them know you’re sorry you can’t invite
them to dinner .
• Print a message to each of the two people still on your list, letting them
know they’re still invited .
• Use
del to remove the last two names from your list, so you have an empty
list . Print your list to make sure you actually have an empty list at the end
organizing a l ist
Often, your lists will be created in an unpredictable order, because you can’t
always control the order in which your users provide their data. Although
this is unavoidable in most circumstances, you’ll frequently want to present
your information in a particular order. Sometimes you’ll want to preser ve the
original order of your list, and other times you’ll want to change the origi -
nal order. Python provides a number of different ways to organize your lists,
depending on the situation.
Sorting a List Permanently with the sort() Method
P y t hon’s sort() method makes it relatively easy to sort a list. Imagine we
have a list of cars and want to change the order of the list to store them
alphabetically. To keep the task simple, let’s assume that all the values in
the list are lowercase.
cars.py cars = ['bmw', 'audi', 'toyota', 'subaru']
u cars.sort() print(cars)
The sort() method, show n at u , changes the order of the list perma -
nently. The cars are now in alphabetical order, and we can never revert to
the original order:
['audi', 'bmw', 'subaru', 'toyota']

48 Chapter 3
You can also sort this list in reverse alphabetical order by passing the
argument
reverse=True to the sort() method. The following example sorts
the list of cars in reverse alphabetical order:
cars = ['bmw', 'audi', 'toyota', 'subaru']
cars.sort(reverse=True)
print(cars)
Again, the order of the list is permanently changed:
['toyota', 'subaru', 'bmw', 'audi']
Sorting a List Temporarily with the sorted() Function
To maintain the original order of a list but present it in a sorted order, you
can use the
sorted() function. The sorted() function lets you display your list
in a particular order but doesn’t affect the actual order of the list. Let’s tr y this function on the list of cars.
cars = ['bmw', 'audi', 'toyota', 'subaru']
u print("Here is the original list:") print(cars)
v print("\nHere is the sorted list:") print(sorted(cars))
w print("\nHere is the original list again:") print(cars)
We first print the list in its original order at u and then in alphabetical
order at v . A fter the list is displayed in the new order, we show that the list is
still stored in its original order at w .
Here is the original list:
['bmw', 'audi', 'toyota', 'subaru']
Here is the sorted list:
['audi', 'bmw', 'subaru', 'toyota']
x Here is the original list again: ['bmw', 'audi', 'toyota', 'subaru']
Notice that the list still exists in its original order at x after the sorted()
function has been used. The
sorted() function can also accept a reverse=True
argument if you want to display a list in reverse alphabetical order.

Introducing Lists 49
note Sorting a list alphabetically is a bit more complicated when all the values are not in
lowercase. There are several ways to interpret capital letters when you’re deciding on
a sort order, and specifying the exact order can be more complex than we want to deal
with at this time. However, most approaches to sorting will build directly on what you
learned in this section.
Printing a List in Reverse Order
To reverse the original order of a list, you can use the reverse() method.
If we originally stored the list of cars in chronological order according to
when we owned them, we could easily rearrange the list into reverse chron -
ological order:
cars = ['bmw', 'audi', 'toyota', 'subaru']
print(cars)
cars.reverse()
print(cars)
Notice that reverse() doesn’t sort backward alphabetically; it simply
reverses the order of the list:
['bmw', 'audi', 'toyota', 'subaru']
['subaru', 'toyota', 'audi', 'bmw']
The reverse() method changes the order of a list permanently, but you
can revert to the original order anytime by applying
reverse() to the same
list a second time.
Finding the Length of a List
You can quickly find the length of a list by using the len() function. The list
in this example has four items, so its length is
4:
>>> cars = ['bmw', 'audi', 'toyota', 'subaru']
>>> len(cars)
4
You’ l l fi nd len() useful when you need to identify the number of aliens
that still need to be shot down in a game, determine the amount of data
you have to manage in a visualization, or figure out the number of regis -
tered users on a website, among other tasks.
note Python counts the items in a list starting with one, so you shouldn’t run into any off-
by-one errors when determining the length of a list.

50 Chapter 3
try It y ourself
3-8. Seeing the World: Think of at least five places in the world you’d like to
visit .
• Store the locations in a list . Make sure the list is not in alphabetical order .
• Print your list in its original order . Don’t worry about printing the list neatly,
just print it as a raw Python list .
• Use
sorted() to print your list in alphabetical order without modifying the
actual list .
• Show that your list is still in its original order by printing it .
• Use
sorted() to print your list in reverse alphabetical order without chang -
ing the order of the original list .
• Show that your list is still in its original order by printing it again .
• Use
reverse() to change the order of your list . Print the list to show that its
order has changed .
• Use
reverse() to change the order of your list again . Print the list to show
it’s back to its original order .
• Use
sort() to change your list so it’s stored in alphabetical order . Print the
list to show that its order has been changed .
• Use
sort() to change your list so it’s stored in reverse alphabetical order .
Print the list to show that its order has changed .
3 - 9. Dinner Guests: Working with one of the programs from Exercises 3-4
through 3-7 (page 46), use
len() to print a message indicating the number
of people you are inviting to dinner .
3 -10 . Every Function: Think of something you could store in a list . For example,
you could make a list of mountains, rivers, countries, cities, languages, or any -
thing else you’d like . Write a program that creates a list containing these items
and then uses each function introduced in this chapter at least once .
avoiding Index e rrors when working with lists
One type of error is common to see when you’re working with lists for the
first time. Let’s say you have a list with three items, and you ask for the
fourth item:
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles[3])

Introducing Lists 51
This example results in an index error:
Traceback (most recent call last):
File "motorcycles.py", line 3, in
print(motorcycles[3])
IndexError: list index out of range
Python attempts to give you the item at index 3. But when it searches
the list, no item in
motorcycles has an index of 3. Because of the off-by-one
nature of indexing in lists, this error is typical. People think the third item
is item number 3, because they start counting at 1. But in Python the third
item is number 2, because it starts indexing at 0. An index error means Python can’t figure out the index you requested. If
for by one. Then run the program again to see if the results are correct. Keep in mind that whenever you want to access the last item in a list
you use the index
-1. This will always work, even if your list has changed
size since the last time you accessed it:
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles[-1])
The index -1 always returns the last item in a list, in this case the value
'suzuki' :
'suzuki'
The only time this approach will cause an error is when you request the
last item from an empty list:
motorcycles = []
print(motorcycles[-1])
No items are in motorcycles , so Python returns another index error:
Traceback (most recent call last):
File "motorcyles.py", line 3, in
print(motorcycles[-1])
IndexError: list index out of range
note If an index error occurs and you can’t figure out how to resolve it, try printing your
list or just printing the length of your list. Your list might look much different than
you thought it did, especially if it has been managed dynamically by your program.
Seeing the actual list, or the exact number of items in your list, can help you sort out
such logical errors.

52 Chapter 3
try It y ourself
3 -11 . Intentional Error: If you haven’t received an index error in one of your
programs yet, try to make one happen . Change an index in one of your pro-
grams to produce an index error . Make sure you correct the error before clos -
ing the program .
summary
In this chapter you learned what lists are and how to work with the indi -
vidual items in a list. You learned how to define a list and how to add and
remove elements. You learned to sort lists permanently and temporarily for
display purposes. You also learned how to find the length of a list and how
to avoid index errors when you’re working with lists. In Chapter 4 you’ll learn how to work with items in a list more effi -
ciently. By looping through each item in a list using just a few lines of code
you’ll be able to work efficiently, even when your list contains thousands or
millions of items.

4
work Ing w Ith lI sts
In Chapter 3 you learned how to make a
simple list, and you learned to work with
the individual elements in a list. In this chap -
ter you’ll learn how to loop through an entire
list using just a few lines of code regardless of how
long the list is. Looping allows you to take the same action, or set of actions,
with ever y item in a list. As a result, you’ll be able to work efficiently with
lists of any length, including those with thousands or even millions of items.
l ooping t hrough an e ntire list
You’ll often want to run through all entries in a list, performing the same
task with each item. For example, in a game you might want to move ever y
element on the screen by the same amount, or in a list of numbers you
might want to perform the same statistical operation on ever y element. Or
perhaps you’ll want to display each headline from a list of articles on a web -
site. When you want to do the same action with ever y item in a list, you can
use Python’s
for loop.

54 Chapter 4
Let’s say we have a list of magicians’ names, and we want to print out
each name in the list. We could do this by retrieving each name from the
list individually, but this approach could cause several problems. For one,
it would be repetitive to do this with a long list of names. Also, we’d have to
change our code each time the list’s length changed. A
for loop avoids both
of these issues by letting Python manage these issues internally. Let’s use a
for loop to print out each name in a list of magicians:
magicians.py u magicians = ['alice', 'david', 'carolina']
v for magician in magicians:
w print(magician)
We begin by defining a list at u , just as we did in Chapter 3. At v ,
we define a
for loop. This line tells Python to pull a name from the list
magicians , and store it in the variable magician . At w we tell Python to print
the name that was just stored in
magician . Python then repeats lines v
and w , once for each name in the list. It might help to read this code as
“For ever y magician in the list of magicians, print the magician’s name.”
The output is a simple printout of each name in the list:
alice
david
carolina
A Closer Look at Looping
The concept of looping is important because it’s one of the most common
ways a computer automates repetitive tasks. For example, in a simple loop
like we used in magicians.py , Python initially reads the first line of the loop:
for magician in magicians:
This line tells Python to retrieve the first value from the list magicians
and store it in the variable
magician . This first value is 'alice' . Python then
print(magician)
Python prints the current value of magician , which is still 'alice' . Because
the list contains more values, Python returns to the first line of the loop:
for magician in magicians:
Python retrieves the next name in the list, 'david' , and stores that value
in
magician . Python then executes the line:
print(magician)

Working with Lists 55
Python prints the current value of magician again, which is now 'david' .
Python repeats the entire loop once more with the last value in the list,
'carolina' . Because no more values are in the list, Python moves on to the
next line in the program. In this case nothing comes after the
for loop, so
the program simply ends. When you’re using loops for the first time, keep in mind that the set of
steps is repeated once for each item in the list, no matter how many items
are in the list. If you have a million items in your list, Python repeats these
steps a million times—and usually ver y quickly. Also keep in mind when writing your own
for loops that you can choose
any name you want for the temporar y variable that holds each value in the
list. However, it’s helpful to choose a meaningful name that represents a
single item from the list. For example, here’s a good way to start a
for loop
for a list of cats, a list of dogs, and a general list of items:
for cat in cats:
for dog in dogs:
for item in list_of_items:
on each item within a
for loop. Using singular and plural names can help
you identify whether a section of code is working with a single element from
the list or the entire list.
Doing More Work Within a for Loop
You can do just about anything with each item in a for loop. Let’s build on
the previous example by printing a message to each magician, telling them
that they performed a great trick:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
u print(magician.title() + ", that was a great trick!")
The only difference in this code is at u where we compose a message to
each magician, starting with that magician’s name. The first time through
the loop the value of magician is
'alice' , so Python starts the first message
with the name
'Alice' . The second time through the message will begin with
'David' , and the third time through the message will begin with 'Carolina' .
The output shows a personalized message for each magician in the list:
Alice, that was a great trick!
David, that was a great trick!
Carolina, that was a great trick!
You can also write as many lines of code as you like in the for loop.
Ever y indented line following the line
for magician in magicians is con -
sidered inside the loop , and each indented line is executed once for each

56 Chapter 4
value in the list. Therefore, you can do as much work as you like with
each value in the list.Let’s add a second line to our message, telling each magician that we’re
looking for ward to their next trick:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician.title() + ", that was a great trick!")
u print("I can't wait to see your next trick, " + magician.title()
+ ".\n")
Because we have indented both print statements, each line will be exe -
cuted once for ever y magician in the list. The newline (
"\n" ) in the second
print st atement u inserts a blank line after each pass through the loop. This
creates a set of messages that are neatly grouped for each person in the list:
Alice, that was a great trick!
I can't wait to see your next trick, Alice.
David, that was a great trick!
I can't wait to see your next trick, David.
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
You can use as many lines as you like in your for loops. In practice you’ll
often find it useful to do a number of different operations with each item in
a list when you use a
for loop.
Doing Something After a for Loop
What happens once a for loop has finished executing? Usually, you’ll want
to summarize a block of output or move on to other work that your pro -
gram must accomplish. Any lines of code after the
for loop that are not indented are executed
once without repetition. Let’s write a thank you to the group of magicians
as a whole, thanking them for putting on an excellent show. To display this
group message after all of the individual messages have been printed, we
place the thank you message after the
for loop w ithout indentation:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician.title() + ", that was a great trick!")
print("I can't wait to see your next trick, " + magician.title()
+ ".\n")

u print("Thank you, everyone. That was a great magic show!")

Working with Lists 57
The first two print statements are repeated once for each magician in
the list, as you saw earlier. However, because the line at u is not indented,
it’s printed only once:
Alice, that was a great trick!
I can't wait to see your next trick, Alice.
David, that was a great trick!
I can't wait to see your next trick, David.
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
Thank you, everyone. That was a great magic show!
When you’re processing data using a for loop, you’ll find that this is a
good way to summarize an operation that was performed on an entire data
set. For example, you might use a
for loop to initialize a game by running
through a list of characters and displaying each character on the screen.
You might then write an unindented block after this loop that displays a
Play Now button after all the characters have been drawn to the screen.
a voiding Indentation e rrors
Python uses indentation to determine when one line of code is connected to
the line above it. In the previous examples, the lines that printed messages to
individual magicians were part of the
for loop because they were indented.
Python’s use of indentation makes code ver y easy to read. Basically, it uses
whitespace to force you to write neatly formatted code with a clear visual
structure. In longer Python programs, you’ll notice blocks of code indented
at a few different levels. These indentation levels help you gain a general
sense of the overall program’s organization. As you begin to write code that relies on proper indentation, you’ll
need to watch for a few common indentation errors . For example, people
sometimes indent blocks of code that don’t need to be indented or forget
to indent blocks that need to be indented. Seeing examples of these errors
now will help you avoid them in the future and correct them when they do
appear in your own programs. Let’s examine some of the more common indentation errors.
Forgetting to Indent
Always indent the line after the for statement in a loop. If you forget, Python
will remind you:
magicians.py magicians = ['alice', 'david', 'carolina']
for magician in magicians:
u print(magician)

58 Chapter 4
The print statement at u should be indented, but it’s not. When Python
expects an indented block and doesn’t find one, it lets you know which line
File "magicians.py", line 3
print(magician)
^
IndentationError: expected an indented block
You can usually resolve this kind of indentation error by indenting the
line or lines immediately after the
for st atement.
Sometimes your loop will run without any errors but won’t produce the
expected result. This can happen when you’re tr ying to do several tasks in
a loop and you forget to indent some of its lines. For example, this is what happens when we forget to indent the second
line in the loop that tells each magician we’re looking for ward to their next
trick:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician.title() + ", that was a great trick!")
u print("I can't wait to see your next trick, " + magician.title() + "
.\n")
The print statement at u is supposed to be indented, but because
Python finds at least one indented line after the
for statement, it doesn’t
report an error. As a result, the first
print statement is executed once for
each name in the list because it is indented. The second
print statement is
not indented, so it is executed only once after the loop has finished run -
ning. Because the final value of
magician is 'carolina' , she is the only one
who receives the “looking for ward to the next trick ” message:
Alice, that was a great trick!
David, that was a great trick!
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
This is a logical error . The syntax is valid Python code, but the code does
not produce the desired result because a problem occurs in its logic. If you
expect to see a certain action repeated once for each item in a list and it’s
executed only once, determine whether you need to simply indent a line or
a group of lines.

Working with Lists 59
Indenting Unnecessarily
If you accidentally indent a line that doesn’t need to be indented, Python
informs you about the unexpected indent:
hello_world.py message = "Hello Python world!"
u print(message)
We don’t need to indent the print statement at u , because it doesn’t
belong to the line above it; hence, Python reports that error:
File "hello_world.py", line 2
print(message)
^
IndentationError: unexpected indent
You can avoid unexpected indentation errors by indenting only when
you have a specific reason to do so. In the programs you’re writing at this
point, the only lines you should indent are the actions you want to repeat
for each item in a
for loop.
Indenting Unnecessarily After the Loop
If you accidentally indent code that should run after a loop has finished, that
code will be repeated once for each item in the list. Sometimes this prompts
Python to report an error, but often you’ll receive a simple logical error. For example, let’s see what happens when we accidentally indent the
line that thanked the magicians as a group for putting on a good show:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician.title() + ", that was a great trick!")
print("I can't wait to see your next trick, " + magician.title()
+ ".\n")

u print("Thank you everyone, that was a great magic show!")
Because the line at u is indented, it’s printed once for each person in
the list, as you can see at v :
Alice, that was a great trick!
I can't wait to see your next trick, Alice.
v Thank you everyone, that was a great magic show! David, that was a great trick!
I can't wait to see your next trick, David.
v Thank you everyone, that was a great magic show! Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
v Thank you everyone, that was a great magic show!

60 Chapter 4
This is another logical error, similar to the one in “Forgetting to Indent
Additional Lines” on page 58. Because P ython doesn’t know what you’re
tr ying to accomplish with your code, it will run all code that is written in
valid syntax. If an action is repeated many times when it should be executed
only once, determine whether you just need to unindent the code for that
action.
Forgetting the Colon
The colon at the end of a for statement tells Python to interpret the next
line as the start of a loop.
magicians = ['alice', 'david', 'carolina']
u for magician in magicians print(magician)
If you accidentally forget the colon, as shown at u , you’ll get a syntax
error because Python doesn’t know what you’re tr ying to do. Although
this is an easy error to fix, it’s not always an easy error to find. You’d be
surprised by the amount of time programmers spend hunting down single-
character errors like this. Such errors are difficult to find because we often
just see what we expect to see.
t ry It y ourself
4 -1. Pizzas: Think of at least three kinds of your favorite pizza . Store these
pizza names in a list, and then use a
for loop to print the name of each pizza .
• Modify your
for loop to print a sentence using the name of the pizza
instead of printing just the name of the pizza . For each pizza you should
have one line of output containing a simple statement like I like pepperoni
pizza .
• Add a line at the end of your program, outside the
for loop, that states
how much you like pizza . The output should consist of three or more lines
about the kinds of pizza you like and then an additional sentence, such as
I really love pizza!
4 -2 . Animals: Think of at least three different animals that have a common char -
acteristic . Store the names of these animals in a list, and then use a
for loop to
print out the name of each animal .
• Modify your program to print a statement about each animal, such as
A dog would make a great pet.
• Add a line at the end of your program stating what these animals have in
common . You could print a sentence such as Any of these animals would
make a great pet!

Working with Lists 61
making n umerical lists
Many reasons exist to store a set of numbers. For example, you’ll need to
keep track of the positions of each character in a game, and you might want
to keep track of a player’s high scores as well. In data visualizations, you’ll
almost always work with sets of numbers, such as temperatures, distances,
population sizes, or latitude and longitude values, among other types of
numerical sets.
Lists are ideal for storing sets of numbers, and Python provides a num -
ber of tools to help you work efficiently with lists of numbers. Once you
understand how to use these tools effectively, your code will work well even
when your lists contain millions of items.
Using the range() Function
P y t hon’s range() function makes it easy to generate a series of numbers.
For example, you can use the
range() function to print a series of numbers
like this:
numbers.py for value in range(1,5):
print(value)
Although this code looks like it should print the numbers from 1 to 5, it
doesn’t print the number 5:
1
2
3
4
In this example, range() prints only the numbers 1 through 4. This is
another result of the off-by-one behavior you’ll see often in programming
languages. The
range() function causes Python to start counting at the first
value you give it, and it stops when it reaches the second value you provide.
Because it stops at that second value, the output never contains the end
value, which would have been 5 in this case. To print the numbers from 1 to 5, you would use
range(1,6) :
for value in range(1,6):
print(value)
This time the output starts at 1 and ends at 5:
1
2
3
4
5

62 Chapter 4
If your output is different than what you expect when you’re using
Using range() to Make a List of Numbers
If you want to make a list of numbers, you can convert the results of range()
directly into a list using the
list() function. When you wrap list() around a
call to the
range() function, the output will be a list of numbers.
In the example in the previous section, we simply printed out a series of
numbers. We can use
list() to convert that same set of numbers into a list:
numbers = list(range(1,6))
print(numbers)
And this is the result:
[1, 2, 3, 4, 5]
We can also use the range() function to tell Python to skip numbers
in a given range. For example, here’s how we would list the even numbers
between 1 and 10 :
even_numbers.py even_numbers = list(range(2,11,2))
print(even_numbers)
In this example, the range() function starts with the value 2 and then
adds 2 to that value. It adds 2 repeatedly until it reaches or passes the end
value, 11, and produces this result:
[2, 4, 6, 8, 10]
You can create almost any set of numbers you want to using the range()
function. For example, consider how you might make a list of the first 10
square numbers (that is, the square of each integer from 1 through 10). In
Python, two asterisks (
**) represent exponents. Here’s how you might put
the first 10 square numbers into a list:
squares.py u squares = []
v for value in range(1,11):
w square = value**2
x squares.append(square)
y print(squares)
We start with an empty list called squares at u . At v , we tell Python to
loop through each value from 1 to 10 using the
range() function. Inside
the loop, the current value is raised to the second power and stored in the

Working with Lists 63
variable square at w. At x , each new value of square is appended to the list
squares . Finally, when the loop has finished running, the list of squares is
printed at y :
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
To write this code more concisely, omit the temporar y variable square
and append each new value directly to the list:
squares = []
for value in range(1,11):
u squares.append(value**2)
print(squares)
The code at u does the same work as the lines at w and x in squares.py .
Each value in the loop is raised to the second power and then immediately
appended to the list of squares. You can use either of these two approaches when you’re making more
complex lists. Sometimes using a temporar y variable makes your code eas -
ier to read; other times it makes the code unnecessarily long. Focus first on
writing code that you understand clearly, which does what you want it to do.
Then look for more efficient approaches as you review your code.
Simple Statistics with a List of Numbers
A few Python functions are specific to lists of numbers. For example, you
can easily find the minimum, maximum, and sum of a list of numbers:
>>> digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> min(digits)
0
>>> max(digits)
9
>>> sum(digits)
45
note The examples in this section use short lists of numbers in order to fit easily on the
page. They would work just as well if your list contained a million or more numbers.
List Comprehensions
The approach described earlier for generating the list squares consisted of
using three or four lines of code. A list comprehension allows you to generate
this same list in just one line of code. A list comprehension combines the
for loop and the creation of new elements into one line, and automatically
appends each new element. List comprehensions are not always presented
to beginners, but I have included them here because you’ll most likely see
them as soon as you start looking at other people’s code.

64 Chapter 4
The following example builds the same list of square numbers you saw
earlier but uses a list comprehension:
squares.py squares = [value**2 for value in range(1,11)]
print(squares)
To use this syntax, begin with a descriptive name for the list, such as
squares . Next, open a set of square brackets and define the expression for
the values you want to store in the new list. In this example the expres -
sion is
value**2 , which raises the value to the second power. Then, write
a
for loop to generate the numbers you want to feed into the expression,
and close the square brackets. The
for loop in this example is for value
in range(1,11)
, which feeds the values 1 through 10 into the expression
value**2 . Notice that no colon is used at the end of the for st atement.
The result is the same list of square numbers you saw earlier:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
It takes practice to write your own list comprehensions, but you’ll find
them worthwhile once you become comfortable creating ordinar y lists.
When you’re writing three or four lines of code to generate lists and it
begins to feel repetitive, consider writing your own list comprehensions.
t ry It y ourself
4-3. Counting to Twenty: Use a for loop to print the numbers from 1 to 20,
inclusive .
4-4. One Million: Make a list of the numbers from one to one million, and then
use a
for loop to print the numbers . (If the output is taking too long, stop it by
pressing
ctrl - C or by closing the output window .)
4-5. Summing a Million: Make a list of the numbers from one to one million,
and then use
min() and max() to make sure your list actually starts at one and
ends at one million . Also, use the
sum() function to see how quickly Python can
4-6. Odd Numbers: Use the third argument of the
range() function to make a list
of the odd numbers from 1 to 20 . Use a
for loop to print each number .
4 - 7. Threes: Make a list of the multiples of 3 from 3 to 30 . Use a
for loop to
print the numbers in your list .
4-8. Cubes: A number raised to the third power is called a cube . For example,
the cube of 2 is written as
2**3 in Python . Make a list of the first 10 cubes (that
is, the cube of each integer from 1 through 10), and use a
for loop to print out
the value of each cube .
4 - 9. Cube Comprehension: Use a list comprehension to generate a list of the
first 10 cubes .

Working with Lists 65
working with Part of a l ist
In Chapter 3 you learned how to access single elements in a list, and in this
chapter you’ve been learning how to work through all the elements in a list.
You can also work with a specific group of items in a list, which Python calls
a slice.
Slicing a List
To make a slice, you specify the index of the first and last elements you
want to work with. As with the
range() function, Python stops one item
before the second index you specify. To output the first three elements
in a list, you would request indices
0 through 3, which would return ele -
ment s
0, 1, and 2.
The following example involves a list of players on a team:
players.py players = ['charles', 'martina', 'michael', 'florence', 'eli']
u print(players[0:3])
The code at u prints a slice of this list, which includes just the first
three players. The output retains the structure of the list and includes the
first three players in the list:
['charles', 'martina', 'michael']
You can generate any subset of a list. For example, if you want the sec -
ond, third, and fourth items in a list, you would start the slice at index
1 and
end at index
4:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[1:4])
This time the slice starts with 'martina' and ends with 'florence' :
['martina', 'michael', 'florence']
If you omit the first index in a slice, Python automatically starts your
slice at the beginning of the list:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[:4])
Without a starting index, Python starts at the beginning of the list:
['charles', 'martina', 'michael', 'florence']

66 Chapter 4
A similar syntax works if you want a slice that includes the end of a list.
For example, if you want all items from the third item through the last item,
2 and omit the second index:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[2:])
Python returns all items from the third item through the end of the list:
['michael', 'florence', 'eli']
This syntax allows you to output all of the elements from any point in
your list to the end regardless of the length of the list. Recall that a nega -
tive index returns an element a certain distance from the end of a list;
therefore, you can output any slice from the end of a list. For example, if
we want to output the last three players on the roster, we can use the slice
players[-3:] :
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[-3:])
This prints the names of the last three players and would continue to
work as the list of players changes in size.
Looping Through a Slice
You can use a slice in a for loop if you want to loop through a subset of
the elements in a list. In the next example we loop through the first three
players and print their names as part of a simple roster:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print("Here are the first three players on my team:")
u for player in players[:3]: print(player.title())
Instead of looping through the entire list of players at u , Python loops
through only the first three names:
Here are the first three players on my team:
Charles
Martina
Michael
Slices are ver y useful in a number of situations. For instance, when you’re
creating a game, you could add a player’s final score to a list ever y time that
player finishes playing. You could then get a player’s top three scores by sort -
ing the list in decreasing order and taking a slice that includes just the first
three scores. When you’re working with data, you can use slices to process

Working with Lists 67
your data in chunks of a specific size. Or, when you’re building a web appli-
cation, you could use slices to display information in a series of pages with
an appropriate amount of information on each page.
Copying a List
Often, you’ll want to start with an existing list and make an entirely new list
based on the first one. Let’s explore how copying a list works and examine
one situation in which copying a list is useful. To copy a list, you can make a slice that includes the entire original list
by omitting the first index and the second index (
[:] ). This tells Python to
make a slice that starts at the first item and ends with the last item, produc -
ing a copy of the entire list. For example, imagine we have a list of our favorite foods and want to
make a separate list of foods that a friend likes. This friend likes ever ything
in our list so far, so we can create their list by copying ours:
foods.py u my_foods = ['pizza', 'falafel', 'carrot cake']
v friend_foods = my_foods[:]
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
At u we make a list of the foods we like called my_foods . At v we make a
new list called
friend_foods . We make a copy of my_foods by asking for a slice
of
my_foods without specifying any indices and store the copy in friend_foods .
When we print each list, we see that they both contain the same foods:
My favorite foods are:
['pizza', 'falafel', 'carrot cake']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake']
To prove that we actually have two separate lists, we’ll add a new food
to each list and show that each list keeps track of the appropriate person’s
favorite foods:
my_foods = ['pizza', 'falafel', 'carrot cake']
u friend_foods = my_foods[:]
v my_foods.append('cannoli')
w friend_foods.append('ice cream')
print("My favorite foods are:")
print(my_foods)

68 Chapter 4
print("\nMy friend's favorite foods are:")
print(friend_foods)
At u we copy the original items in my_foods to the new list friend_foods , as
we did in the previous example. Next, we add a new food to each list: at v we
'cannoli' to my_foods , and at w we add 'ice cream' to friend_foods . We then
print the two lists to see whether each of these foods is in the appropriate list.
My favorite foods are:
x ['pizza', 'falafel', 'carrot cake', 'cannoli']
My friend's favorite foods are:
y ['pizza', 'falafel', 'carrot cake', 'ice cream']
The output at x shows that 'cannoli' now appears in our list of favorite
foods but
'ice cream' doesn’t. At y we can see that 'ice cream' now appears
in our friend’s list but
'cannoli' doesn’t. If we had simply set friend_foods
equal to
my_foods , we would not produce two separate lists. For example,
here’s what happens when you tr y to copy a list without using a slice:
my_foods = ['pizza', 'falafel', 'carrot cake']
# This doesn't work:
u friend_foods = my_foods
my_foods.append('cannoli')
friend_foods.append('ice cream')
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
Instead of storing a copy of my_foods in friend_foods at u , we set
friend_foods equal to my_foods . This syntax actually tells Python to con -
nect the new variable
friend_foods to the list that is already contained in
my_foods , so now both variables point to the same list. As a result, when we
'cannoli' to my_foods , it will also appear in friend_foods . Likewise 'ice
cream'
will appear in both lists, even though it appears to be added only to
friend_foods .
The output shows that both lists are the same now, which is not what we
wanted :
My favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']

Working with Lists 69
note Don’t worry about the details in this example for now. Basically, if you’re trying to
work with a copy of a list and you see unexpected behavior, make sure you are copying
the list using a slice, as we did in the first example.
try It y ourself
4 -10 . Slices: Using one of the programs you wrote in this chapter, add several
lines to the end of the program that do the following:
• Print the message, The first three items in the list are: . Then use a slice to
print the first three items from that program’s list .
• Print the message, Three items from the middle of the list are: . Use a slice
to print three items from the middle of the list .
• Print the message, The last three items in the list are: . Use a slice to print
the last three items in the list .
(page 60) . Make a copy of the list of pizzas, and call it
friend_pizzas .
Then, do the following:
• Add a new pizza to the original list .
• Add a different pizza to the list
friend_pizzas .
• Prove that you have two separate lists . Print the message, My favorite
pizzas are:, and then use a
for loop to print the first list . Print the message,
My friend’s favorite pizzas are: , and then use a
for loop to print the sec-
ond list . Make sure each new pizza is stored in the appropriate list .
4 -12 . More Loops: All versions of foods.py in this section have avoided using
for loops when printing to save space . Choose a version of foods.py, and
write two
for loops to print each list of foods .
tu p l es
Lists work well for storing sets of items that can change throughout the
life of a program. The ability to modify lists is particularly important when
you’re working with a list of users on a website or a list of characters in a
game. However, sometimes you’ll want to create a list of items that cannot
change. Tuples allow you to do just that. Python refers to values that cannot
change as immutable, and an immutable list is called a tuple .
Defining a Tuple
A tuple looks just like a list except you use parentheses instead of square
brackets. Once you define a tuple, you can access individual elements by
using each item’s index, just as you would for a list.

70 Chapter 4
For example, if we have a rectangle that should always be a certain size,
we can ensure that its size doesn’t change by putting the dimensions into a
tuple:
dimensions.py u dimensions = (200, 50)
v print(dimensions[0]) print(dimensions[1])
We define the tuple dimensions at u , using parentheses instead of square
brackets. At v we print each element in the tuple individually, using the
same syntax we’ve been using to access elements in a list:
200
50
Let’s see what happens if we tr y to change one of the items in the tuple
dimensions :
dimensions = (200, 50)
u dimensions[0] = 250
The code at u tries to change the value of the first dimension, but
Python returns a type error. Basically, because we’re tr ying to alter a tuple,
which can’t be done to that type of object, Python tells us we can’t assign a
new value to an item in a tuple:
Traceback (most recent call last):
File "dimensions.py", line 3, in
dimensions[0] = 250
TypeError: 'tuple' object does not support item assignment
This is beneficial because we want Python to raise an error when a line
of code tries to change the dimensions of the rectangle.
Looping Through All Values in a Tuple
You can loop over all the values in a tuple using a for loop, just as you did
with a list:
dimensions = (200, 50)
for dimension in dimensions:
print(dimension)
Python returns all the elements in the tuple, just as it would for a list:
200
50

Working with Lists 71
Writing over a Tuple
Although you can’t modify a tuple, you can assign a new value to a variable
that holds a tuple. So if we wanted to change our dimensions, we could
redefine the entire tuple:
u dimensions = (200, 50)print("Original dimensions:")
for dimension in dimensions:
print(dimension)

v dimensions = (400, 100)
w print("\nModified dimensions:") for dimension in dimensions:
print(dimension)
The block at u defines the original tuple and prints the initial dimen -
sions. At v , we store a new tuple in the variable
dimensions . We then print the
new dimensions at w . Python doesn’t raise any errors this time, because
over writing a variable is valid:
Original dimensions:
200
50
Modified dimensions:
400
100
When compared with lists, tuples are simple data structures. Use them
when you want to store a set of values that should not be changed through -
out the life of a program.
t ry It y ourself
4 -13 . Buffet: A buffet-style restaurant offers only five basic foods . Think of five
simple foods, and store them in a tuple .
• Use a
for loop to print each food the restaurant offers .
• Try to modify one of the items, and make sure that Python rejects the
change .
• The restaurant changes its menu, replacing two of the items with different
foods . Add a block of code that rewrites the tuple, and then use a
for
loop to print each of the items on the revised menu .

72 Chapter 4
styling y our Code
Now that you’re writing longer programs, ideas about how to style your code
are worthwhile to know. Take the time to make your code as easy as possible
grams are doing and helps others understand your code as well. Python programmers have agreed on a number of styling conven -
tions to ensure that ever yone’s code is structured in roughly the same way.
Once you’ve learned to write clean Python code, you should be able to
understand the overall structure of anyone else’s Python code, as long as
they follow the same guidelines. If you’re hoping to become a professional
programmer at some point, you should begin following these guidelines as
soon as possible to develop good habits.
The Style Guide
When someone wants to make a change to the Python language, they write
a Python Enhancement Proposal (PEP) . One of the oldest PEPs is PEP 8, which
instructs Python programmers on how to style their code. PEP 8 is fairly
lengthy, but much of it relates to more complex coding structures than
what you’ve seen so far. The Python style guide was written with the understanding that code
is read more often than it is written. You’ll write your code once and then
start reading it as you begin debugging. When you add features to a pro -
gram, you’ll spend more time reading your code. When you share your
code with other programmers, they’ll read your code as well. Given the choice between writing code that’s easier to write or code
that’s easier to read, Python programmers will almost always encourage you
write clear code from the start.
Indentation
PEP 8 recommends that you use four spaces per indentation level. Using
four spaces improves readability while leaving room for multiple levels of
indentation on each line. In a word processing document, people often use tabs rather than
spaces to indent. This works well for word processing documents, but the
Python interpreter gets confused when tabs are mixed with spaces. Ever y
text editor provides a setting that lets you use the
ta b key but then converts
each tab to a set number of spaces. You should definitely use your
ta b ke y,
but also make sure your editor is set to insert spaces rather than tabs into
your document. Mixing tabs and spaces in your file can cause problems that are ver y
difficult to diagnose. If you think you have a mix of tabs and spaces, you
can convert all tabs in a file to spaces in most editors.

Working with Lists 73
Line Length
Many Python programmers recommend that each line should be less than
80 characters. Historically, this guideline developed because most com-
puters could fit only 79 characters on a single line in a terminal window.
Currently, people can fit much longer lines on their screens, but other rea -
sons exist to adhere to the 79 -character standard line length. Professional
programmers often have several files open on the same screen, and using
the standard line length allows them to see entire lines in two or three files
that are open side by side onscreen. PEP 8 also recommends that you limit
all of your comments to 72 characters per line, because some of the tools
that generate automatic documentation for larger projects add formatting
characters at the beginning of each commented line. The PEP 8 guidelines for line length are not set in stone, and some
teams prefer a 99 -character limit. Don’t worr y too much about line length
in your code as you’re learning, but be aware that people who are work -
ing collaboratively almost always follow the PEP 8 guidelines. Most editors
allow you to set up a visual cue, usually a vertical line on your screen, that
shows you where these limits are.
note Appendix B shows you how to configure your text editor so it always inserts four
spaces each time you press the
Blank Lines
To group parts of your program visually, use blank lines. You should use
blank lines to organize your files, but don’t do so excessively. By following
the examples provided in this book, you should strike the right balance. For
example, if you have five lines of code that build a list, and then another
three lines that do something with that list, it’s appropriate to place a blank
line between the two sections. However, you should not place three or four
blank lines between the two sections. Blank lines won’t affect how your code runs, but they will affect the
tation to interpret the meaning of your code, but it disregards vertical
spacing.
Other Style Guidelines
PEP 8 has many additional styling recommendations, but most of the guide -
lines refer to more complex programs than what you’re writing at this point.
As you learn more complex Python structures, I’ll share the relevant parts of
the PEP 8 guidelines.

74 Chapter 4
try It y ourself
4 -14 . PEP 8: Look through the original PEP 8 style guide at https://python.org/
d e v/p e p s/p e p - 0 0 0 8/ . You won’t use much of it now, but it might be interesting
to skim through it .
4 -15 . Code Review: Choose three of the programs you’ve written in this chapter
and modify each one to comply with PEP 8:
• Use four spaces for each indentation level . Set your text editor to insert
four spaces every time you press
tab , if you haven’t already done so (see
Appendix B for instructions on how to do this) .
• Use less than 80 characters on each line, and set your editor to show a
vertical guideline at the 80th character position .
• Don’t use blank lines excessively in your program files .
summary
In this chapter you learned how to work efficiently with the elements in a
list. You learned how to work through a list using a
for loop, how Python
uses indentation to structure a program, and how to avoid some common
indentation errors. You learned to make simple numerical lists, as well as a
few operations you can perform on numerical lists. You learned how to slice
a list to work with a subset of items and how to copy lists properly using a
slice. You also learned about tuples, which provide a degree of protection
to a set of values that shouldn’t change, and how to style your increasingly
complex code to make it easy to read. In Chapter 5, you’ll learn to respond appropriately to different condi -
tions by using
if statements. You’ll learn to string together relatively com -
plex sets of conditional tests to respond appropriately to exactly the kind
of situation or information you’re looking for. You’ll also learn to use
if
statements while looping through a list to take specific actions with selected
elements from a list.

5
If s tatements
Programming often involves examining
a set of conditions and deciding which
action to take based on those conditions.
P y t ho n’s
if statement allows you to examine the
current state of a program and respond appropriately
to that state.
In this chapter you’ll learn to write conditional tests, which allow you to
check any condition of interest. You’ll learn to write simple
if statements,
and you’ll learn how to create a more complex series of
if statements to
identify when the exact conditions you want are present. You’ll then apply
this concept to lists, so you’ll be able to write a
for loop that handles most
items in a list one way but handles certain items with specific values in a
different way.

76 Chapter 5
a s imple e xample
The following short example shows how if tests let you respond to special
situations correctly. Imagine you have a list of cars and you want to print
out the name of each car. Car names are proper names, so the names of
most cars should be printed in title case. However, the value
'bmw' should
be printed in all uppercase. The following code loops through a list of car
names and looks for the value
'bmw' . Whenever the value is 'bmw' , it’s printed
in uppercase instead of title case:
cars.py cars = ['audi', 'bmw', 'subaru', 'toyota']
for car in cars:
u if car == 'bmw': print(car.upper())
else:
print(car.title())
The loop in this example first checks if the current value of car is 'bmw' u .
If it is, the value is printed in uppercase. If the value of
car is anything other
than
'bmw' , it’s printed in title case:
Audi
BMW
Subaru
Toyota
This example combines a number of the concepts you’ll learn about
in this chapter. Let’s begin by looking at the kinds of tests you can use to
examine the conditions in your program.
Conditional t ests
At the heart of ever y if statement is an expression that can be evaluated as
True or False and is called a conditional test . Python uses the values True and
False to decide whether the code in an if statement should be executed. If a
conditional test evaluates to
True , Python executes the code following the if
statement. If the test evaluates to
False , Python ignores the code following
the
if st atement.
Checking for Equality
Most conditional tests compare the current value of a variable to a specific
value of interest. The simplest conditional test checks whether the value of a
variable is equal to the value of interest:
u >>> car = 'bmw'
v >>> car == 'bmw'
True

if Statements 77
The line at u sets the value of car to 'bmw' using a single equal sign,
as you’ve seen many times already. The line at v checks whether the value
of
car is 'bmw' using a double equal sign ( ==). This equality operator returns
True if the values on the left and right side of the operator match, and
False if they don’t match. The values in this example match, so Python
returns
True .
When the value of
car is anything other than 'bmw' , this test returns
False :
u >>> car = 'audi'
v >>> car == 'bmw'
False
A single equal sign is really a statement; you might read the code at u
as “Set the value of car equal to
'audi' .” On the other hand, a double equal
sign, like the one at v , asks a question: “Is the value of car equal to
'bmw' ? ”
Most programming languages use equal signs in this way.
Ignoring Case When Checking for Equality
Testing for equality is case sensitive in Python. For example, two values with
different capitalization are not considered equal:
>>> car = 'Audi'
>>> car == 'audi'
False
If case matters, this behavior is advantageous. But if case doesn’t matter
and instead you just want to test the value of a variable, you can convert the
variable’s value to lowercase before doing the comparison:
>>> car = 'Audi'
>>> car.lower() == 'audi'
True
This test would return True no matter how the value 'Audi' is formatted
because the test is now case insensitive. The
lower() function doesn’t change
the value that was originally stored in
car , so you can do this kind of com -
parison without affecting the original variable:
u >>> car = 'Audi'
v >>> car.lower() == 'audi'
True
w >>> car 'Audi'
At u we store the capitalized string 'Audi' in the variable car . At v
we convert the value of
car to lowercase and compare the lowercase value

78 Chapter 5
to the string 'audi' . The two strings match, so Python returns True . At w
we can see that the value stored in
car has not been affected by the condi -
tional test. Websites enforce certain rules for the data that users enter in a
manner similar to this. For example, a site might use a conditional test
like this to ensure that ever y user has a truly unique username, not just a
variation on the capitalization of another person’s username. When some -
one submits a new username, that new username is converted to lowercase
and compared to the lowercase versions of all existing usernames. During
'John' will be rejected if any variation of 'john'
Checking for Inequality
When you want to determine whether two values are not equal, you can
combine an exclamation point and an equal sign (
!=). The exclamation
point represents not , as it does in many programming languages.
Let’s use another
if statement to examine how to use the inequality
operator. We’ll store a requested pizza topping in a variable and then print
a message if the person did not order anchovies:
toppings.py requested_topping = 'mushrooms'
u if requested_topping != 'anchovies': print("Hold the anchovies!")
The line at u compares the value of requested_topping to the value
'anchovies' . If these two values do not match, Python returns True and exe-
cutes the code following the
if statement. If the two values match, Python
returns
False and does not run the code following the if st atement.
Because the value of
requested_topping is not 'anchovies' , the print state -
ment is executed:
Hold the anchovies!
Most of the conditional expressions you write will test for equality, but
sometimes you’ll find it more efficient to test for inequality.
Numerical Comparisons
Testing numerical values is pretty straightfor ward. For example, the follow -
ing code checks whether a person is 18 years old:
>>> age = 18
>>> age == 18
True

if Statements 79
You can also test to see if two numbers are not equal. For example, the
following code prints a message if the given answer is not correct:
The conditional test at u passes, because the value of answer (17) is not
equal to
42. Because the test passes, the indented code block is executed:
You can include various mathematical comparisons in your conditional
statements as well, such as less than, less than or equal to, greater than, and
greater than or equal to:
>>> age = 19
>>> age < 21
True
>>> age <= 21
True
>>> age > 21
False
>>> age >= 21
False
Each mathematical comparison can be used as part of an if st atement,
Checking Multiple Conditions
You may want to check multiple conditions at the same time. For example,
sometimes you might need two conditions to be
True to take an action. Other
times you might be satisfied with just one condition being
True . The key words
Using and to Check Multiple Conditions
To check whether two conditions are both
True simultaneously, use the key -
word
and to combine the two conditional tests; if each test passes, the over -
all expression evaluates to
True . If either test fails or if both tests fail, the
expression evaluates to
False .
For example, you can check whether two people are both over 21 using
the following test:
u >>> age_0 = 22
>>> age_1 = 18
v >>> age_0 >= 21 and age_1 >= 21 False

80 Chapter 5
w >>> age_1 = 22 >>> age_0 >= 21 and age_1 >= 21
True
At u we define two ages, age_0 and age_1 . At v we check whether both
ages are 21 or older. The test on the left passes, but the test on the right fails,
so the overall conditional expression evaluates to
False . At w we change age_1
to 22. The value of
age_1 is now greater than 21, so both individual tests pass,
causing the overall conditional expression to evaluate as
True .
To improve readability, you can use parentheses around the individual
tests, but they are not required. If you use parentheses, your test would look
like this:
(age_0 >= 21) and (age_1 >= 21)
Using or to Check Multiple Conditions
The key word
or allows you to check multiple conditions as well, but it
passes when either or both of the individual tests pass. An
or expression
fails only when both individual tests fail. Let’s consider two ages again, but this time we’ll look for only one per -
son to be over 21:
u >>> age_0 = 22
>>> age_1 = 18
v >>> age_0 >= 21 or age_1 >= 21 True
w >>> age_0 = 18 >>> age_0 >= 21 or age_1 >= 21
False
We start with two age variables again at u . Because the test for age_0 at v
passes, the overall expression evaluates to
True . We then lower age_0 to 18. In
the test at w , both tests now fail and the overall expression evaluates to
False .
Checking Whether a Value Is in a List
Sometimes it’s important to check whether a list contains a certain value
before taking an action. For example, you might want to check whether a
ing someone’s registration on a website. In a mapping project, you might
want to check whether a submitted location already exists in a list of known
locations. To find out whether a particular value is already in a list, use the key -
word
in. Let’s consider some code you might write for a pizzeria. We’ll
make a list of toppings a customer has requested for a pizza and then
check whether certain toppings are in the list.

if Statements 81
>>> requested_toppings = ['mushrooms', 'onions', 'pineapple']
u >>> 'mushrooms' in requested_toppings True
v >>> 'pepperoni' in requested_toppings False
At u and v , the key word in tells Python to check for the existence of
'mushrooms' and 'pepperoni' in the list requested_toppings . This technique is
quite powerful because you can create a list of essential values, and then
easily check whether the value you’re testing matches one of the values in
the list.
Checking Whether a Value Is Not in a List
Other times, it’s important to know if a value does not appear in a list. You
can use the key word
not in this situation. For example, consider a list of users
who are banned from commenting in a forum. You can check whether a
user has been banned before allowing that person to submit a comment:
banned_ banned_users = ['andrew', 'carolina', 'david']
users.py user = 'marie'
u if user not in banned_users: print(user.title() + ", you can post a response if you wish.")
The line at u reads quite clearly. If the value of user is not in the list
banned_users , Python returns True and executes the indented line.
The user
'marie' is not in the list banned_users , so she sees a message
inviting her to post a response:
Marie, you can post a response if you wish.
Boolean Expressions
expression at some point. A Boolean expression is just another name for a
conditional test. A Boolean value is either
True or False , just like the value
of a conditional expression after it has been evaluated. Boolean values are often used to keep track of certain conditions, such
as whether a game is running or whether a user can edit certain content on
a website:
game_active = True
can_edit = False
Boolean values provide an efficient way to track the state of a program
or a particular condition that is important in your program.

82 Chapter 5
try It y ourself
5 -1. Conditional Tests: Write a series of conditional tests . Print a statement
describing each test and your prediction for the results of each test . Your code
should look something like this:
car = 'subaru'
print("Is car == 'subaru'? I predict True.")
print(car == 'subaru')
print("\nIs car == 'audi'? I predict False.")
print(car == 'audi')
• Look closely at your results, and make sure you understand why each line
evaluates to
True or False .
• Create at least 10 tests . Have at least 5 tests evaluate to
True and another
5 tests evaluate to
False .
5 -2 . More Conditional Tests: You don’t have to limit the number of tests you
create to 10 . If you want to try more comparisons, write more tests and add
them to conditional_tests.py . Have at least one
True and one False result for
each of the following:
• Tests for equality and inequality with strings
• Tests using the
lower() function
• Numerical tests involving equality and inequality, greater than and
less than, greater than or equal to, and less than or equal to
• Tests using the
and keyword and the or key word
• Test whether an item is in a list
• Test whether an item is not in a list
if statements
When you understand conditional tests, you can start writing if statements.
Several different kinds of
if statements exist, and your choice of which to
use depends on the number of conditions you need to test. You saw several
examples of
if statements in the discussion about conditional tests, but now
let’s dig deeper into the topic.
Simple if Statements
The simplest kind of if statement has one test and one action:
if conditional_test:
do something

if Statements 83
You can put any conditional test in the first line and just about any
action in the indented block following the test. If the conditional test
evaluates to
True , Python executes the code following the if st atement.
If the test evaluates to
False , Python ignores the code following the if
st atement. Let’s say we have a variable representing a person’s age, and we want to
know if that person is old enough to vote. The following code tests whether
the person can vote:
voting.py age = 19
u if age >= 18:
v print("You are old enough to vote!")
At u Python checks to see whether the value in age is greater than or
equal to 18. It is, so Python executes the indented
print statement at v :
You are old enough to vote!
Indentation plays the same role in if statements as it did in for loops.
All indented lines after an
if statement will be executed if the test passes,
and the entire block of indented lines will be ignored if the test does
not pass.
You can have as many lines of code as you want in the block follow -
ing the
if statement. Let’s add another line of output if the person is old
enough to vote, asking if the individual has registered to vote yet:
age = 19
if age >= 18:
print("You are old enough to vote!")
print("Have you registered to vote yet?")
The conditional test passes, and both print statements are indented, so
both lines are printed:
You are old enough to vote!
Have you registered to vote yet?
If the value of age is less than 18, this program would produce no
output.
if-else Statements
Often, you’ll want to take one action when a conditional test passes and a dif -
ferent action in all other cases. Python’s
if-else syntax makes this possible.
An
if-else block is similar to a simple if statement, but the else st atement
allows you to define an action or set of actions that are executed when the
conditional test fails.

84 Chapter 5
We’ll display the same message we had previously if the person is old
enough to vote, but this time we’ll add a message for anyone who is not
old enough to vote:
age = 17
u if age >= 18: print("You are old enough to vote!")
print("Have you registered to vote yet?")
v else: print("Sorry, you are too young to vote.")
print("Please register to vote as soon as you turn 18!")
If the conditional test at u passes, the first block of indented print
statements is executed. If the test evaluates to
False , the else block at v is
executed. Because
age is less than 18 this time, the conditional test fails and
the code in the
else block is executed:
Sorry, you are too young to vote.
Please register to vote as soon as you turn 18!
This code works because it has only two possible situations to evaluate:
a person is either old enough to vote or not old enough to vote. The
if-else
structure works well in situations in which you want Python to always execute
one of two possible actions. In a simple
if-else chain like this, one of the two
actions will always be executed.
The if-elif-else Chain
Often, you’ll need to test more than two possible situations, and to evaluate
these you can use Python’s
if-elif -else syntax. Python executes only one
block in an
if-elif -else chain. It runs each conditional test in order until
one passes. When a test passes, the code following that test is executed and
Python skips the rest of the tests. Many real-world situations involve more than two possible conditions.
For example, consider an amusement park that charges different rates for
different age groups:
• Admission for anyone under age 4 is free.
• Admission for anyone between the ages of 4 and 18 is $5. • Admission for anyone age 18 or older is$10.
How can we use an
if statement to determine a person’s admission rate?
The following code tests for the age group of a person and then prints an
amusement_ age = 12
park.py u if age < 4: print("Your admission cost is $0.") if Statements 85 v elif age < 18: print("Your admission cost is$5.")
w else: print("Your admission cost is $10.") The if test at u tests whether a person is under 4 years old. If the test passes, an appropriate message is printed and Python skips the rest of the tests. The elif line at v is really another if test, which runs only if the pre - vious test failed. At this point in the chain, we know the person is at least 4 years old because the first test failed. If the person is less than 18, an appropriate message is printed and Python skips the else block. If both the if and elif tests fail, Python runs the code in the else block at w . In this example the test at u evaluates to False , so its code block is not executed. However, the second test evaluates to True (12 is less than 18) so its code is executed. The output is one sentence, informing the user of the admission cost: Your admission cost is$5.
Any age greater than 17 would cause the first two tests to fail. In these
situations, the
else block would be executed and the admission price would
b e $10. Rather than printing the admission price within the if-elif -else block, it would be more concise to set just the price inside the if-elif -else chain and then have a simple print statement that runs after the chain has been evaluated: age = 12 if age < 4: u price = 0 elif age < 18: v price = 5 else: w price = 10 x print("Your admission cost is$" + str(price) + ".")
The lines at u , v, and w set the value of price according to the person’s
age, as in the previous example. A fter the price is set by the
if-elif -else chain,
a separate unindented
print st atement x uses this value to display a mes -
sage reporting the person’s admission price. This code produces the same output as the previous example, but the
purpose of the
if-elif -else chain is narrower. Instead of determining a
price and displaying a message, it simply determines the admission price.
In addition to being more efficient, this revised code is easier to modify
than the original approach. To change the text of the output message, you
would need to change only one
print statement rather than three separate
print statements.

86 Chapter 5
Using Multiple elif Blocks
You can use as many elif blocks in your code as you like. For example, if the
amusement park were to implement a discount for seniors, you could add
one more conditional test to the code to determine whether someone quali -
fied for the senior discount. Let’s say that anyone 65 or older pays half the
regular admission, or $5: age = 12 if age < 4: price = 0 elif age < 18: price = 5 u elif age < 65: price = 10 v else: price = 5 print("Your admission cost is$" + str(price) + ".")
Most of this code is unchanged. The second elif block at u now checks
to make sure a person is less than age 65 before assigning them the full
admission rate of $10. Notice that the value assigned in the else block at v needs to be changed to$ 5, because the only ages that make it to this block
are people 65 or older.
Omitting the else Block
Python does not require an else block at the end of an if-elif chain. Some -
times an
else block is useful; sometimes it is clearer to use an additional
elif statement that catches the specific condition of interest:
age = 12
if age < 4:
price = 0
elif age < 18:
price = 5
elif age < 65:
price = 10
u elif age >= 65: price = 5
print("Your admission cost is $" + str(price) + ".") The extra elif block at u assigns a price of$ 5 when the person is 65 or
older, which is a bit clearer than the general
else block. With this change,
ever y block of code must pass a specific test in order to be executed.

if Statements 87
The else block is a catchall statement. It matches any condition that
wasn’t matched by a specific
if or elif test, and that can sometimes include
invalid or even malicious data. If you have a specific final condition you are
testing for, consider using a final
elif block and omit the else block. As a
result, you’ll gain extra confidence that your code will run only under the
correct conditions.
Testing Multiple Conditions
The if-elif -else chain is powerful, but it’s only appropriate to use when you
just need one test to pass. As soon as Python finds one test that passes, it
skips the rest of the tests. This behavior is beneficial, because it’s efficient
and allows you to test for one specific condition. However, sometimes it’s important to check all of the conditions of
interest. In this case, you should use a series of simple
if statements with no
elif or else blocks. This technique makes sense when more than one condi -
tion could be
True , and you want to act on ever y condition that is True .
Let’s reconsider the pizzeria example. If someone requests a two-topping
pizza, you’ll need to be sure to include both toppings on their pizza:
toppings.py u requested_toppings = ['mushrooms', 'extra cheese']
v if 'mushrooms' in requested_toppings: print("Adding mushrooms.")
w if 'pepperoni' in requested_toppings: print("Adding pepperoni.")
x if 'extra cheese' in requested_toppings: print("Adding extra cheese.")

We start at u with a list containing the requested toppings. The if
statement at v checks to see whether the person requested mushrooms
on their pizza. If so, a message is printed confirming that topping. The
test for pepperoni at w is another simple
if statement, not an elif or else
statement, so this test is run regardless of whether the previous test passed
or not. The code at x checks whether extra cheese was requested regard -
less of the results from the first two tests. These three independent tests
are executed ever y time this program is run. Because ever y condition in this example is evaluated, both mushrooms
and extra cheese are added to the pizza:

88 Chapter 5
This code would not work properly if we used an if-elif -else block,
because the code would stop running after only one test passes. Here’s what
that would look like:
requested_toppings = ['mushrooms', 'extra cheese']
if 'mushrooms' in requested_toppings:
elif 'pepperoni' in requested_toppings:
elif 'extra cheese' in requested_toppings:

The test for 'mushrooms' is the first test to pass, so mushrooms are added
to the pizza. However, the values
'extra cheese' and 'pepperoni' are never
checked, because Python doesn’t run any tests beyond the first test that
passes in an
if-elif-else chain. The customer’s first topping will be added,
but all of their other toppings will be missed:
In summar y, if you want only one block of code to run, use an if-elif -
else chain. If more than one block of code needs to run, use a series of
independent
if statements.
t ry It y ourself
5-3. Alien Colors #1: Imagine an alien was just shot down in a game . Create a
variable called
alien_color and assign it a value of 'green' , 'yellow' , or 'red' .
• Write an
if statement to test whether the alien’s color is green . If it is, print
a message that the player just earned 5 points .
• Write one version of this program that passes the
if test and another that
fails . (The version that fails will have no output .)
5-4. Alien Colors #2 : Choose a color for an alien as you did in Exercise 5-3, and
write an
if-else chain .
• If the alien’s color is green, print a statement that the player just earned
5 points for shooting the alien .
• If the alien’s color isn’t green, print a statement that the player just earned
10 points .
• Write one version of this program that runs the
if block and another that
runs the
else block .

if Statements 89
5-5. Alien Colors #3: Turn your if-else chain from Exercise 5-4 into an if-elif -
else chain .
• If the alien is green, print a message that the player earned 5 points .
• If the alien is yellow, print a message that the player earned 10 points .
• If the alien is red, print a message that the player earned 15 points .
• Write three versions of this program, making sure each message is printed
for the appropriate color alien .
5-6. Stages of Life: Write an
if-elif -else chain that determines a person’s
stage of life . Set a value for the variable
age , and then:
• If the person is less than 2 years old, print a message that the person is
a b a b y .
• If the person is at least 2 years old but less than 4, print a message that
the person is a toddler .
• If the person is at least 4 years old but less than 13, print a message that
the person is a kid .
• If the person is at least 13 years old but less than 20, print a message that
the person is a teenager .
• If the person is at least 20 years old but less than 65, print a message that
the person is an adult .
• If the person is age 65 or older, print a message that the person is an
elder .
5 - 7. Favorite Fruit: Make a list of your favorite fruits, and then write a series of
independent
if statements that check for certain fruits in your list .
• Make a list of your three favorite fruits and call it
favorite_fruits .
• Write five
if statements . Each should check whether a certain kind of fruit
is in your list . If the fruit is in your list, the
if block should print a statement,
such as You really like bananas!
u sing if s tatements with l ists
You can do some interesting work when you combine lists and if state-
ments. You can watch for special values that need to be treated differently
than other values in the list. You can manage changing conditions effi -
ciently, such as the availability of certain items in a restaurant throughout a
shift. You can also begin to prove that your code works as you expect it to in
all possible situations.

90 Chapter 5
Checking for Special Items
This chapter began with a simple example that showed how to handle a spe-
cial value like
'bmw' , which needed to be printed in a different format than
other values in the list. Now that you have a basic understanding of condi -
tional tests and
if statements, let’s take a closer look at how you can watch
for special values in a list and handle those values appropriately. Let’s continue with the pizzeria example. The pizzeria displays a message
this action can be written ver y efficiently by making a list of toppings the
customer has requested and using a loop to announce each topping as it’s
toppings.py requested_toppings = ['mushrooms', 'green peppers', 'extra cheese']
for requested_topping in requested_toppings:
print("Adding " + requested_topping + ".")
The output is straightfor ward because this code is just a simple for loop:
But what if the pizzeria runs out of green peppers? An if st atement
inside the
for loop can handle this situation appropriately:
requested_toppings = ['mushrooms', 'green peppers', 'extra cheese']
for requested_topping in requested_toppings:
u if requested_topping == 'green peppers': print("Sorry, we are out of green peppers right now.")
v else: print("Adding " + requested_topping + ".")
This time we check each requested item before adding it to the pizza.
The code at u checks to see if the person requested green peppers. If so,
we display a message informing them why they can’t have green peppers.
The
else block at v ensures that all other toppings will be added to the
pizza.

if Statements 91
The output shows that each requested topping is handled appropriately.
Sorry, we are out of green peppers right now.
Checking That a List Is Not Empty
We’ve made a simple assumption about ever y list we’ve worked with so far;
we’ve assumed that each list has at least one item in it. Soon we’ll let users
provide the information that’s stored in a list, so we won’t be able to assume
that a list has any items in it each time a loop is run. In this situation, it’s
useful to check whether a list is empty before running a
for loop.
As an example, let’s check whether the list of requested toppings is
empty before building the pizza. If the list is empty, we’ll prompt the user
and make sure they want a plain pizza. If the list is not empty, we’ll build
the pizza just as we did in the previous examples:
u requested_toppings = []
v if requested_toppings:
for requested_topping in requested_toppings:
print("Adding " + requested_topping + ".")
w else: print("Are you sure you want a plain pizza?")
This time we start out with an empty list of requested toppings at u .
Instead of jumping right into a
for loop, we do a quick check at v . W hen the
name of a list is used in an
if statement, Python returns True if the list con -
tains at least one item; an empty list evaluates to
False . If requested_toppings
passes the conditional test, we run the same
for loop we used in the previous
example. If the conditional test fails, we print a message asking the customer
if they really want a plain pizza with no toppings w .
The list is empty in this case, so the output asks if the user really wants
a plain pizza:
Are you sure you want a plain pizza?
If the list is not empty, the output will show each requested topping

92 Chapter 5
Using Multiple Lists
People will ask for just about anything, especially when it comes to pizza
toppings. What if a customer actually wants french fries on their pizza? You
can use lists and
if statements to make sure your input makes sense before
you act on it. Let’s watch out for unusual topping requests before we build a pizza.
The following example defines two lists. The first is a list of available top -
pings at the pizzeria, and the second is the list of toppings that the user has
requested. This time, each item in
requested_toppings is checked against the
list of available toppings before it’s added to the pizza:
u available_toppings = ['mushrooms', 'olives', 'green peppers',
'pepperoni', 'pineapple', 'extra cheese']
v requested_toppings = ['mushrooms', 'french fries', 'extra cheese']
w for requested_topping in requested_toppings:
x if requested_topping in available_toppings: print("Adding " + requested_topping + ".")
y else: print("Sorry, we don't have " + requested_topping + ".")

At u we define a list of available toppings at this pizzeria. Note that
this could be a tuple if the pizzeria has a stable selection of toppings. At v ,
we make a list of toppings that a customer has requested. Note the unusual
request,
'french fries' . At w we loop through the list of requested toppings.
Inside the loop, we first check to see if each requested topping is actually
in the list of available toppings x . If it is, we add that topping to the pizza.
If the requested topping is not in the list of available toppings, the
else block
will run y . The
else block prints a message telling the user which toppings
are unavailable. This code syntax produces clean, informative output:
Sorry, we don't have french fries.
In just a few lines of code, we’ve managed a real-world situation pretty
effectively!

if Statements 93
try It y ourself
5-8. Hello Admin: Make a list of five or more usernames, including the name
'admin' . Imagine you are writing code that will print a greeting to each user
after they log in to a website . Loop through the list, and print a greeting to
each user:
would you like to see a status report?
• Otherwise, print a generic greeting, such as Hello Eric, thank you for log -
ging in again.
5 - 9. No Users: Add an
if test to hello_admin.py to make sure the list of users is
not empty .
• If the list is empty, print the message We need to find some users!
• Remove all of the usernames from your list, and make sure the correct
message is printed .
5 -10 . Checking Usernames: Do the following to create a program that simulates
how websites ensure that everyone has a unique username .
• Make a list of five or more usernames called
current_users .
• Make another list of five usernames called
new_users . Make sure one or
two of the new usernames are also in the
current_users list .
• Loop through the
been used . If it has, print a message that the person will need to enter a
new username . If a username has not been used, print a message saying
that the username is available .
• Make sure your comparison is case insensitive . If
'John' has been used,
'JOHN' should not be accepted .
5 -11 . Ordinal Numbers: Ordinal numbers indicate their position in a list, such
as 1s t or 2nd . Most ordinal numbers end in th , except 1, 2, and 3 .
• Store the numbers 1 through 9 in a list .
• Loop through the list .
• Use an
if-elif -else chain inside the loop to print the proper ordinal end -
"1st 2nd 3rd 4th 5th 6th
7th 8th 9th"
, and each result should be on a separate line .

94 Chapter 5
In ever y example in this chapter, you’ve seen good styling habits. The only
recommendation PEP 8 provides for styling conditional tests is to use a
single space around comparison operators, such as
==, >=, <=. For example:
if age < 4:
is better than:
if age<4:
Such spacing does not affect the way Python interprets your code; it just
t ry It y ourself
5 -12 . Styling if statements: Review the programs you wrote in this chapter, and
make sure you styled your conditional tests appropriately .
5 -13 . Your Ideas: At this point, you’re a more capable programmer than you
were when you started this book . Now that you have a better sense of how
real-world situations are modeled in programs, you might be thinking of some
problems you could solve with your own programs . Record any new ideas you
have about problems you might want to solve as your programming skills con -
tinue to improve . Consider games you might want to write, data sets you might
want to explore, and web applications you’d like to create .
summary
In this chapter you learned how to write conditional tests, which always
evaluate to
True or False . You learned to write simple if statements, if-else
chains, and
if-elif -else chains. You began using these structures to identify
particular conditions you needed to test and to know when those conditions
have been met in your programs. You learned to handle certain items in a
list differently than all other items while continuing to utilize the efficiency
of a
for loop. You also revisited Python’s style recommendations to ensure
that your increasingly complex programs are still relatively easy to read and
understand. In Chapter 6 you’ll learn about Python’s dictionaries. A dictionar y is
similar to a list, but it allows you to connect pieces of information. You’ll
learn to build dictionaries, loop through them, and use them in combina -
tion with lists and
if statements. Learning about dictionaries will enable
you to model an even wider variety of real-world situations.

6
DIC tIonar Ies
In this chapter you’ll learn how to use
Python’s dictionaries, which allow you to
connect pieces of related information. You’ll
learn how to access the information once it’s
in a dictionary and how to modify that information.
Because dictionaries can store an almost limitless
amount of information, I’ll show you how to loop through the data in a
dictionar y. Additionally, you’ll learn to nest dictionaries inside lists, lists
inside dictionaries, and even dictionaries inside other dictionaries. Understanding dictionaries allows you to model a variety of real-world
objects more accurately. You’ll be able to create a dictionar y representing a
person and then store as much information as you want about that person.
You can store their name, age, location, profession, and any other aspect of
a person you can describe. You’ll be able to store any two kinds of informa -
tion that can be matched up, such as a list of words and their meanings, a
list of people’s names and their favorite numbers, a list of mountains and
their elevations, and so forth.

96 Chapter 6
a simple d ictionary
Consider a game featuring aliens that can have different colors and point
values. This simple dictionar y stores information about a particular alien:
alien.py alien_0 = {'color': 'green', 'points': 5}
print(alien_0['color'])
print(alien_0['points'])
The dictionar y alien_0 stores the alien’s color and point value. The two
print statements access and display that information, as shown here:
green
5
As with most new programming concepts, using dictionaries takes
practice. Once you’ve worked with dictionaries for a bit you’ll soon see how
effectively they can model real-world situations.
w orking with d ictionaries
A dictionary in Python is a collection of key-value pairs . Each key is connected
to a value, and you can use a key to access the value associated with that key.
A key’s value can be a number, a string, a list, or even another dictionar y.
In fact, you can use any object that you can create in Python as a value in a
dictionar y. In Python, a dictionar y is wrapped in braces,
{}, with a series of key-
value pairs inside the braces, as shown in the earlier example:
alien_0 = {'color': 'green', 'points': 5}
A key-value pair is a set of values associated with each other. When you
provide a key, Python returns the value associated with that key. Ever y key
is connected to its value by a colon, and individual key-value pairs are sepa -
rated by commas. You can store as many key-value pairs as you want in a
dictionar y. The simplest dictionar y has exactly one key-value pair, as shown in this
modified version of the
alien_0 dictionar y:
alien_0 = {'color': 'green'}
This dictionar y stores one piece of information about alien_0 , namely
the alien’s color. The string
'color' is a key in this dictionar y, and its associ -
ated value is
'green' .

Dictionaries 97
Accessing Values in a Dictionary
To get the value associated with a key, give the name of the dictionar y and
then place the key inside a set of square brackets, as shown here:
alien_0 = {'color': 'green'}
print(alien_0['color'])
This returns the value associated with the key 'color' from the diction-
ar y
alien_0 :
green
You can have an unlimited number of key-value pairs in a dictionar y.
For example, here’s the original
alien_0 dictionar y with two key-value pairs:
alien_0 = {'color': 'green', 'points': 5}
Now you can access either the color or the point value of alien_0 . If a
player shoots down this alien, you can look up how many points they should
earn using code like this:
alien_0 = {'color': 'green', 'points': 5}
u new_points = alien_0['points']
v print("You just earned " + str(new_points) + " points!")
Once the dictionar y has been defined, the code at u pulls the value
associated with the key
'points' from the dictionar y. This value is then
stored in the variable
new_points . The line at v converts this integer value
to a string and prints a statement about how many points the player just
earned:
You just earned 5 points!
If you run this code ever y time an alien is shot down, the alien’s point
value will be retrieved.
Dictionaries are dynamic structures, and you can add new key-value pairs
to a dictionar y at any time. For example, to add a new key-value pair, you
would give the name of the dictionar y followed by the new key in square
brackets along with the new value. Let’s add two new pieces of information to the
alien_0 dictionar y: the
alien’s x- and y-coordinates, which will help us display the alien in a par -
ticular position on the screen. Let’s place the alien on the left edge of the
screen, 25 pixels down from the top. Because screen coordinates usually
start at the upper-left corner of the screen, we’ll place the alien on the left

98 Chapter 6
edge of the screen by setting the x-coordinate to 0 and 25 pixels from the
top by setting its y-coordinate to positive 25, as shown here:
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
u alien_0['x_position'] = 0
v alien_0['y_position'] = 25 print(alien_0)
We start by defining the same dictionar y that we’ve been working
with. We then print this dictionar y, displaying a snapshot of its informa -
tion. At u we add a new key-value pair to the dictionar y: key
'x_position'
and value
0. We do the same for key 'y_position' at v . When we print the
modified dictionar y, we see the two additional key-value pairs:
{'color': 'green', 'points': 5}
{'color': 'green', 'points': 5, 'y_position': 25, 'x_position': 0}
The final version of the dictionar y contains four key-value pairs. The
original two specify color and point value, and two more specify the alien’s
position. Notice that the order of the key-value pairs does not match the
order in which we added them. Python doesn’t care about the order in
which you store each key-value pair; it cares only about the connection
between each key and its value.
Starting with an Empty Dictionary
It’s sometimes convenient, or even necessar y, to start with an empty diction -
ar y and then add each new item to it. To start filling an empty dictionar y,
define a dictionar y with an empty set of braces and then add each key-value
pair on its own line. For example, here’s how to build the
alien_0 dictionar y
using this approach:
alien_0 = {}
alien_0['color'] = 'green'
alien_0['points'] = 5
print(alien_0)
Here we define an empty alien_0 dictionar y, and then add color and
point values to it. The result is the dictionar y we’ve been using in previous
examples :
{'color': 'green', 'points': 5}
Typically, you’ll use empty dictionaries when storing user-supplied data
in a dictionar y or when you write code that generates a large number of
key-value pairs automatically.

Dictionaries 99
Modifying Values in a Dictionary
To modify a value in a dictionar y, give the name of the dictionar y with the
key in square brackets and then the new value you want associated with
that key. For example, consider an alien that changes from green to yellow
as a game progresses:
alien_0 = {'color': 'green'}
print("The alien is " + alien_0['color'] + ".")
alien_0['color'] = 'yellow'
print("The alien is now " + alien_0['color'] + ".")
We first define a dictionar y for alien_0 that contains only the alien’s
color; then we change the value associated with the key
'color' to 'yellow' .
The output shows that the alien has indeed changed from green to yellow:
The alien is green.
The alien is now yellow.
For a more interesting example, let’s track the position of an alien that
can move at different speeds. We’ll store a value representing the alien’s
current speed and then use it to determine how far to the right the alien
should move:
alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}
print("Original x-position: " + str(alien_0['x_position']))
# Move the alien to the right.
# Determine how far to move the alien based on its current speed.
u if alien_0['speed'] == 'slow': x_increment = 1
elif alien_0['speed'] == 'medium':
x_increment = 2
else:
# This must be a fast alien.
x_increment = 3
# The new position is the old position plus the increment.
v alien_0['x_position'] = alien_0['x_position'] + x_increment
print("New x-position: " + str(alien_0['x_position']))
We start by defining an alien with an initial x position and y position,
and a speed of
'medium' . We’ve omitted the color and point values for the
sake of simplicity, but this example would work the same way if you included
those key-value pairs as well. We also print the original value of
x_position to
see how far the alien moves to the right. At u , an
if-elif -else chain determines how far the alien should move to
the right and stores this value in the variable
x_increment . If the alien’s speed
is
'slow' , it moves one unit to the right; if the speed is 'medium' , it moves two

10 0 Chapter 6
units to the right; and if it’s 'fast' , it moves three units to the right. Once
the increment has been calculated, it’s added to the value of
x_position at v ,
and the result is stored in the dictionar y’s
x_position .
Because this is a medium-speed alien, its position shifts two units to the
r ight:
Original x-position: 0
New x-position: 2
This technique is pretty cool: by changing one value in the alien’s dic -
tionar y, you can change the overall behavior of the alien. For example, to
turn this medium-speed alien into a fast alien, you would add the line:
alien_0['speed'] = fast
The if-elif -else block would then assign a larger value to x_increment
the next time the code runs.
Removing Key-Value Pairs
When you no longer need a piece of information that’s stored in a diction -
ar y, you can use the
del statement to completely remove a key-value pair.
All
del needs is the name of the dictionar y and the key that you want to
remove. For example, let’s remove the key
'points' from the alien_0 dictionar y
along with its value:
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
u del alien_0['points'] print(alien_0)
The line at u tells Python to delete the key 'points' from the dictionar y
alien_0 and to remove the value associated with that key as well. The output
shows that the key
'points' and its value of 5 are deleted from the diction -
ar y, but the rest of the dictionar y is unaffected:
{'color': 'green', 'points': 5}
{'color': 'green'}
note Be aware that the deleted key-value pair is removed permanently.
A Dictionary of Similar Objects
The previous example involved storing different kinds of information about
one object, an alien in a game. You can also use a dictionar y to store one
kind of information about many objects. For example, say you want to poll a

Dictionaries 101
number of people and ask them what their favorite programming language
is. A dictionar y is useful for storing the results of a simple poll, like this:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
As you can see, we’ve broken a larger dictionar y into several lines. Each
key is the name of a person who responded to the poll, and each value is their
language choice. When you know you’ll need more than one line to define
a dictionar y, press
enter after the opening brace. Then indent the next
line one level (four spaces), and write the first key-value pair, followed by
a comma. From this point for ward when you press
should automatically indent all subsequent key-value pairs to match the first
key-value pair. Once you’ve finished defining the dictionar y, add a closing brace on a
new line after the last key-value pair and indent it one level so it aligns with
the keys in the dictionar y. It’s good practice to include a comma after the
last key-value pair as well, so you’re ready to add a new key-value pair on the
next line.
note Most editors have some functionality that helps you format extended lists and dic -
tionaries in a similar manner to this example. Other acceptable ways to format long
dictionaries are available as well, so you may see slightly different formatting in your
editor, or in other sources.
To use this dictionar y, given the name of a person who took the poll,
you can easily look up their favorite language:
favorite_ favorite_languages = {
languages.py 'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
u print("Sarah's favorite language is " +
v favorite_languages['sarah'].title() +
w ".")
To see which language Sarah chose, we ask for the value at:
favorite_languages['sarah']
This syntax is used in the print statement at v , and the output shows
Sarah’s favorite language:
Sarah's favorite language is C.

102 Chapter 6
This example also shows how you can break up a long print st atement
over several lines. The word
print is shorter than most dictionar y names, so
it makes sense to include the first part of what you want to print right after
the opening parenthesis u . Choose an appropriate point at which to break
what’s being printed, and add a concatenation operator (
+) at the end of
the first line v . Press
enter and then press ta b to align all subsequent lines
at one indentation level under the
print statement. W hen you’ve finished
composing your output, you can place the closing parenthesis on the last
line of the
print block w .
try It y ourself
6 -1. Person: Use a dictionary to store information about a person you know .
Store their first name, last name, age, and the city in which they live . You
should have keys such as
first_name , last_name , age , and city . Print each
piece of information stored in your dictionary .
6 -2 . Favorite Numbers: Use a dictionary to store people’s favorite numbers .
Think of five names, and use them as keys in your dictionary . Think of a favorite
number for each person, and store each as a value in your dictionary . Print
each person’s name and their favorite number . For even more fun, poll a few
friends and get some actual data for your program .
6-3. Glossary: A Python dictionary can be used to model an actual dictionary .
However, to avoid confusion, let’s call it a glossary .
• Think of five programming words you’ve learned about in the previous
chapters . Use these words as the keys in your glossary, and store their
meanings as values .
• Print each word and its meaning as neatly formatted output . You might
print the word followed by a colon and then its meaning, or print the word
on one line and then print its meaning indented on a second line . Use the
newline character (
\n) to insert a blank line between each word-meaning
looping t hrough a d ictionary
A single Python dictionar y can contain just a few key-value pairs or millions
of pairs. Because a dictionar y can contain large amounts of data, Python lets
you loop through a dictionar y. Dictionaries can be used to store information
in a variety of ways; therefore, several different ways exist to loop through
them. You can loop through all of a dictionar y’s key-value pairs, through its
keys, or through its values.

Dictionaries 103
Looping Through All Key-Value Pairs
Before we explore the different approaches to looping, let’s consider a
new dictionar y designed to store information about a user on a website.
The following dictionar y would store one person’s username, first name,
and last name:
user_0 = {
'first': 'enrico',
'last': 'fermi',
}
You can access any single piece of information about user_0 based
on what you’ve already learned in this chapter. But what if you wanted to
see ever ything stored in this user’s dictionar y? To do so, you could loop
through the dictionar y using a
for loop:
user.py user_0 = {
'first': 'enrico',
'last': 'fermi',
}
u for key, value in user_0.items():
v print("\nKey: " + key)
w print("Value: " + value)
As shown at u , to write a for loop for a dictionar y, you create names for
the two variables that will hold the key and value in each key-value pair. You
can choose any names you want for these two variables. This code would work
just as well if you had used abbreviations for the variable names, like this:
for k, v in user_0.items()
The second half of the for statement at u includes the name of the dic -
tionar y followed by the method
items() , which returns a list of key-value pairs.
The
for loop then stores each of these pairs in the two variables provided.
In the preceding example, we use the variables to print each
key v , followed
by the associated
value w . The "\n" in the first print statement ensures that a
blank line is inserted before each key-value pair in the output:
Key: last
Value: fermi
Key: first
Value: enrico
Value: efermi

104 Chapter 6
Notice again that the key-value pairs are not returned in the order in
which they were stored, even when looping through a dictionar y. Python
doesn’t care about the order in which key-value pairs are stored; it tracks
only the connections between individual keys and their values. Looping through all key-value pairs works particularly well for diction -
aries like the favorite_languages.py example on page 101, which stores the
same kind of information for many different keys. If you loop through the
favorite_languages dictionar y, you get the name of each person in the dic -
tionar y and their favorite programming language. Because the keys always
refer to a person’s name and the value is always a language, we’ll use the
variables
name and language in the loop instead of key and value . This will
make it easier to follow what’s happening inside the loop:
favorite_ favorite_languages = {
languages.py 'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
u for name, language in favorite_languages.items():
v print(name.title() + "'s favorite language is " + language.title() + ".")
The code at u tells Python to loop through each key-value pair in
the dictionar y. As it works through each pair the key is stored in the vari -
able
name , and the value is stored in the variable language . These descriptive
names make it much easier to see what the
print statement at v is doing.
Now, in just a few lines of code, we can display all of the information
from the poll:
Jen's favorite language is Python.
Sarah's favorite language is C.
Phil's favorite language is Python.
Edward's favorite language is Ruby.
This type of looping would work just as well if our dictionar y stored the
results from polling a thousand or even a million people.
Looping Through All the Keys in a Dictionary
The keys() method is useful when you don’t need to work with all of the
values in a dictionar y. Let’s loop through the
favorite_languages dictionar y
and print the names of ever yone who took the poll:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}

Dictionaries 105
u for name in favorite_languages.keys(): print(name.title())
The line at u tells Python to pull all the keys from the dictionar y
favorite_languages and store them one at a time in the variable name . The
output shows the names of ever yone who took the poll:
Jen
Sarah
Phil
Edward
Looping through the keys is actually the default behavior when looping
through a dictionar y, so this code would have exactly the same output if you
wrote . . .
for name in favorite_languages:
rather than . . .
for name in favorite_languages.keys():
You can choose to use the keys() method explicitly if it makes your code
easier to read, or you can omit it if you wish. You can access the value associated with any key you care about inside
the loop by using the current key. Let’s print a message to a couple of friends
about the languages they chose. We’ll loop through the names in the diction -
ar y as we did previously, but when the name matches one of our friends, we’ll
display a message about their favorite language:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
u friends = ['phil', 'sarah'] for name in favorite_languages.keys():
print(name.title())

v if name in friends: print(" Hi " + name.title() +
", I see your favorite language is " +
w favorite_languages[name].title() + "!")
At u we make a list of friends that we want to print a message to.
Inside the loop, we print each person’s name. Then at v we check to see
whether the
name we are working with is in the list friends . If it is, we print a
special greeting, including a reference to their language choice. To access

106 Chapter 6
the favorite language at w, we use the name of the dictionar y and the cur -
rent value of
name as the key. Ever yone’s name is printed, but our friends
Edward
Phil
Hi Phil, I see your favorite language is Python!
Sarah
Hi Sarah, I see your favorite language is C!
Jen
You can also use the keys() method to find out if a particular person
was polled. This time, let’s find out if Erin took the poll:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
u if 'erin' not in favorite_languages.keys(): print("Erin, please take our poll!")
The keys() method isn’t just for looping: It actually returns a list of all
the keys, and the line at u simply checks if
'erin' is in this list. Because
she’s not, a message is printed inviting her to take the poll:
Looping Through a Dictionary’s Keys in Order
A dictionar y always maintains a clear connection between each key and
its associated value, but you never get the items from a dictionar y in any
predictable order. That’s not a problem, because you’ll usually just want
to obtain the correct value associated with each key. One way to return items in a certain order is to sort the keys as they’re
returned in the
for loop. You can use the sorted() function to get a copy of
the keys in order:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
for name in sorted(favorite_languages.keys()):
print(name.title() + ", thank you for taking the poll.")

Dictionaries 107
This for statement is like other for statements except that we’ve wrapped
the
sorted() function around the dictionary.keys() method. This tells P ython
to list all keys in the dictionar y and sort that list before looping through it.
The output shows ever yone who took the poll with the names displayed in
order:
Edward, thank you for taking the poll.
Jen, thank you for taking the poll.
Phil, thank you for taking the poll.
Sarah, thank you for taking the poll.
Looping Through All Values in a Dictionary
If you are primarily interested in the values that a dictionar y contains,
you can use the
values() method to return a list of values without any keys.
For example, say we simply want a list of all languages chosen in our pro -
gramming language poll without the name of the person who chose each
language:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
print("The following languages have been mentioned:")
for language in favorite_languages.values():
print(language.title())
The for statement here pulls each value from the dictionar y and stores
it in the variable
language . When these values are printed, we get a list of all
chosen languages:
The following languages have been mentioned:
Python
C
Python
Ruby
This approach pulls all the values from the dictionar y without checking
for repeats. That might work fine with a small number of values, but in a
poll with a large number of respondents, this would result in a ver y repeti -
tive list. To see each language chosen without repetition, we can use a set .
A set is similar to a list except that each item in the set must be unique:
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',

108 Chapter 6
'phil': 'python',
}
print("The following languages have been mentioned:")
u for language in set(favorite_languages.values()): print(language.title())
When you wrap set() around a list that contains duplicate items, Python
identifies the unique items in the list and builds a set from those items. At u
we use
set() to pull out the unique languages in favorite_languages.values() .
The result is a nonrepetitive list of languages that have been mentioned
by people taking the poll:
The following languages have been mentioned:
Python
C
Ruby
As you continue learning about Python, you’ll often find a built-in fea -
ture of the language that helps you do exactly what you want with your data.
t ry It y ourself
6-4. Glossary 2: Now that you know how to loop through a dictionary, clean
up the code from Exercise 6 -3 (page 102) by replacing your series of
print
statements with a loop that runs through the dictionary’s keys and values .
When you’re sure that your loop works, add five more Python terms to your
glossary . When you run your program again, these new words and meanings
should automatically be included in the output .
6-5. Rivers: Make a dictionary containing three major rivers and the country
each river runs through . One key-value pair might be 'nile': 'egypt' .
• Use a loop to print a sentence about each river, such as The Nile runs
through Egypt .
• Use a loop to print the name of each river included in the dictionary .
• Use a loop to print the name of each country included in the dictionary .
6-6. Polling: Use the code in favorite_languages.py ( p a g e 10 4 ) .
• Make a list of people who should take the favorite languages poll . Include
some names that are already in the dictionary and some that are not .
• Loop through the list of people who should take the poll . If they have
already taken the poll, print a message thanking them for responding .
If they have not yet taken the poll, print a message inviting them to take
the poll .

Dictionaries 109
nesting
Sometimes you’ll want to store a set of dictionaries in a list or a list of
items as a value in a dictionar y. This is called nesting . You can nest a set
of dictionaries inside a list, a list of items inside a dictionar y, or even a
dictionar y inside another dictionar y. Nesting is a powerful feature, as the
following examples will demonstrate.
A List of Dictionaries
The alien_0 dictionar y contains a variety of information about one alien,
but it has no room to store information about a second alien, much less a
screen full of aliens. How can you manage a fleet of aliens? One way is to
make a list of aliens in which each alien is a dictionar y of information about
that alien. For example, the following code builds a list of three aliens:
aliens.py alien_0 = {'color': 'green', 'points': 5}
alien_1 = {'color': 'yellow', 'points': 10}
alien_2 = {'color': 'red', 'points': 15}
u aliens = [alien_0, alien_1, alien_2] for alien in aliens:
print(alien)
We first create three dictionaries, each representing a different alien.
At u we pack each of these dictionaries into a list called
aliens . Finally, we
loop through the list and print out each alien:
{'color': 'green', 'points': 5}
{'color': 'yellow', 'points': 10}
{'color': 'red', 'points': 15}
A more realistic example would involve more than three aliens with
code that automatically generates each alien. In the following example we
use
range() to create a fleet of 30 aliens:
# Make an empty list for storing aliens.
aliens = []
# Make 30 green aliens.
u for alien_number in range(30):
v new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
w aliens.append(new_alien)
# Show the first 5 aliens:
x for alien in aliens[:5]: print(alien)
print("...")
# Show how many aliens have been created.
y print("Total number of aliens: " + str(len(aliens)))

110 Chapter 6
This example begins with an empty list to hold all of the aliens that
will be created. At u
range() returns a set of numbers, which just tells
Python how many times we want the loop to repeat. Each time the loop
runs we create a new alien v and then append each new alien to the list
aliens w . At x we use a slice to print the first five aliens, and then at y we
print the length of the list to prove we’ve actually generated the full fleet
of 30 aliens:
{'speed': 'slow', 'color': 'green', 'points': 5}
{'speed': 'slow', 'color': 'green', 'points': 5}
{'speed': 'slow', 'color': 'green', 'points': 5}
{'speed': 'slow', 'color': 'green', 'points': 5}
{'speed': 'slow', 'color': 'green', 'points': 5}
...
Total number of aliens: 30
These aliens all have the same characteristics, but Python considers each
one a separate object, which allows us to modify each alien individually. How might you work with a set of aliens like this? Imagine that one
aspect of a game has some aliens changing color and moving faster as the
game progresses. When it’s time to change colors, we can use a
for loop and
an
if statement to change the color of aliens. For example, to change the
first three aliens to yellow, medium-speed aliens worth 10 points each, we
could do this:
# Make an empty list for storing aliens.
aliens = []
# Make 30 green aliens.
for alien_number in range (0,30):
new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
aliens.append(new_alien)

for alien in aliens[0:3]:
if alien['color'] == 'green':
alien['color'] = 'yellow'
alien['speed'] = 'medium'
alien['points'] = 10

# Show the first 5 aliens:
for alien in aliens[0:5]:
print(alien)
print("...")
Because we want to modify the first three aliens, we loop through a
slice that includes only the first three aliens. All of the aliens are green now
but that won’t always be the case, so we write an
if statement to make sure

Dictionaries 111
we’re only modifying green aliens. If the alien is green, we change the color
to
'yellow' , the speed to 'medium' , and the point value to 10, as shown in the
{'speed': 'medium', 'color': 'yellow', 'points': 10}
{'speed': 'medium', 'color': 'yellow', 'points': 10}
{'speed': 'medium', 'color': 'yellow', 'points': 10}
{'speed': 'slow', 'color': 'green', 'points': 5}
{'speed': 'slow', 'color': 'green', 'points': 5}
...
You could expand this loop by adding an elif block that turns yellow
aliens into red, fast-moving ones worth 15 points each. Without showing the
entire program again, that loop would look like this:
for alien in aliens[0:3]:
if alien['color'] == 'green':
alien['color'] = 'yellow'
alien['speed'] = 'medium'
alien['points'] = 10
elif alien['color'] == 'yellow':
alien['color'] = 'red'
alien['speed'] = 'fast'
alien['points'] = 15
It’s common to store a number of dictionaries in a list when each dic -
tionar y contains many kinds of information about one object. For example,
you might create a dictionar y for each user on a website, as we did in user.py
on page 103, and store the individual dictionaries in a list called
users . All
of the dictionaries in the list should have an identical structure so you can
loop through the list and work with each dictionar y object in the same way.
A List in a Dictionary
Rather than putting a dictionar y inside a list, it’s sometimes useful to put
a list inside a dictionar y. For example, consider how you might describe a
pizza that someone is ordering. If you were to use only a list, all you could
really store is a list of the pizza’s toppings. With a dictionar y, a list of top -
pings can be just one aspect of the pizza you’re describing. In the following example, two kinds of information are stored for each
pizza: a type of crust and a list of toppings. The list of toppings is a value
associated with the key
'toppings' . To use the items in the list, we give the
name of the dictionar y and the key
'toppings' , as we would any value in the
dictionar y. Instead of returning a single value, we get a list of toppings:
pizza.py # Store information about a pizza being ordered.
u pizza = { 'crust': 'thick',
'toppings': ['mushrooms', 'extra cheese'],
}

112 Chapter 6
# Summarize the order.
v print("You ordered a " + pizza['crust'] + "-crust pizza " + "with the following toppings:")
w for topping in pizza['toppings']: print("\t" + topping)
We begin at u with a dictionar y that holds information about a pizza
that has been ordered. One key in the dictionar y is
'crust' , and the associ -
ated value is the string
'thick' . The next key, 'toppings' , has a list as its value
that stores all requested toppings. At v we summarize the order before
building the pizza. To print the toppings, we write a
for loop w . To access
the list of toppings, we use the key
'toppings' , and Python grabs the list of
toppings from the dictionar y. The following output summarizes the pizza that we plan to build:
You ordered a thick-crust pizza with the following toppings:
mushrooms
extra cheese
You can nest a list inside a dictionar y any time you want more than
one value to be associated with a single key in a dictionar y. In the earlier
example of favorite programming languages, if we were to store each
person’s responses in a list, people could choose more than one favorite
language. When we loop through the dictionar y, the value associated with
each person would be a list of languages rather than a single language.
Inside the dictionar y’s
for loop, we use another for loop to run through
the list of languages associated with each person:
u favorite_languages = {
'jen': ['python', 'ruby'],
'sarah': ['c'],
'edward': ['ruby', 'go'],
}
v for name, languages in favorite_languages.items(): print("\n" + name.title() + "'s favorite languages are:")
w for language in languages: print("\t" + language.title())
As you can see at u the value associated with each name is now a list.
Notice that some people have one favorite language and others have
multiple favorites. When we loop through the dictionar y at v , we use the
variable name
languages to hold each value from the dictionar y, because we
know that each value will be a list. Inside the main dictionar y loop, we use
favorite_
languages.py

Dictionaries 113
another for loop w to run through each person’s list of favorite languages.
Now each person can list as many favorite languages as they like:
Jen's favorite languages are:
Python
Ruby
Sarah's favorite languages are:
C
Phil's favorite languages are:
Python
Edward's favorite languages are:
Ruby
Go
To refine this program even further, you could include an if st atement
at the beginning of the dictionar y’s
for loop to see whether each person has
more than one favorite language by examining the value of
len(languages) .
If a person has more than one favorite, the output would stay the same. If
the person has only one favorite language, you could change the wording to
reflect that. For example, you could say
Sarah's favorite language is C .
note You should not nest lists and dictionaries too deeply. If you’re nesting items much
deeper than what you see in the preceding examples or you’re working with someone
else’s code with significant levels of nesting, most likely a simpler way to solve the
problem exists.
A Dictionary in a Dictionary
You can nest a dictionar y inside another dictionar y, but your code can get
complicated quickly when you do. For example, if you have several users
for a website, each with a unique username, you can use the usernames as
the keys in a dictionar y. You can then store information about each user by
using a dictionar y as the value associated with their username. In the fol -
lowing listing, we store three pieces of information about each user: their
first name, last name, and location. We’ll access this information by looping
through the usernames and the dictionar y of information associated with
many_users.py users = {
'aeinstein': {
'first': 'albert',
'last': 'einstein',
'location': 'princeton',
},

114 Chapter 6
'mcurie': {
'first': 'marie',
'last': 'curie',
'location': 'paris',
},
}
u for username, user_info in users.items():
w full_name = user_info['first'] + " " + user_info['last'] location = user_info['location']
x print("\tFull name: " + full_name.title()) print("\tLocation: " + location.title())
We first define a dictionar y called users with two keys: one each for the
'aeinstein' and 'mcurie' . The value associated with each key is
a dictionar y that includes each user’s first name, last name, and location.
At u we loop through the
users dictionar y. Python stores each key in the
variable
into the variable
user_info . Once inside the main dictionar y loop, we print
At w we start accessing the inner dictionar y. The variable
user_info ,
which contains the dictionar y of user information, has three keys:
'first' ,
'last' , and 'location' . We use each key to generate a neatly formatted full
name and location for each person, and then print a summar y of what we
know about each user x :
Full name: Albert Einstein
Location: Princeton
Full name: Marie Curie
Location: Paris
Notice that the structure of each user’s dictionar y is identical. Although
not required by Python, this structure makes nested dictionaries easier to
work with. If each user’s dictionar y had different keys, the code inside the
for loop would be more complicated.
t ry It y ourself
6 - 7. People: Start with the program you wrote for Exercise 6 -1 (page 102) .
Make two new dictionaries representing different people, and store all three
dictionaries in a list called
people . Loop through your list of people . As you
loop through the list, print everything you know about each person .

Dictionaries 115
6-8. Pets: Make several dictionaries, where the name of each dictionary is the
name of a pet . In each dictionary, include the kind of animal and the owner’s
name . Store these dictionaries in a list called
pets . Next, loop through your list
and as you do print everything you know about each pet .
6 - 9. Favorite Places: Make a dictionary called
favorite_places . Think of three
names to use as keys in the dictionary, and store one to three favorite places
for each person . To make this exercise a bit more interesting, ask some friends
to name a few of their favorite places . Loop through the dictionary, and print
each person’s name and their favorite places .
6 -10 . Favorite Numbers: Modify your program from Exercise 6 -2 (page 102) so
each person can have more than one favorite number . Then print each person’s
name along with their favorite numbers .
6 -11 . Cities: Make a dictionary called
cities . Use the names of three cities as
keys in your dictionary . Create a dictionary of information about each city and
include the country that the city is in, its approximate population, and one fact
about that city . The keys for each city’s dictionary should be something like
country , population , and fact . Print the name of each city and all of the infor -
mation you have stored about it .
6 -12 . Extensions: We’re now working with examples that are complex enough
that they can be extended in any number of ways . Use one of the example pro-
grams from this chapter, and extend it by adding new keys and values, chang -
ing the context of the program or improving the formatting of the output .
summary
In this chapter you learned how to define a dictionar y and how to work
with the information stored in a dictionar y. You learned how to access and
modify individual elements in a dictionar y, and how to loop through all of
the information in a dictionar y. You learned to loop through a dictionar y’s
key-value pairs, its keys, and its values. You also learned how to nest multiple
dictionaries in a list, nest lists in a dictionar y, and nest a dictionar y inside
a dictionar y.
In the next chapter you’ll learn about
while loops and how to accept
input from people who are using your programs. This will be an exciting
chapter, because you’ll learn to make all of your programs interactive:
they’ll be able to respond to user input.

7
user In Put an D whIle l ooP s
Most programs are written to solve an end
user’s problem. To do so, you usually need
to get some information from the user. For a
simple example, let’s say someone wants to find
out whether they’re old enough to vote. If you write a
program to answer this question, you need to know the user’s age before
you can provide an answer. The program will need to ask the user to enter,
or input , their age; once the program has this input, it can compare it to the
voting age to determine if the user is old enough and then report the result. In this chapter you’ll learn how to accept user input so your program
can then work with it. When your program needs a name, you’ll be able
to prompt the user for a name. When your program needs a list of names,
you’ll be able to prompt the user for a series of names. To do this, you’ll use
the
input() function.
You’ll also learn how to keep programs running as long as users want
them to, so they can enter as much information as they need to; then, your
program can work with that information. You’ll use Python’s
while loop to
keep programs running as long as certain conditions remain true.

118 Chapter 7
With the ability to work with user input and the ability to control how
long your programs run, you’ll be able to write fully interactive programs.
h ow the input() f unction works
The input() function pauses your program and waits for the user to enter
some text. Once Python receives the user’s input, it stores it in a variable to
make it convenient for you to work with. For example, the following program asks the user to enter some text,
then displays that message back to the user:
parrot.py message = input("Tell me something, and I will repeat it back to you: "
)
print(message)
The input() function takes one argument: the prompt , or instructions,
that we want to display to the user so they know what to do. In this example,
when Python runs the first line, the user sees the prompt
Tell me something,
and I will repeat it back to you:
. The program waits while the user enters
their response and continues after the user presses
enter . The response is
stored in the variable
message , then print(message) displays the input back to
the user:
Tell me something, and I will repeat it back to you: Hello everyone!
Hello everyone!
note Sublime Text doesn’t run programs that prompt the user for input. You can use Sublime
Text to write programs that prompt for input, but you’ll need to run these programs from
a terminal. See “Running Python Programs from a Terminal” on page 16.
Writing Clear Prompts
Each time you use the input() function, you should include a clear, easy-to-
follow prompt that tells the user exactly what kind of information you’re
looking for. Any statement that tells the user what to enter should work. For
example:
print("Hello, " + name + "!")
Add a space at the end of your prompts (after the colon in the preced -
ing example) to separate the prompt from the user’s response and to make
it clear to your user where to enter their text. For example:
Hello, Eric!
Sometimes you’ll want to write a prompt that’s longer than one line. For
example, you might want to tell the user why you’re asking for certain input.

User Input and while Loops 119
You can store your prompt in a variable and pass that variable to the input()
function. This allows you to build your prompt over several lines, then write
a clean
input() st atement.
greeter.py prompt = "If you tell us who you are, we can personalize the messages yo
u see."
prompt += "\nWhat is your first name? "
name = input(prompt)
print("\nHello, " + name + "!")
This example shows one way to build a multi-line string. The first line
stores the first part of the message in the variable
prompt . In the second line,
the operator
+= takes the string that was stored in prompt and adds the new
string onto the end. The prompt now spans two lines, again with space after the question
mark for clarity:
If you tell us who you are, we can personalize the messages you see.
What is your first name? Eric
Hello, Eric!
Using int() to Accept Numerical Input
When you use the input() function, Python interprets ever ything the user
enters as a string. Consider the following interpreter session, which asks for
the user’s age:
>>> age = input("How old are you? ")
How old are you? 21
>>> age
'21'
The user enters the number 21, but when we ask Python for the value of
age , it returns '21' , the string representation of the numerical value entered.
We know Python interpreted the input as a string because the number is now
enclosed in quotes. If all you want to do is print the input, this works well. But
if you tr y to use the input as a number, you’ll get an error:
>>> age = input("How old are you? ")
How old are you? 21
u >>> age >= 18 Traceback (most recent call last):
File "", line 1, in
v TypeError: unorderable types: str() >= int()
When you tr y to use the input to do a numerical comparison u , P ython
produces an error because it can’t compare a string to an integer: the string
'21' that’s stored in age can’t be compared to the numerical value 18 v .

12 0 Chapter 7
We can resolve this issue by using the int() function, which tells
Python to treat the input as a numerical value. The
int() function con -
verts a string representation of a number to a numerical representation,
as shown here:
>>> age = input("How old are you? ")
How old are you? 21
u >>> age = int(age) >>> age >= 18
True
In this example, when we enter 21 at the prompt, Python interprets the
number as a string, but the value is then converted to a numerical represen -
tation by
int() u . Now Python can run the conditional test: it compares age
(which now contains the numerical value 21) and
18 to see if age is greater
than or equal to 18. This test evaluates to
True .
How do you use the
int() function in an actual program? Consider a
program that determines whether people are tall enough to ride a roller
coaster:
rollercoaster.py height = input("How tall are you, in inches? ")
height = int(height)
if height >= 36:
print("\nYou're tall enough to ride!")
else:
print("\nYou'll be able to ride when you're a little older.")
The program can compare height to 36 because height = int(height)
converts the input value to a numerical representation before the compari -
son is made. If the number entered is greater than or equal to 36, we tell
the user that they’re tall enough:
How tall are you, in inches? 71
You're tall enough to ride!
When you use numerical input to do calculations and comparisons,
be sure to convert the input value to a numerical representation first.
The Modulo Operator
A useful tool for working with numerical information is the modulo operator ( %),
which divides one number by another number and returns the remainder:
>>> 4 % 3
1
>>> 5 % 3
2
>>> 6 % 3
0

User Input and while Loops 121
>>> 7 % 3
1
The modulo operator doesn’t tell you how many times one number fits
into another; it just tells you what the remainder is. When one number is divisible by another number, the remainder is 0,
so the modulo operator always returns 0. You can use this fact to determine
if a number is even or odd:
even_or_odd.py number = input("Enter a number, and I'll tell you if it's even or odd:
")
number = int(number)
if number % 2 == 0:
print("\nThe number " + str(number) + " is even.")
else:
print("\nThe number " + str(number) + " is odd.")
Even numbers are always divisible by two, so if the modulo of a number
and two is zero (here,
if number % 2 == 0 ) the number is even. Other wise,
it’s o d d .
Enter a number, and I'll tell you if it's even or odd: 42
The number 42 is even.
Accepting Input in Python 2.7
If you’re using Python 2.7, you should use the raw_input() function when
prompting for user input. This function interprets all input as a string, just
as
input() does in Python 3.
Python 2.7 has an
input() function as well, but this function interprets
the user’s input as Python code and attempts to run the input. At best you’ll
get an error that Python doesn’t understand the input; at worst you’ll run
code that you didn’t intend to run. If you’re using Python 2.7, use
raw_input()
input() .
t ry It y ourself
7-1. Rental Car: Write a program that asks the user what kind of rental car they
would like . Print a message about that car, such as “Let me see if I can find you
a S u b a r u .”
7-2 . Restaurant Seating: Write a program that asks the user how many people
are in their dinner group . If the answer is more than eight, print a message say -
ing they’ll have to wait for a table . Otherwise, report that their table is ready .
7- 3 . Multiples of Ten: Ask the user for a number, and then report whether the
number is a multiple of 10 or not .

12 2 Chapter 7
Introducing while loops
The for loop takes a collection of items and executes a block of code once
for each item in the collection. In contrast, the
while loop runs as long as,
or while , a certain condition is true.
The while Loop in Action
You can use a while loop to count up through a series of numbers. For
example, the following
while loop counts from 1 to 5:
counting.py current_number = 1
while current_number <= 5:
print(current_number)
current_number += 1
In the first line, we start counting from 1 by setting the value of
current_number to 1. The while loop is then set to keep running as long
as the value of
current_number is less than or equal to 5. The code inside
the loop prints the value of
current_number and then adds 1 to that value
w ith
current_number += 1 . (The += operator is shorthand for current_number =
current_number + 1
.)
Python repeats the loop as long as the condition
current_number <= 5
is true. Because 1 is less than 5, Python prints 1 and then adds 1, mak -
ing the current number
2. Because 2 is less than 5, Python prints 2
and adds 1 again, making the current number
3, and so on. Once the
value of
current_number is greater than 5, the loop stops running and the
program ends:
1
2
3
4
5
The programs you use ever y day most likely contain while loops. For
example, a game needs a
while loop to keep running as long as you want
to keep playing, and so it can stop running as soon as you ask it to quit.
Programs wouldn’t be fun to use if they stopped running before we told
them to or kept running even after we wanted to quit, so
while loops are
quite useful.
Letting the User Choose When to Quit
We can make the parrot.py program run as long as the user wants by putting
most of the program inside a
while loop. We’ll define a quit value and then
keep the program running as long as the user has not entered the quit value:
parrot.py u prompt = "\nTell me something, and I will repeat it back to you:"
prompt += "\nEnter 'quit' to end the program. "

User Input and while Loops 12 3
v message = ""
w while message != 'quit': message = input(prompt)
print(message)
At u , we define a prompt that tells the user their two options: enter -
ing a message or entering the quit value (in this case,
'quit' ). Then we set
up a variable
message v to store whatever value the user enters. We define
message as an empty string, "", so Python has something to check the first
time it reaches the
while line. The first time the program runs and Python
reaches the
while statement, it needs to compare the value of message to
'quit' , but no user input has been entered yet. If Python has nothing to
compare, it won’t be able to continue running the program. To solve this
problem, we make sure to give
message an initial value. Although it’s just an
empty string, it will make sense to Python and allow it to perform the com -
parison that makes the
while loop work. This while loop w runs as long as
the value of
message is not 'quit' .
The first time through the loop,
message is just an empty string, so Python
enters the loop. At
message = input(prompt) , Python displays the prompt and
waits for the user to enter their input. Whatever they enter is stored in
message
and printed; then, Python reevaluates the condition in the
while st atement.
As long as the user has not entered the word
'quit' , the prompt is displayed
again and Python waits for more input. When the user finally enters
'quit' ,
Python stops executing the
while loop and the program ends:
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. Hello everyone!
Hello everyone!
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. Hello again.
Hello again.
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. quit
quit
This program works well, except that it prints the word 'quit' as if it
were an actual message. A simple
if test fixes this:
prompt = "\nTell me something, and I will repeat it back to you:"
prompt += "\nEnter 'quit' to end the program. "
message = ""
while message != 'quit':
message = input(prompt)

if message != 'quit':
print(message)

124 Chapter 7
Now the program makes a quick check before displaying the message
and only prints the message if it does not match the quit value:
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. Hello everyone!
Hello everyone!
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. Hello again.
Hello again.
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. quit
Using a Flag
In the previous example, we had the program perform certain tasks while
a given condition was true. But what about more complicated programs in
which many different events could cause the program to stop running? For example, in a game, several different events can end the game.
When the player runs out of ships, their time runs out, or the cities they
were supposed to protect are all destroyed, the game should end. It needs
to end if any one of these events happens. If many possible events might
occur to stop the program, tr ying to test all these conditions in one
while
statement becomes complicated and difficult. For a program that should run only as long as many conditions are true,
you can define one variable that determines whether or not the entire pro -
gram is active. This variable, called a flag , acts as a signal to the program. We
can write our programs so they run while the flag is set to
True and stop run -
ning when any of several events sets the value of the flag to
False . As a result,
our overall
while statement needs to check only one condition: whether or
not the flag is currently
True . Then, all our other tests (to see if an event has
occurred that should set the flag to
False ) can be neatly organized in the rest
of the program. Let’s add a flag to parrot.py from the previous section. This flag, which
we’ll call
active (though you can call it anything), will monitor whether or
not the program should continue running:
prompt = "\nTell me something, and I will repeat it back to you:"
prompt += "\nEnter 'quit' to end the program. "

u active = True
v while active: message = input(prompt)

w if message == 'quit': active = False
x else: print(message)

User Input and while Loops 125
We set the variable active to True u so the program starts in an active
state. Doing so makes the
while statement simpler because no comparison is
while statement itself; the logic is taken care of in other parts of
the program. As long as the
active variable remains True , the loop will con -
tinue running v .
In the
if statement inside the while loop, we check the value of message
once the user enters their input. If the user enters
'quit' w , we set active
to
False , and the while loop stops. If the user enters anything other than
'quit' x , we print their input as a message.
This program has the same output as the previous example where we
placed the conditional test directly in the
while statement. But now that we
have a flag to indicate whether the overall program is in an active state, it
would be easy to add more tests (such as
elif statements) for events that
should cause
active to become False . This is useful in complicated programs
like games in which there may be many events that should each make the
program stop running. When any of these events causes the active flag to
become
False , the main game loop will exit, a Game Over message can be
displayed, and the player can be given the option to play again.
Using break to Exit a Loop
To exit a while loop immediately without running any remaining code in the
loop, regardless of the results of any conditional test, use the
break st atement.
The
break statement directs the flow of your program; you can use it to con -
trol which lines of code are executed and which aren’t, so the program only
executes code that you want it to, when you want it to. For example, consider a program that asks the user about places they’ve
visited. We can stop the
while loop in this program by calling break as soon
as the user enters the
'quit' value:
cities.py prompt = "\nPlease enter the name of a city you have visited:"
prompt += "\n(Enter 'quit' when you are finished.) "
u while True: city = input(prompt)

if city == 'quit':
break
else:
print("I'd love to go to " + city.title() + "!")
A loop that starts with while True u will run forever unless it reaches a
break statement. The loop in this program continues asking the user to enter
the names of cities they’ve been to until they enter
'quit' . W hen they enter
'quit' , the break statement runs, causing Python to exit the loop:
Please enter the name of a city you have visited:
(Enter 'quit' when you are finished.) New York
I'd love to go to New York!

126 Chapter 7
Please enter the name of a city you have visited:
(Enter 'quit' when you are finished.) San Francisco
I'd love to go to San Francisco!
Please enter the name of a city you have visited:
(Enter 'quit' when you are finished.) quit
note You can use the break statement in any of Python’s loops. For example, you could use
break to quit a for loop that’s working through a list or a dictionary.
Using continue in a Loop
Rather than breaking out of a loop entirely without executing the rest of its
code, you can use the
loop based on the result of a conditional test. For example, consider a loop
that counts from 1 to 10 but prints only the odd numbers in that range:
counting.py current_number = 0
while current_number < 10:
u current_number += 1 if current_number % 2 == 0:
continue

print(current_number)
First we set current_number to 0. Because it’s less than 10, Python
enters the
while loop. Once inside the loop, we increment the count by 1
at u, so
current_number is 1. The if statement then checks the modulo of
current_number and 2. If the modulo is 0 (which means current_number is
divisible by 2), the
continue statement tells Python to ignore the rest of
the loop and return to the beginning. If the current number is not divis -
ible by 2, the rest of the loop is executed and Python prints the current
number:
1
3
5
7
9
Avoiding Infinite Loops
Ever y while loop needs a way to stop running so it won’t continue to run for -
ever. For example, this counting loop should count from 1 to 5:
counting.py x = 1
while x <= 5:
print(x)
x += 1

User Input and while Loops 12 7
But if you accidentally omit the line x += 1 (as shown next), the loop
will run forever:
# This loop runs forever!
x = 1
while x <= 5:
print(x)
Now the value of x will start at 1 but never change. As a result, the con -
ditional test
x <= 5 will always evaluate to True and the while loop will run
forever, printing a series of 1s, like this:
1
1
1
1
--snip--
Ever y programmer accidentally writes an infinite while loop from time
to time, especially when a program’s loops have subtle exit conditions. If
your program gets stuck in an infinite loop, press
ctr l - C or just close the
terminal window displaying your program’s output. To avoid writing infinite loops, test ever y
while loop and make sure
the loop stops when you expect it to. If you want your program to end
when the user enters a certain input value, run the program and enter
that value. If the program doesn’t end, scrutinize the way your program
handles the value that should cause the loop to exit. Make sure at least
one part of the program can make the loop’s condition
False or cause it
to reach a
break st atement.
note Some editors, such as Sublime Text, have an embedded output window. This can
make it difficult to stop an infinite loop, and you might have to close the editor to
end the loop.
try It y ourself
7- 4 . Pizza Toppings: Write a loop that prompts the user to enter a series of
pizza toppings until they enter a
'quit' value . As they enter each topping,
print a message saying you’ll add that topping to their pizza .
7- 5 . Movie Tickets: A movie theater charges different ticket prices depending on
a person’s age . If a person is under the age of 3, the ticket is free; if they are
between 3 and 12, the ticket is $10; and if they are over age 12, the ticket is$15 . Write a loop in which you ask users their age, and then tell them the cost
of their movie ticket .
(continued)

12 8 Chapter 7
7- 6 . Three Exits: Write different versions of either Exercise 7-4 or Exercise 7-5
that do each of the following at least once:
• Use a conditional test in the
while statement to stop the loop .
• Use an
active variable to control how long the loop runs .
• Use a
break statement to exit the loop when the user enters a 'quit' value .
7 - 7. Infinity: Write a loop that never ends, and run it . (To end the loop, press
ctrl - C or close the window displaying the output .)
u sing a while l oop with lists and dictionaries
So far, we’ve worked with only one piece of user information at a time. We
received the user’s input and then printed the input or a response to it.
The next time through the
while loop, we’d receive another input value
and respond to that. But to keep track of many users and pieces of informa -
tion, we’ll need to use lists and dictionaries with our
while loops.
A
for loop is effective for looping through a list, but you shouldn’t modify
a list inside a
for loop because Python will have trouble keeping track of the
items in the list. To modify a list as you work through it, use a
while loop.
Using
while loops with lists and dictionaries allows you to collect, store, and
organize lots of input to examine and report on later.
Moving Items from One List to Another
Consider a list of newly registered but unverified users of a website. A fter
we verify these users, how can we move them to a separate list of confirmed
users? One way would be to use a
while loop to pull users from the list of
unconfirmed users as we verify them and then add them to a separate list of
confirmed users. Here’s what that code might look like:
users.py # and an empty list to hold confirmed users.
u unconfirmed_users = ['alice', 'brian', 'candace'] confirmed_users = []
# Verify each user until there are no more unconfirmed users.
# Move each verified user into the list of confirmed users.
v while unconfirmed_users:
w current_user = unconfirmed_users.pop()
print("Verifying user: " + current_user.title())
x confirmed_users.append(current_user)

User Input and while Loops 12 9
# Display all confirmed users.
print("\nThe following users have been confirmed:")
for confirmed_user in confirmed_users:
print(confirmed_user.title())
We begin with a list of unconfirmed users at u (Alice, Brian, and
Candace) and an empty list to hold confirmed users. The
while loop at v
runs as long as the list
unconfirmed_users is not empty. Within this loop, the
pop() function at w removes unverified users one at a time from the end of
unconfirmed_users . Here, because Candace is last in the unconfirmed_users list,
her name will be the first to be removed, stored in
to the
confirmed_users list at x . Next is Brian, then Alice.
We simulate confirming each user by printing a verification message
and then adding them to the list of confirmed users. As the list of uncon -
firmed users shrinks, the list of confirmed users grows. When the list of
unconfirmed users is empty, the loop stops and the list of confirmed users
is printed:
Verifying user: Candace
Verifying user: Brian
Verifying user: Alice
The following users have been confirmed:
Candace
Brian
Alice
Removing All Instances of Specific Values from a List
In Chapter 3 we used remove() to remove a specific value from a list. The
remove() function worked because the value we were interested in appeared
only once in the list. But what if you want to remove all instances of a value
from a list? Say you have a list of pets with the value
'cat' repeated several times. To
remove all instances of that value, you can run a
while loop until 'cat' is no
longer in the list, as shown here:
pets.py pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
print(pets)
while 'cat' in pets:
pets.remove('cat')

print(pets)
We start with a list containing multiple instances of 'cat' . A fter printing
the list, Python enters the
while loop because it finds the value 'cat' in the list

13 0 Chapter 7
at least once. Once inside the loop, Python removes the first instance of 'cat' ,
returns to the
while line, and then reenters the loop when it finds that 'cat' is
still in the list. It removes each instance of
'cat' until the value is no longer in
the list, at which point Python exits the loop and prints the list again:
['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
['dog', 'dog', 'goldfish', 'rabbit']
Filling a Dictionary with User Input
You can prompt for as much input as you need in each pass through a while
loop. Let’s make a polling program in which each pass through the loop
prompts for the participant’s name and response. We’ll store the data we
gather in a dictionar y, because we want to connect each response with a
particular user:
mountain_ responses = {}
poll.py # Set a flag to indicate that polling is active.
polling_active = True
while polling_active:
# Prompt for the person's name and response.
u name = input("\nWhat is your name? ") response = input("Which mountain would you like to climb someday? "
)

# Store the response in the dictionary:
v responses[name] = response
# Find out if anyone else is going to take the poll.
w repeat = input("Would you like to let another person respond? (ye
s/ no) ") if repeat == 'no':
polling_active = False

# Polling is complete. Show the results.
print("\n--- Poll Results ---")
x for name, response in responses.items(): print(name + " would like to climb " + response + ".")
The program first defines an empty dictionar y ( responses ) and sets a flag
(
polling_active ) to indicate that polling is active. As long as polling_active is
True , Python will run the code in the while loop.
Within the loop, the user is prompted to enter their username and a
mountain they’d like to climb u . That information is stored in the
responses
dictionar y v, and the user is asked whether or not to keep the poll run -
ning w . If they enter
yes , the program enters the while loop again. If they
enter
no, the polling_active flag is set to False , the while loop stops running,
and the final code block at x displays the results of the poll.

User Input and while Loops 131
If you run this program and enter sample responses, you should see
output like this:
Which mountain would you like to climb someday? Denali
Would you like to let another person respond? (yes/ no) yes

Which mountain would you like to climb someday? Devil's Thumb
Would you like to let another person respond? (yes/ no) no
--- Poll Results ---
Lynn would like to climb Devil's Thumb.
Eric would like to climb Denali.
t ry It y ourself
7- 8 . Deli: Make a list called sandwich_orders and fill it with the names of vari -
ous sandwiches . Then make an empty list called
finished_sandwiches . Loop
through the list of sandwich orders and print a message for each order, such
as
of finished sandwiches . After all the sandwiches have been made, print a
message listing each sandwich that was made .
7- 9. No Pastrami: Using the list
sandwich_orders from Exercise 7-8, make sure
the sandwich
'pastrami' appears in the list at least three times . Add code
near the beginning of your program to print a message saying the deli has
run out of pastrami, and then use a
while loop to remove all occurrences of
'pastrami' from sandwich_orders . Make sure no pastrami sandwiches end up
in
finished_sandwiches .
7-10 . Dream Vacation: Write a program that polls users about their dream
vacation . Write a prompt similar to If you could visit one place in the world,
where would you go? Include a block of code that prints the results of the poll .
summary
In this chapter you learned how to use input() to allow users to provide
their own information in your programs. You learned to work with both
text and numerical input and how to use
while loops to make your programs
run as long as your users want them to. You saw several ways to control the
flow of a
while loop by setting an active flag, using the break statement, and

132 Chapter 7
using the continue statement. You learned how to use a while loop to move
items from one list to another and how to remove all instances of a value
from a list. You also learned how
while loops can be used with dictionaries.
In Chapter 8 you’ll learn about functions . Functions allow you to break
your programs into small parts, each of which does one specific job. You
can call a function as many times as you want, and you can store your
functions in separate files. By using functions, you’ll be able to write more
efficient code that’s easier to troubleshoot and maintain and that can be
reused in many different programs.

8
funC tIons
In this chapter you’ll learn to write
functions , which are named blocks of code
that are designed to do one specific job.
When you want to perform a particular task
that you’ve defined in a function, you call the name
of the function responsible for it. If you need to
need to type all the code for the same task again and again; you just call
the function dedicated to handling that task, and the call tells Python to
run the code inside the function. You’ll find that using functions makes
your programs easier to write, read, test, and fix. In this chapter you’ll also learn ways to pass information to functions.
You’ll learn how to write certain functions whose primar y job is to display
information and other functions designed to process data and return a
value or set of values. Finally, you’ll learn to store functions in separate files
called modules to help organize your main program files.

13 4 Chapter 8
defining a f unction
Here’s a simple function named greet_user() that prints a greeting:
greeter.py u def greet_user():
v """Display a simple greeting."""
w print("Hello!")
x greet_user()
This example shows the simplest structure of a function. The line at u
uses the key word
def to inform Python that you’re defining a function. This
is the function definition , which tells Python the name of the function and, if
applicable, what kind of information the function needs to do its job. The
parentheses hold that information. In this case, the name of the function
is
greet_user() , and it needs no information to do its job, so its parentheses
are empty. (Even so, the parentheses are required.) Finally, the definition
ends in a colon. Any indented lines that follow
def greet_user(): make up the body of
the function. The text at v is a comment called a docstring , which describes
what the function does. Docstrings are enclosed in triple quotes, which
Python looks for when it generates documentation for the functions in your
programs. The line
print("Hello!") w is the only line of actual code in the body
of this function, so
greet_user() has just one job: print("Hello!") .
When you want to use this function, you call it. A function call tells
Python to execute the code in the function. To call a function, you write
the name of the function, followed by any necessar y information in paren -
theses, as shown at x . Because no information is needed here, calling our
function is as simple as entering
greet_user() . As expected, it prints Hello! :
Hello!
Passing Information to a Function
Modified slightly, the function greet_user() can not only tell the user Hello!
but also greet them by name. For the function to do this, you enter
in the parentheses of the function’s definition at
def greet_user() . By add -
ing
username here you allow the function to accept any value of username you
specify. The function now expects you to provide a value for
time you call it. When you call
greet_user() , you can pass it a name, such as
'jesse' , inside the parentheses:
"""Display a simple greeting."""
print("Hello, " + username.title() + "!")

greet_user('jesse')

Functions 135
Enter ing greet_user('jesse') calls greet_user() and gives the function the
information it needs to execute the
print statement. The function accepts
the name you passed it and displays the greeting for that name:
Hello, Jesse!
Likewise, entering greet_user('sarah') calls greet_user() , passes it 'sarah' ,
and prints
Hello, Sarah! You can call greet_user() as often as you want and
pass it any name you want to produce a predictable output ever y time.
Arguments and Parameters
In the preceding greet_user() function, we defined greet_user() to require a
value for the variable
username . Once we called the function and gave it the
information (a person’s name), it printed the right greeting. The variable
username in the definition of greet_user() is an example of a
parameter , a piece of information the function needs to do its job. The value
'jesse' in greet_user('jesse') is an example of an argument . An argument
is a piece of information that is passed from a function call to a function.
When we call the function, we place the value we want the function to work
with in parentheses. In this case the argument
'jesse' was passed to the
function
greet_user() , and the value was stored in the parameter username .
note People sometimes speak of arguments and parameters interchangeably. Don’t be sur -
prised if you see the variables in a function definition referred to as arguments or the
variables in a function call referred to as parameters.
t ry It y ourself
8-1. Message: Write a function called display_message() that prints one sen -
tence telling everyone what you are learning about in this chapter . Call the
function, and make sure the message displays correctly .
8-2. Favorite Book: Write a function called
favorite_book() that accepts one
parameter,
title . The function should print a message, such as One of my
favorite books is Alice in Wonderland
. Call the function, making sure to
include a book title as an argument in the function call .
Passing arguments
Because a function definition can have multiple parameters, a function call
may need multiple arguments. You can pass arguments to your functions
in a number of ways. You can use positional arguments , which need to be in

13 6 Chapter 8
the same order the parameters were written; keyword arguments, where each
argument consists of a variable name and a value; and lists and dictionaries
of values. Let’s look at each of these in turn.
Positional Arguments
When you call a function, Python must match each argument in the func -
tion call with a parameter in the function definition. The simplest way to
do this is based on the order of the arguments provided. Values matched
up this way are called positional arguments .
To see how this works, consider a function that displays information
about pets. The function tells us what kind of animal each pet is and the
pet’s name, as shown here:
pets.py u def describe_pet(animal_type, pet_name):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".
")

v describe_pet('hamster', 'harry')
The definition shows that this function needs a type of animal and the
animal’s name u . When we call
describe_pet() , we need to provide an ani -
mal type and a name, in that order. For example, in the function call, the
argument
'hamster' is stored in the parameter animal_type and the argu -
ment
'harry' is stored in the parameter pet_name v . In the function body,
these two parameters are used to display information about the pet being
described. The output describes a hamster named Harr y:
I have a hamster.
My hamster's name is Harry.
Multiple Function Calls
You can call a function as many times as needed. Describing a second, dif -
ferent pet requires just one more call to
describe_pet() :
def describe_pet(animal_type, pet_name):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".
")

describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')
In this second function call, we pass describe_pet() the arguments 'dog'
and
'willie' . As with the previous set of arguments we used, Python matches
'dog' with the parameter animal_type and 'willie' with the parameter pet_name .

Functions 137
As before, the function does its job, but this time it prints values for a dog
named Willie. Now we have a hamster named Harr y and a dog named Willie:
I have a hamster.
My hamster's name is Harry.
I have a dog.
My dog's name is Willie.
Calling a function multiple times is a ver y efficient way to work. The
code describing a pet is written once in the function. Then, anytime you
want to describe a new pet, you call the function with the new pet’s infor -
mation. Even if the code for describing a pet were to expand to ten lines,
you could still describe a new pet in just one line by calling the function
again. You can use as many positional arguments as you need in your func -
tions. Python works through the arguments you provide when calling the
function and matches each one with the corresponding parameter in
the function’s definition.
Order Matters in Positional arguments
You can get unexpected results if you mix up the order of the arguments in
a function call when using positional arguments:
def describe_pet(animal_type, pet_name):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".
")

describe_pet('harry', 'hamster')
In this function call we list the name first and the type of animal second.
Because the argument
'harry' is listed first this time, that value is stored in
the parameter
animal_type . Likew ise, 'hamster' is stored in pet_name . Now we
have a “harr y” named “Hamster”:
I have a harry.
My harry's name is Hamster.
If you get funny results like this, check to make sure the order of the
arguments in your function call matches the order of the parameters in the
function’s definition.
Keyword Arguments
A keyword argument is a name-value pair that you pass to a function. You
directly associate the name and the value within the argument, so when you
pass the argument to the function, there’s no confusion (you won’t end up

13 8 Chapter 8
with a harr y named Hamster). Key word arguments free you from having
to worr y about correctly ordering your arguments in the function call, and
they clarify the role of each value in the function call.Let’s rewrite pets.py using key word arguments to call
describe_pet() :
def describe_pet(animal_type, pet_name):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".
")

describe_pet(animal_type='hamster', pet_name='harry')
The function describe_pet() hasn’t changed. But when we call the func -
tion, we explicitly tell Python which parameter each argument should be
matched with. When Python reads the function call, it knows to store the
argument
'hamster' in the parameter animal_type and the argument 'harry'
in
pet_name . The output correctly shows that we have a hamster named
H a r r y. The order of key word arguments doesn’t matter because Python
knows where each value should go. The following two function calls are
equivalent :
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster')
note When you use keyword arguments, be sure to use the exact names of the parameters in
the function’s definition.
Default Values
When writing a function, you can define a default value for each parameter.
If an argument for a parameter is provided in the function call, Python uses
the argument value. If not, it uses the parameter’s default value. So when
you define a default value for a parameter, you can exclude the correspond -
ing argument you’d usually write in the function call. Using default values
can simplify your function calls and clarify the ways in which your functions
are typically used. For example, if you notice that most of the calls to
describe_pet() are
being used to describe dogs, you can set the default value of
animal_type to
'dog' . Now anyone calling describe_pet() for a dog can omit that information:
def describe_pet(pet_name, animal_type='dog'):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".
")
describe_pet(pet_name='willie')

Functions 139
We changed the definition of describe_pet() to include a default value,
'dog' , for animal_type . Now when the function is called with no animal_type
specified, Python knows to use the value
'dog' for this parameter:
I have a dog.
My dog's name is Willie.
Note that the order of the parameters in the function definition had
to be changed. Because the default value makes it unnecessar y to specify a
type of animal as an argument, the only argument left in the function call
is the pet’s name. Python still interprets this as a positional argument, so if
the function is called with just a pet’s name, that argument will match up
with the first parameter listed in the function’s definition. This is the rea -
son the first parameter needs to be
pet_name .
The simplest way to use this function now is to provide just a dog’s
name in the function call:
describe_pet('willie')
This function call would have the same output as the previous example.
The only argument provided is
'willie' , so it is matched up with the first
parameter in the definition,
pet_name . Because no argument is provided for
animal_type , Python uses the default value 'dog' .
To describe an animal other than a dog, you could use a function call
like this:
describe_pet(pet_name='harry', animal_type='hamster')
Because an explicit argument for animal_type is provided, Python will
ignore the parameter’s default value.
note When you use default values, any parameter with a default value needs to be listed
after all the parameters that don’t have default values. This allows Python to con -
tinue interpreting positional arguments correctly.
Equivalent Function Calls
Because positional arguments, key word arguments, and default values can
all be used together, often you’ll have several equivalent ways to call a func -
tion. Consider the following definition for
describe_pets() with one default
value provided:
def describe_pet(pet_name, animal_type='dog'):
With this definition, an argument always needs to be provided for
pet_name , and this value can be provided using the positional or key word

14 0 Chapter 8
format. If the animal being described is not a dog, an argument for
animal_type must be included in the call, and this argument can also be
specified using the positional or key word format. All of the following calls would work for this function:
# A dog named Willie.
describe_pet('willie')
describe_pet(pet_name='willie')
# A hamster named Harry.
describe_pet('harry', 'hamster')
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet(animal_type='hamster', pet_name='harry')
Each of these function calls would have the same output as the previous
examples.
note It doesn’t really matter which calling style you use. As long as your function calls pro -
duce the output you want, just use the style you find easiest to understand.
Avoiding Argument Errors
When you start to use functions, don’t be surprised if you encounter errors
about unmatched arguments. Unmatched arguments occur when you
provide fewer or more arguments than a function needs to do its work.
For example, here’s what happens if we tr y to call
describe_pet() w ith no
arguments:
def describe_pet(animal_type, pet_name):
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".
")
describe_pet()
Python recognizes that some information is missing from the function
call, and the traceback tells us that:
Traceback (most recent call last):
u File "pets.py", line 6, in
v describe_pet()
w TypeError: describe_pet() missing 2 required positional arguments: 'a
nimal_ type' and 'pet_name'
At u the traceback tells us the location of the problem, allowing us to
look back and see that something went wrong in our function call. At v
the offending function call is written out for us to see. At w the traceback

Functions 141
tells us the call is missing two arguments and reports the names of the miss-
ing arguments. If this function were in a separate file, we could probably
rewrite the call correctly without having to open that file and read the func -
tion code. Python is helpful in that it reads the function’s code for us and tells us
the names of the arguments we need to provide. This is another motiva -
tion for giving your variables and functions descriptive names. If you do,
Python’s error messages will be more useful to you and anyone else who
might use your code. If you provide too many arguments, you should get a similar trace -
definition.
t ry It y ourself
8-3. T-Shirt: Write a function called make_shirt() that accepts a size and the
text of a message that should be printed on the shirt . The function should print
a sentence summarizing the size of the shirt and the message printed on it .
Call the function once using positional arguments to make a shirt . Call the
function a second time using keyword arguments .
8-4. Large Shirts: Modify the
make_shirt() function so that shirts are large
by default with a message that reads I love Python . Make a large shirt and a
medium shirt with the default message, and a shirt of any size with a different
message .
8-5. Cities: Write a function called
describe_city() that accepts the name of
a city and its country . The function should print a simple sentence, such as
Reykjavik is in Iceland . Give the parameter for the country a default value .
Call your function for three different cities, at least one of which is not in the
default country .
return Values
A function doesn’t always have to display its output directly. Instead, it can
process some data and then return a value or set of values. The value the
function returns is called a return value . The
return statement takes a value
from inside a function and sends it back to the line that called the function.
Return values allow you to move much of your program’s grunt work into
functions, which can simplify the body of your program.

142 Chapter 8
Returning a Simple Value
Let’s look at a function that takes a first and last name, and returns a neatly
formatted full name:
formatted_ u def get_formatted_name(first_name, last_name):
name.py """Return a full name, neatly formatted."""
v full_name = first_name + ' ' + last_name
w return full_name.title()
x musician = get_formatted_name('jimi', 'hendrix') print(musician)
The definition of get_formatted_name() takes as parameters a first and last
name u . The function combines these two names, adds a space between
them, and stores the result in
full_name v . The value of full_name is con -
verted to title case, and then returned to the calling line at w .
When you call a function that returns a value, you need to provide a
variable where the return value can be stored. In this case, the returned
value is stored in the variable
musician at x . The output shows a neatly for -
matted name made up of the parts of a person’s name:
Jimi Hendrix
This might seem like a lot of work to get a neatly formatted name when
we could have just written:
print("Jimi Hendrix")
But when you consider working with a large program that needs to
store many first and last names separately, functions like
get_formatted_name()
become ver y useful. You store first and last names separately and then call
this function whenever you want to display a full name.
Making an Argument Optional
Sometimes it makes sense to make an argument optional so that people
using the function can choose to provide extra information only if they
want to. You can use default values to make an argument optional. For example, say we want to expand
get_formatted_name() to handle
middle names as well. A first attempt to include middle names might look
like this:
def get_formatted_name(first_name, middle_name, last_name):
"""Return a full name, neatly formatted."""
full_name = first_name + ' ' + middle_name + ' ' + last_name
return full_name.title()

musician = get_formatted_name('john', 'lee', 'hooker')
print(musician)

Functions 14 3
This function works when given a first, middle, and last name. The
function takes in all three parts of a name and then builds a string out of
them. The function adds spaces where appropriate and converts the full
name to title case:
John Lee Hooker
But middle names aren’t always needed, and this function as written
would not work if you tried to call it with only a first name and a last name.
To make the middle name optional, we can give the
middle_name argument
an empty default value and ignore the argument unless the user provides a
value. To make
get_formatted_name() work without a middle name, we set the
default value of
middle_name to an empty string and move it to the end of the
list of parameters:
u def get_formatted_name(first_name, last_name, middle_name=''):
"""Return a full name, neatly formatted."""
v if middle_name: full_name = first_name + ' ' + middle_name + ' ' + last_name
w else: full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
x musician = get_formatted_name('john', 'hooker', 'lee') print(musician)
In this example, the name is built from three possible parts. Because
there’s always a first and last name, these parameters are listed first in the
function’s definition. The middle name is optional, so it’s listed last in the
definition, and its default value is an empty string u .
In the body of the function, we check to see if a middle name has been
provided. Python interprets non-empty strings as
True , so if middle_name
evaluates to
True if a middle name argument is in the function call v . If a
middle name is provided, the first, middle, and last names are combined to
form a full name. This name is then changed to title case and returned to
the function call line where it’s stored in the variable
musician and printed.
If no middle name is provided, the empty string fails the
if test and the else
block runs w . The full name is made with just a first and last name, and the
formatted name is returned to the calling line where it’s stored in
musician
and pr inted. Calling this function with a first and last name is straightfor ward. If
we’re using a middle name, however, we have to make sure the middle
name is the last argument passed so Python will match up the positional
arguments correctly x .

14 4 Chapter 8
This modified version of our function works for people with just a first
and last name, and it works for people who have a middle name as well:
Jimi Hendrix
John Lee Hooker
Optional values allow functions to handle a wide range of use cases
while letting function calls remain as simple as possible.
Returning a Dictionary
A function can return any kind of value you need it to, including more com -
plicated data structures like lists and dictionaries. For example, the follow -
ing function takes in parts of a name and returns a dictionar y representing
a person:
person.py def build_person(first_name, last_name):
"""Return a dictionary of information about a person."""
u person = {'first': first_name, 'last': last_name}
v return person
musician = build_person('jimi', 'hendrix')
w print(musician)
The function build_person() takes in a first and last name, and packs
these values into a dictionar y at u . The value of
first_name is stored with
the key
'first' , and the value of last_name is stored with the key 'last' . The
entire dictionar y representing the person is returned at v . The return
value is printed at w with the original two pieces of textual information
now stored in a dictionar y:
{'first': 'jimi', 'last': 'hendrix'}
This function takes in simple textual information and puts it into a
more meaningful data structure that lets you work with the information
beyond just printing it. The strings
'jimi' and 'hendrix' are now labeled as
a first name and last name. You can easily extend this function to accept
optional values like a middle name, an age, an occupation, or any other
information you want to store about a person. For example, the following
change allows you to store a person’s age as well:
def build_person(first_name, last_name, age=''):
"""Return a dictionary of information about a person."""
person = {'first': first_name, 'last': last_name}
if age:
person['age'] = age
return person
musician = build_person('jimi', 'hendrix', age=27)
print(musician)

Functions 145
We add a new optional parameter age to the function definition and
assign the parameter an empty default value. If the function call includes a
value for this parameter, the value is stored in the dictionar y. This function
always stores a person’s name, but it can also be modified to store any other
information you want about a person.
Using a Function with a while Loop
You can use functions with all the Python structures you’ve learned about
so far. For example, let’s use the
get_formatted_name() function with a while
loop to greet users more formally. Here’s a first attempt at greeting people
using their first and last names:
greeter.py def get_formatted_name(first_name, last_name):
"""Return a full name, neatly formatted."""
full_name = first_name + ' ' + last_name
return full_name.title()
# This is an infinite loop!
while True:
l_name = input("Last name: ")

formatted_name = get_formatted_name(f_name, l_name)
print("\nHello, " + formatted_name + "!")
For this example, we use a simple version of get_formatted_name() that
doesn’t involve middle names. The
while loop asks the user to enter their
name, and we prompt for their first and last name separately u .
But there’s one problem with this
while loop: We haven’t defined a quit
condition. Where do you put a quit condition when you ask for a series of
inputs? We want the user to be able to quit as easily as possible, so each
prompt should offer a way to quit. The
break statement offers a straight-
for ward way to exit the loop at either prompt:
def get_formatted_name(first_name, last_name):
"""Return a full name, neatly formatted."""
full_name = first_name + ' ' + last_name
return full_name.title()
while True:
print("(enter 'q' at any time to quit)")

f_name = input("First name: ")
if f_name == 'q':
break

l_name = input("Last name: ")
if l_name == 'q':
break

14 6 Chapter 8

formatted_name = get_formatted_name(f_name, l_name)
print("\nHello, " + formatted_name + "!")
We add a message that informs the user how to quit, and then we
break out of the loop if the user enters the quit value at either prompt.
Now the program will continue greeting people until someone enters
'q'
for either name:
(enter 'q' at any time to quit)
First name: eric
Last name: matthes
Hello, Eric Matthes!
(enter 'q' at any time to quit)
First name: q
t ry It y ourself
8-6. City Names: Write a function called city_country() that takes in the name
of a city and its country . The function should return a string formatted like this:
"Santiago, Chile"
Call your function with at least three city- country pairs, and print the value
that’s returned .
8-7. Album: Write a function called
make_album() that builds a dictionary
describing a music album . The function should take in an artist name and an
album title, and it should return a dictionary containing these two pieces of
information . Use the function to make three dictionaries representing different
albums . Print each return value to show that the dictionaries are storing the
album information correctly .
make_album() that allows you to store the
number of tracks on an album . If the calling line includes a value for the num -
ber of tracks, add that value to the album’s dictionary . Make at least one new
function call that includes the number of tracks on an album .
while
loop that allows users to enter an album’s artist and title . Once you have that
information, call
make_album() with the user’s input and print the dictionary
that’s created . Be sure to include a quit value in the
while loop .

Functions 147
Passing a list
You’ll often find it useful to pass a list to a function, whether it’s a list of
names, numbers, or more complex objects, such as dictionaries. When you
pass a list to a function, the function gets direct access to the contents of
the list. Let’s use functions to make working with lists more efficient.
Say we have a list of users and want to print a greeting to each. The
following example sends a list of names to a function called
greet_users() ,
which greets each person in the list individually:
greet_users.py def greet_users(names):
"""Print a simple greeting to each user in the list."""
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
We define greet_users() so it expects a list of names, which it stores in
the parameter
names . The function loops through the list it receives and
prints a greeting to each user. At u we define a list of users and then pass
the list
usernames to greet_users() in our function call:
Hello, Hannah!
Hello, Ty!
Hello, Margot!
This is the output we wanted. Ever y user sees a personalized greet -
ing, and you can call the function any time you want to greet a specific set
of users.
Modifying a List in a Function
When you pass a list to a function, the function can modify the list. Any
changes made to the list inside the function’s body are permanent, allowing
you to work efficiently even when you’re dealing with large amounts of data.
Consider a company that creates 3D printed models of designs that
users submit. Designs that need to be printed are stored in a list, and after
being printed they’re moved to a separate list. The following code does this
w ithout using functions:
models.py unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
# Simulate printing each design, until none are left.
# Move each design to completed_models after printing.
while unprinted_designs:
current_design = unprinted_designs.pop()

14 8 Chapter 8
# Simulate creating a 3D print from the design.
print("Printing model: " + current_design)
completed_models.append(current_design)

# Display all completed models.
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
This program starts with a list of designs that need to be printed and
an empty list called
completed_models that each design will be moved to after
it has been printed. As long as designs remain in
unprinted_designs , the while
loop simulates printing each design by removing a design from the end of
the list, storing it in
current_design , and displaying a message that the cur -
rent design is being printed. It then adds the design to the list of completed
models. When the loop is finished running, a list of the designs that have
been printed is displayed:
Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case
The following models have been printed:
dodecahedron
robot pendant
iphone case
We can reorganize this code by writing two functions, each of which
does one specific job. Most of the code won’t change; we’re just making it
more efficient. The first function will handle printing the designs, and the
second will summarize the prints that have been made:
u def print_models(unprinted_designs, completed_models):
"""
Simulate printing each design, until none are left.
Move each design to completed_models after printing.
"""
while unprinted_designs:
current_design = unprinted_designs.pop()

# Simulate creating a 3D print from the design.
print("Printing model: " + current_design)
completed_models.append(current_design)

v def show_completed_models(completed_models): """Show all the models that were printed."""
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)

unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []

Functions 149
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
At u we define the function print_models() with two parameters: a list of
designs that need to be printed and a list of completed models. Given these
two lists, the function simulates printing each design by emptying the list
of unprinted designs and filling up the list of completed models. At v we
define the function
show_completed_models() with one parameter: the list of
completed models. Given this list,
show_completed_models() displays the name
of each model that was printed. This program has the same output as the version without functions, but
the code is much more organized. The code that does most of the work has
been moved to two separate functions, which makes the main part of the
program easier to understand. Look at the body of the program to see how
much easier it is to understand what this program is doing:
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
We set up a list of unprinted designs and an empty list that will hold the
completed models. Then, because we’ve already defined our two functions,
all we have to do is call them and pass them the right arguments. We call
print_models() and pass it the two lists it needs; as expected, print_models()
simulates printing the designs. Then we call
show_completed_models() and
pass it the list of completed models so it can report the models that have
been printed. The descriptive function names allow others to read this
code and understand it, even without comments. This program is easier to extend and maintain than the version with -
out functions. If we need to print more designs later on, we can simply call
print_models() again. If we realize the printing code needs to be modified,
we can change the code once, and our changes will take place ever y where
the function is called. This technique is more efficient than having to update
code separately in several places in the program. This example also demonstrates the idea that ever y function should
have one specific job. The first function prints each design, and the second
displays the completed models. This is more beneficial than using one func -
tion to do both jobs. If you’re writing a function and notice the function
is doing too many different tasks, tr y to split the code into two functions.
Remember that you can always call a function from another function,
which can be helpful when splitting a complex task into a series of steps.
Preventing a Function from Modifying a List
Sometimes you’ll want to prevent a function from modifying a list. For
example, say that you start with a list of unprinted designs and write a

15 0 Chapter 8
function to move them to a list of completed models, as in the previous
example. You may decide that even though you’ve printed all the designs,
you want to keep the original list of unprinted designs for your records. But
because you moved all the design names out of
unprinted_designs , the list is
now empty, and the empty list is the only version you have; the original is
gone. In this case, you can address this issue by passing the function a copy
of the list, not the original. Any changes the function makes to the list will
affect only the copy, leaving the original list intact. You can send a copy of a list to a function like this:
function_name(list_name[:])
The slice notation [:] makes a copy of the list to send to the function.
If we didn’t want to empty the list of unprinted designs in pr int _model s.py ,
we could call
print_models() like this:
print_models(unprinted_designs[:], completed_models)
The function print_models() can do its work because it still receives the
names of all unprinted designs. But this time it uses a copy of the origi -
nal unprinted designs list, not the actual
unprinted_designs list. The list
completed_models will fill up with the names of printed models like it did
before, but the original list of unprinted designs will be unaffected by the
function. Even though you can preser ve the contents of a list by passing a copy
of it to your functions, you should pass the original list to functions unless
you have a specific reason to pass a copy. It’s more efficient for a function
to work with an existing list to avoid using the time and memor y needed to
make a separate copy, especially when you’re working with large lists.
t ry It y ourself
8-9. Magicians: Make a list of magician’s names . Pass the list to a function
called
show_magicians() , which prints the name of each magician in the list .
Write a function called
make_great() that modifies the list of magicians by add -
ing the phrase the Great to each magician’s name . Call
show_magicians() to
see that the list has actually been modified .
function
make_great() with a copy of the list of magicians’ names . Because the
original list will be unchanged, return the new list and store it in a separate list .
Call
show_magicians() with each list to show that you have one list of the origi -
nal names and one list with the Great added to each magician’s name .

Functions 151
Passing an arbitrary number of a rguments
Sometimes you won’t know ahead of time how many arguments a function
needs to accept. Fortunately, Python allows a function to collect an arbi -
trar y number of arguments from the calling statement. For example, consider a function that builds a pizza. It needs to accept a
number of toppings, but you can’t know ahead of time how many toppings
a person will want. The function in the following example has one param -
et er,
*toppings , but this parameter collects as many arguments as the calling
line provides:
pizza.py def make_pizza(*toppings):
"""Print the list of toppings that have been requested."""
print(toppings)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
The asterisk in the parameter name *toppings tells Python to make an
empty tuple called
toppings and pack whatever values it receives into this
tuple. The
print statement in the function body produces output showing
that Python can handle a function call with one value and a call with three
values. It treats the different calls similarly. Note that Python packs the
arguments into a tuple, even if the function receives only one value:
('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')
Now we can replace the print statement with a loop that runs through
the list of toppings and describes the pizza being ordered:
def make_pizza(*toppings):
"""Summarize the pizza we are about to make."""
print("\nMaking a pizza with the following toppings:")
for topping in toppings:
print("- " + topping)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
The function responds appropriately, whether it receives one value or
three values:
Making a pizza with the following toppings:
- pepperoni
Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese

152 Chapter 8
This syntax works no matter how many arguments the function
Mixing Positional and Arbitrary Arguments
If you want a function to accept several different kinds of arguments, the
parameter that accepts an arbitrar y number of arguments must be placed
last in the function definition. Python matches positional and key word
arguments first and then collects any remaining arguments in the final
p a r a met er. For example, if the function needs to take in a size for the pizza, that
parameter must come before the parameter
*toppings :
def make_pizza(size, *toppings):
"""Summarize the pizza we are about to make."""
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
In the function definition, Python stores the first value it receives in
the parameter
size . All other values that come after are stored in the tuple
toppings . The function calls include an argument for the size first, followed
by as many toppings as needed. Now each pizza has a size and a number of toppings, and each piece of
information is printed in the proper place, showing size first and toppings
after:
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
Using Arbitrary Keyword Arguments
Sometimes you’ll want to accept an arbitrar y number of arguments, but you
won’t know ahead of time what kind of information will be passed to the
function. In this case, you can write functions that accept as many key-value
pairs as the calling statement provides. One example involves building user
profiles: you know you’ll get information about a user, but you’re not sure
what kind of information you’ll receive. The function
build_profile() in the

Functions 15 3
following example always takes in a first and last name, but it accepts an
arbitrar y number of key word arguments as well:
user_profile.py def build_profile(first, last, **user_info):
"""Build a dictionary containing everything we know about a user."""
profile = {}
u profile['first_name'] = first profile['last_name'] = last
v for key, value in user_info.items(): profile[key] = value
return profile
user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics')
print(user_profile)
The definition of build_profile() expects a first and last name, and
then it allows the user to pass in as many name-value pairs as they want. The
double asterisks before the parameter
**user_info cause Python to create
an empty dictionar y called
user_info and pack whatever name-value pairs it
receives into this dictionar y. Within the function, you can access the name-
value pairs in
user_info just as you would for any dictionar y.
In the body of
build_profile() , we make an empty dictionar y called
profile to hold the user’s profile. At u we add the first and last names to
this dictionar y because we’ll always receive these two pieces of information
from the user. At v we loop through the additional key-value pairs in the
dictionar y
user_info and add each pair to the profile dictionar y. Finally, we
return the
profile dictionar y to the function call line.
We call
build_profile() , passing it the first name 'albert' , the last
name
'einstein' , and the two key-value pairs location='princeton' and
field='physics' . We store the returned profile in user_profile and print
user_profile :
{'first_name': 'albert', 'last_name': 'einstein',
'location': 'princeton', 'field': 'physics'}
The returned dictionar y contains the user’s first and last names and,
in this case, the location and field of study as well. The function would
work no matter how many additional key-value pairs are provided in the
function call.
You can mix positional, key word, and arbitrar y values in many dif -
ferent ways when writing your own functions. It’s useful to know that all
these argument types exist because you’ll see them often when you start
reading other people’s code. It takes practice to learn to use the different
types correctly and to know when to use each type. For now, remember to
use the simplest approach that gets the job done. As you progress you’ll
learn to use the most efficient approach each time.

15 4 Chapter 8
try It y ourself
8-12. Sandwiches: Write a function that accepts a list of items a person wants
on a sandwich . The function should have one parameter that collects as many
items as the function call provides, and it should print a summary of the sand -
wich that is being ordered . Call the function three times, using a different num -
ber of arguments each time .
8-13. User Profile: Start with a copy of user_profile.py from page 153 . Build
a profile of yourself by calling
build_profile() , using your first and last names
and three other key-value pairs that describe you .
8-14. Cars: Write a function that stores information about a car in a diction -
ary . The function should always receive a manufacturer and a model name . It
should then accept an arbitrary number of keyword arguments . Call the func-
tion with the required information and two other name-value pairs, such as a
color or an optional feature . Your function should work for a call like this one:
car = make_car('subaru', 'outback', color='blue', tow_package=True)
Print the dictionary that’s returned to make sure all the information was
stored correctly .
storing y our functions in m odules
One advantage of functions is the way they separate blocks of code from
main program will be much easier to follow. You can go a step further by
storing your functions in a separate file called a module and then importing
that module into your main program. An
import statement tells Python to
make the code in a module available in the currently running program file. Storing your functions in a separate file allows you to hide the details of
your program’s code and focus on its higher-level logic. It also allows you to
reuse functions in many different programs. When you store your functions
in separate files, you can share those files with other programmers without
having to share your entire program. K nowing how to import functions
also allows you to use libraries of functions that other programmers have
w ritten. There are several ways to import a module, and I’ll show you each of
these briefly.
Importing an Entire Module
To start importing functions, we first need to create a module. A module
is a file ending in .py that contains the code you want to import into your

Functions 155
program. Let’s make a module that contains the function make_pizza() . To
make this module, we’ll remove ever ything from the file pizza.py except the
function
make_pizza() :
pizza.py def make_pizza(size, *toppings):
"""Summarize the pizza we are about to make."""
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)
Now we’ll make a separate file called making_pizzas.py in the same
director y as pizza.py . This file imports the module we just created and then
makes two calls to
make_pizza() :
making_ import pizza
pizzas.py u pizza.make_pizza(16, 'pepperoni')pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
When Python reads this file, the line import pizza tells Python to
open the file pizza.py and copy all the functions from it into this program.
You don’t actually see code being copied between files because Python
copies the code behind the scenes as the program runs. All you need
to know is that any function defined in pizza.py will now be available in
making_pizzas.py .
To call a function from an imported module, enter the name of
the module you imported,
pizza , followed by the name of the function,
make_pizza() , separated by a dot u. This code produces the same output
as the original program that didn’t import a module:
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
This first approach to importing, in which you simply write import fol -
lowed by the name of the module, makes ever y function from the module
available in your program. If you use this kind of
import statement to import
an entire module named module_name.py , each function in the module is
available through the following syntax:
module_name.function_name()

15 6 Chapter 8
Importing Specific Functions
You can also import a specific function from a module. Here’s the general
syntax for this approach:
from module_name import function_name
You can import as many functions as you want from a module by sepa-
rating each function’s name with a comma:
from module_name import function_0, function_1, function_2
The making_pizzas.py example would look like this if we want to import
just the function we’re going to use:
from pizza import make_pizza
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
With this syntax, you don’t need to use the dot notation when you call a
function. Because we’ve explicitly imported the function
make_pizza() in the
import statement, we can call it by name when we use the function.
Using as to Give a Function an Alias
If the name of a function you’re importing might conflict with an exist -
ing name in your program or if the function name is long, you can use a
short, unique alias —an alternate name similar to a nickname for the func -
tion. You’ll give the function this special nickname when you import the
function. Here we give the function
make_pizza() an alias, mp() , by importing
make_pizza as mp
. The as key word renames a function using the alias you
provide:
from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
The import statement shown here renames the function make_pizza() to
mp() in this program. Any time we want to call make_pizza() we can simply
w rite
mp() instead, and Python will run the code in make_pizza() while avoid -
ing any confusion with another
make_pizza() function you might have writ -
ten in this program file. The general syntax for providing an alias is:
from module_name import function_name as fn

Functions 157
Using as to Give a Module an Alias
You can also provide an alias for a module name. Giving a module a short
alias, like
p for pizza , allows you to call the module’s functions more quickly.
Calling
p.make_pizza() is more concise than calling pizza.make_pizza() :
import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
The module pizza is given the alias p in the import statement, but all of
the module’s functions retain their original names. Calling the functions by
w riting
p.make_pizza() is not only more concise than writing pizza.make_pizza() ,
but also redirects your attention from the module name and allows you
to focus on the descriptive names of its functions. These function names,
which clearly tell you what each function does, are more important to the
readability of your code than using the full module name. The general syntax for this approach is:
import module_name as mn
Importing All Functions in a Module
You can tell Python to import ever y function in a module by using the aster -
isk (
*) operator:
from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
The asterisk in the import statement tells Python to copy ever y func -
tion from the module
pizza into this program file. Because ever y function
is imported, you can call each function by name without using the dot
notation. However, it’s best not to use this approach when you’re working
with larger modules that you didn’t write: if the module has a function
name that matches an existing name in your project, you can get some
unexpected results. Python may see several functions or variables with the
same name, and instead of importing all the functions separately, it will
over write the functions. The best approach is to import the function or functions you want,
or import the entire module and use the dot notation. This leads to clear
code that’s easy to read and understand. I include this section so you’ll
recognize
import statements like the following when you see them in other
people’s code:
from module_name import *

15 8 Chapter 8
styling f unctions
You need to keep a few details in mind when you’re styling functions.
Functions should have descriptive names, and these names should use
understand what your code is tr ying to do. Module names should use these
conventions as well.
Ever y function should have a comment that explains concisely what
the function does. This comment should appear immediately after the
function definition and use the docstring format. In a well-documented
function, other programmers can use the function by reading only the
description in the docstring. They should be able to trust that the code
works as described, and as long as they know the name of the function,
the arguments it needs, and the kind of value it returns, they should be
able to use it in their programs. If you specify a default value for a parameter, no spaces should be used
on either side of the equal sign:
def function_name(parameter_0, parameter_1='default value')
The same convention should be used for key word arguments in func -
tion calls:
function_name(value_0, parameter_1='value')
PEP 8 ( https://www.python.org/dev/peps/pep- 0008/ ) recommends that
you limit lines of code to 79 characters so ever y line is visible in a reasonably
sized editor window. If a set of parameters causes a function’s definition to
be longer than 79 characters, press
enter after the opening parenthesis on
the definition line. On the next line, press
ta b twice to separate the list of
arguments from the body of the function, which will only be indented one
level. Most editors automatically line up any additional lines of parameters to
match the indentation you have established on the first line:
def function_name(
parameter_0, parameter_1, parameter_2,
parameter_3, parameter_4, parameter_5):
function body...
If your program or module has more than one function, you can sepa -
rate each by two blank lines to make it easier to see where one function
ends and the next one begins. All
import statements should be written at the beginning of a file.
The only exception is if you use comments at the beginning of your file to
describe the overall program.

Functions 159
try It y ourself
8-15. Printing Models: Put the functions for the example print_models.py in a
separate file called printing _functions.py . Write an
import statement at the top
of print_models.py , and modify the file to use the imported functions .
8-16. Imports: Using a program you wrote that has one function in it, store that
function in a separate file . Import the function into your main program file, and
call the function using each of these approaches:
import module_name
from module_name import function_name
from module_name import function_name as fn
import module_name as mn
from module_name import *
8-17. Styling Functions: Choose any three programs you wrote for this chapter,
and make sure they follow the styling guidelines described in this section .
summary
In this chapter you learned how to write functions and to pass arguments
work. You learned how to use positional and key word arguments, and how
to accept an arbitrar y number of arguments. You saw functions that display
output and functions that return values. You learned how to use functions
with lists, dictionaries,
if statements, and while loops. You also saw how to
store your functions in separate files called modules , so your program files
will be simpler and easier to understand. Finally, you learned to style your
functions so your programs will continue to be well-structured and as easy
as possible for you and others to read. One of your goals as a programmer should be to write simple code that
does what you want it to, and functions help you do this. They allow you to
write blocks of code and leave them alone once you know they work. When
you know a function does its job correctly, you can trust that it will continue
to work and move on to your next coding task. Functions allow you to write code once and then reuse that code as
many times as you want. When you need to run the code in a function,
all you need to do is write a one-line call and the function does its job.
When you need to modify a function’s behavior, you only have to modify
one block of code, and your change takes effect ever y where you’ve made a
call to that function. Using functions makes your programs easier to read, and good func -
tion names summarize what each part of a program does. Reading a series
of function calls gives you a much quicker sense of what a program does
than reading a long series of code blocks.

16 0 Chapter 8
Functions also make your code easier to test and debug. When the bulk
of your program’s work is done by a set of functions, each of which has a
specific job, it’s much easier to test and maintain the code you’ve written.
You can write a separate program that calls each function and tests whether
each function works in all the situations it may encounter. When you do
this, you can be confident that your functions will work properly each time
you call them. In Chapter 9 you’ll learn to write classes. Classes combine functions and
data into one neat package that can be used in flexible and efficient ways.

9
Cl asses
Object-oriented programming is one of the
most effective approaches to writing soft -
ware. In object-oriented programming you
w r ite classes that represent real-world things
and situations, and you create objects based on these
classes. When you write a class, you define the general
behavior that a whole category of objects can have.
When you create individual objects from the class, each object is automati -
cally equipped with the general behavior; you can then give each object
whatever unique traits you desire. You’ll be amazed how well real-world
situations can be modeled with object-oriented programming. Making an object from a class is called instantiation , and you work with
instances of a class. In this chapter you’ll write classes and create instances
of those classes. You’ll specify the kind of information that can be stored in
instances, and you’ll define actions that can be taken with these instances.
You’ll also write classes that extend the functionality of existing classes, so

162 Chapter 9
similar classes can share code efficiently. You’ll store your classes in mod-
ules and import classes written by other programmers into your own pro -
just what’s happening line by line, but also the bigger concepts behind it.
K nowing the logic behind classes will train you to think logically so you can
write programs that effectively address almost any problem you encounter. Classes also make life easier for you and the other programmers you’ll
need to work with as you take on increasingly complex challenges. When
you and other programmers write code based on the same kind of logic,
you’ll be able to understand each other’s work. Your programs will make
sense to many collaborators, allowing ever yone to accomplish more.
Creating and u sing a Class
You can model almost anything using classes. Let’s start by writing a simple
class,
Dog , that represents a dog—not one dog in particular, but any dog.
What do we know about most pet dogs? Well, they all have a name and age.
We also know that most dogs sit and roll over. Those two pieces of infor -
mation (name and age) and those two behaviors (sit and roll over) will go
in our
Dog class because they’re common to most dogs. This class will tell
Python how to make an object representing a dog. A fter our class is written,
we’ll use it to make individual instances, each of which represents one spe -
cific dog.
Creating the Dog Class
Each instance created from the Dog class will store a name and an age , and
we’ll give each dog the ability to
sit() and roll_over() :
dog.py u class Dog():
v """A simple attempt to model a dog."""
w def __init__(self, name, age): """Initialize name and age attributes."""
x self.name = name self.age = age

y def sit(self): """Simulate a dog sitting in response to a command."""
print(self.name.title() + " is now sitting.")
def roll_over(self):
"""Simulate rolling over in response to a command."""
print(self.name.title() + " rolled over!")

Classes 16 3
There’s a lot to notice here, but don’t worr y. You’ll see this structure
throughout this chapter and have lots of time to get used to it. At u we
define a class called
Dog . By convention, capitalized names refer to classes
in Python. The parentheses in the class definition are empty because we’re
creating this class from scratch. At v we write a docstring describing what
this class does.
t he __init__() Method
A function that’s part of a class is a method . Ever ything you learned about
functions applies to methods as well; the only practical difference for now is
the way we’ll call methods. The
__init__() method at w is a special method
Python runs automatically whenever we create a new instance based on the
Dog class. This method has two leading underscores and two trailing under -
scores, a convention that helps prevent Python’s default method names
from conflicting with your method names. We define the
__init__() method to have three parameters: self , name ,
and
age . The self parameter is required in the method definition, and it
must come first before the other parameters. It must be included in the def -
inition because when Python calls this
__init__() method later (to create an
instance of
Dog ), the method call will automatically pass the self argument.
Ever y method call associated with a class automatically passes
self , which
is a reference to the instance itself; it gives the individual instance access to
the attributes and methods in the class. When we make an instance of
Dog ,
Python will call the
__init__() method from the Dog class. We’ll pass Dog()
a name and an age as arguments;
self is passed automatically, so we don’t
need to pass it. Whenever we want to make an instance from the
Dog class,
we’ll provide values for only the last two parameters,
name and age .
The two variables defined at x each have the prefix
self . Any variable
prefixed with
self is available to ever y method in the class, and we’ll also be
able to access these variables through any instance created from the class.
self.name = name takes the value stored in the parameter name and stores it
in the variable
name , which is then attached to the instance being created.
The same process happens with
self.age = age . Variables that are accessible
through instances like this are called attributes .
The
Dog class has two other methods defined: sit() and roll_over() y .
Because these methods don’t need additional information like a name
or age, we just define them to have one parameter,
self . The instances
we create later will have access to these methods. In other words, they’ll
be able to sit and roll over. For now,
sit() and roll_over() don’t do much.
They simply print a message saying the dog is sitting or rolling over. But
the concept can be extended to realistic situations: if this class were part
of an actual computer game, these methods would contain code to make
an animated dog sit and roll over. If this class was written to control a
robot, these methods would direct movements that cause a dog robot to
sit and roll over.

16 4 Chapter 9
Creating Classes in Python 2.7
When you create a class in Python 2.7, you need to make one minor change.
You include the term
object in parentheses when you create a class:
class ClassName(object):
--snip--
This makes Python 2.7 classes behave more like Python 3 classes, which
makes your work easier overall. The
Dog class would be defined like this in Python 2.7:
class Dog(object):
--snip--
Making an Instance from a Class
Think of a class as a set of instructions for how to make an instance. The
class
Dog is a set of instructions that tells Python how to make individual
instances representing specific dogs. Let’s make an instance representing a specific dog:
class Dog():
--snip--

u my_dog = Dog('willie', 6)
v print("My dog's name is " + my_dog.name.title() + ".")
w print("My dog is " + str(my_dog.age) + " years old.")
The Dog class we’re using here is the one we just wrote in the previous
example. At u we tell Python to create a dog whose name is
'willie' and
whose age is
6. When Python reads this line, it calls the __init__() method
in
Dog with the arguments 'willie' and 6. The __init__() method creates an
instance representing this particular dog and sets the
name and age attributes
using the values we provided. The
__init__() method has no explicit return
statement, but Python automatically returns an instance representing this
dog. We store that instance in the variable
my_dog . The naming convention is
helpful here: we can usually assume that a capitalized name like
Dog refers
to a class, and a lowercase name like
my_dog refers to a single instance cre -
ated from a class.
a ccessing a ttributes
To access the attributes of an instance, you use dot notation. At v we access
the value of
my_dog ’s attribute name by writing:
my_dog.name
Dot notation is used often in Python. This syntax demonstrates how
Python finds an attribute’s value. Here Python looks at the instance
my_dog

Classes 165
and then finds the attribute name associated with my_dog . This is the same attri-
bute referred to as
self.name in the class Dog . At w we use the same approach
to work with the attribute
age . In our first print st atement, my_dog.name.title()
makes
'willie' , the value of my_dog ’s name attribute, start with a capital letter. In
the second
print st atement, str(my_dog.age) converts 6, the value of my_dog ’s age
attribute, to a string. The output is a summar y of what we know about
my_dog :
My dog's name is Willie.
My dog is 6 years old.
Calling Methods
A fter we create an instance from the class
Dog , we can use dot notation to
call any method defined in
Dog . Let’s make our dog sit and roll over:
class Dog():
--snip--
my_dog = Dog('willie', 6)
my_dog.sit()
my_dog.roll_over()
To call a method, give the name of the instance (in this case, my_dog )
and the method you want to call, separated by a dot. When Python reads
my_dog.sit() , it looks for the method sit() in the class Dog and runs that
code. Python interprets the line
my_dog.roll_over() in the same way.
Now Willie does what we tell him to:
Willie is now sitting.
Willie rolled over!
This syntax is quite useful. When attributes and methods have been
given appropriately descriptive names like
name , age , sit() , and roll_over() ,
we can easily infer what a block of code, even one we’ve never seen before,
is supposed to do.
Creating Multiple Instances
You can create as many instances from a class as you need. Let’s create a
second dog called
your_dog :
class Dog():
--snip--
my_dog = Dog('willie', 6)
your_dog = Dog('lucy', 3)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
my_dog.sit()

16 6 Chapter 9
print("\nYour dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.")
your_dog.sit()
In this example we create a dog named Willie and a dog named Lucy.
Each dog is a separate instance with its own set of attributes, capable of the
same set of actions:
My dog's name is Willie.
My dog is 6 years old.
Willie is now sitting.
Your dog is 3 years old.
Lucy is now sitting.
Even if we used the same name and age for the second dog, Python
would still create a separate instance from the
Dog class. You can make
as many instances from one class as you need, as long as you give each
instance a unique variable name or it occupies a unique spot in a list or
dictionar y.
t ry It y ourself
9 -1. Restaurant: Make a class called Restaurant . The __init__() method for
Restaurant should store two attributes: a restaurant_name and a cuisine_type .
Make a method called
describe_restaurant() that prints these two pieces of
information, and a method called
open_restaurant() that prints a message indi -
cating that the restaurant is open .
Make an instance called
restaurant from your class . Print the two attri -
butes individually, and then call both methods .
different instances from the class, and call
describe_restaurant() for each
instance .
9-3. Users: Make a class called
User . Create two attributes called first_name
and
last_name , and then create several other attributes that are typically stored
in a user profile . Make a method called
describe_user() that prints a summary
of the user’s information . Make another method called
greet_user() that prints
a personalized greeting to the user .
Create several instances representing different users, and call both methods
for each user .

Classes 167
working with Classes and Instances
You can use classes to represent many real-world situations. Once you write
a class, you’ll spend most of your time working with instances created from
that class. One of the first tasks you’ll want to do is modify the attributes
associated with a particular instance. You can modify the attributes of an
instance directly or write methods that update attributes in specific ways.
The Car Class
Let’s write a new class representing a car. Our class will store information
about the kind of car we’re working with, and it will have a method that
summarizes this information :
car.py class Car():
"""A simple attempt to represent a car."""
u def __init__(self, make, model, year): """Initialize attributes to describe a car."""
self.make = make
self.model = model
self.year = year

v def get_descriptive_name(self): """Return a neatly formatted descriptive name."""
long_name = str(self.year) + ' ' + self.make + ' ' + self.mode
l
return long_name.title()

w my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name())
At u in the Car class, we define the __init__() method with the self
parameter first, just like we did before with our
Dog class. We also give
it three other parameters:
make , model , and year . The __init__() method
takes in these parameters and stores them in the attributes that will be
associated with instances made from this class. When we make a new
Car
instance, we’ll need to specify a make, model, and year for our instance. At v we define a method called
get_descriptive_name() that puts a car’s
year , make , and model into one string neatly describing the car. This will spare
us from having to print each attribute’s value individually. To work with the
attribute values in this method, we use
self.make , self.model , and self.year .
At w we make an instance from the
Car class and store it in the variable
my_new_car . Then we call get_descriptive_name() to show what kind of car
we have:
2016 Audi A4
To make the class more interesting, let’s add an attribute that changes
over time. We’ll add an attribute that stores the car’s overall mileage.

16 8 Chapter 9
Setting a Default Value for an Attribute
Ever y attribute in a class needs an initial value, even if that value is 0 or an
empty string. In some cases, such as when setting a default value, it makes
sense to specify this initial value in the body of the
__init__() method; if
you do this for an attribute, you don’t have to include a parameter for that
attribute. Let’s add an attribute called
odometer_reading that always starts with a
value of 0. We’ll also add a method
car’s odometer:
class Car():

def __init__(self, make, model, year):
"""Initialize attributes to describe a car."""
self.make = make
self.model = model
self.year = year
def get_descriptive_name(self):
--snip--

v def read_odometer(self): """Print a statement showing the car's mileage."""
print("This car has " + str(self.odometer_reading) + " miles
on it.")

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
This time when Python calls the __init__() method to create a new
instance, it stores the make, model, and year values as attributes like
it did in the previous example. Then Python creates a new attribute
called
odometer_reading and sets its initial value to 0 u . We also have a
new method called
mileage. Our car starts with a mileage of 0 :
2016 Audi A4
This car has 0 miles on it.
Not many cars are sold with exactly 0 miles on the odometer, so we
need a way to change the value of this attribute.
Modifying Attribute Values
You can change an attribute’s value in three ways: you can change the value
directly through an instance, set the value through a method, or increment
the value (add a certain amount to it) through a method. Let’s look at each
of these approaches.

Classes 169
Modifying an attribute’s Value Directly
The simplest way to modify the value of an attribute is to access the attri -
bute directly through an instance. Here we set the odometer reading to 23
directly:
class Car():
--snip--

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
At u we use dot notation to access the car’s odometer_reading attri -
bute and set its value directly. This line tells Python to take the instance
my_new_car , find the attribute odometer_reading associated with it, and set the
value of that attribute to 23:
2016 Audi A4
This car has 23 miles on it.
Sometimes you’ll want to access attributes directly like this, but other
times you’ll want to write a method that updates the value for you.
Modifying an a ttribute’s Value t hrough a Method
It can be helpful to have methods that update certain attributes for you.
Instead of accessing the attribute directly, you pass the new value to a
method that handles the updating internally. Here’s an example showing a method called
update_odometer() :
class Car():
--snip--

u def update_odometer(self, mileage): """Set the odometer reading to the given value."""

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
The only modification to Car is the addition of update_odometer() at u .
This method takes in a mileage value and stores it in
At v we call
update_odometer() and give it 23 as an argument (corresponding

170 Chapter 9
to the mileage parameter in the method definition). It sets the odometer
2016 Audi A4
This car has 23 miles on it.
We can extend the method update_odometer() to do additional work
ever y time the odometer reading is modified. Let’s add a little logic to
make sure no one tries to roll back the odometer reading:
class Car():
--snip--

def update_odometer(self, mileage):
"""
Set the odometer reading to the given value.
Reject the change if it attempts to roll the odometer back.
"""
else:
v print("You can't roll back an odometer!")
Now update_odometer() checks that the new reading makes sense before
modifying the attribute. If the new mileage,
mileage , is greater than or equal
to the existing mileage,
self.odometer_reading , you can update the odometer
reading to the new mileage u . If the new mileage is less than the existing
mileage, you’ll get a warning that you can’t roll back an odometer v .
Incrementing an a ttribute’s Value through a Method
Sometimes you’ll want to increment an attribute’s value by a certain
amount rather than set an entirely new value. Say we buy a used car and
put 100 miles on it between the time we buy it and the time we register it.
Here’s a method that allows us to pass this incremental amount and add
that value to the odometer reading:
class Car():
--snip--

def update_odometer(self, mileage):
--snip--

u def increment_odometer(self, miles): """Add the given amount to the odometer reading."""

v my_used_car = Car('subaru', 'outback', 2013) print(my_used_car.get_descriptive_name())

Classes 171
The new method increment_odometer() at u takes in a number of miles,
self.odometer_reading . At v we create a used car,
my_used_car . We set its odometer to 23,500 by calling update_odometer() and
passing it
23500 at w . At x we call increment_odometer() and pass it 100 to add
the 100 miles that we drove between buying the car and registering it:
2013 Subaru Outback
This car has 23500 miles on it.
This car has 23600 miles on it.
You can easily modify this method to reject negative increments so no
one uses this function to roll back an odometer.
note You can use methods like this to control how users of your program update values
such as an odometer reading, but anyone with access to the program can set the odom -
eter reading to any value by accessing the attribute directly. Effective security takes
extreme attention to detail in addition to basic checks like those shown here.
t ry It y ourself
number_served with a default value of 0 . Create an
instance called
restaurant from this class . Print the number of customers the
restaurant has served, and then change this value and print it again .
set_number_served() that lets you set the number
of customers that have been served . Call this method with a new number and
print the value again .
increment_number_served() that lets you increment
the number of customers who’ve been served . Call this method with any num-
ber you like that could represent how many customers were served in, say, a
class from Exercise 9-3 (page 166) . Write a method called
increment_
that increments the value of login_attempts by 1 . Write
another method called
attempts
to 0 .
Make an instance of the
several times . Print the value of
login_attempts to make sure it was incremented
properly, and then call
make sure it was reset to 0 .

172 Chapter 9
Inheritance
You don’t always have to start from scratch when writing a class. If the class
you’re writing is a specialized version of another class you wrote, you can
use inheritance. When one class inherits from another, it automatically takes
on all the attributes and methods of the first class. The original class is
called the parent class , and the new class is the child class . The child class
inherits ever y attribute and method from its parent class but is also free to
define new attributes and methods of its own.
The __init__() Method for a Child Class
The first task Python has when creating an instance from a child class is to
assign values to all attributes in the parent class. To do this, the
__init__()
method for a child class needs help from its parent class. As an example, let’s model an electric car. An electric car is just a spe -
cific kind of car, so we can base our new
ElectricCar class on the Car class
we wrote earlier. Then we’ll only have to write code for the attributes and
behavior specific to electric cars. Let’s start by making a simple version of the
ElectricCar class, which
does ever ything the
Car class does :
electric_car.py u class Car():
"""A simple attempt to represent a car."""
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year

def get_descriptive_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()

print("This car has " + str(self.odometer_reading) + " miles
on it.")

def update_odometer(self, mileage):
else:
print("You can't roll back an odometer!")

def increment_odometer(self, miles):
v class ElectricCar(Car): """Represent aspects of a car, specific to electric vehicles."""

Classes 173
w def __init__(self, make, model, year): """Initialize attributes of the parent class."""
x super().__init__(make, model, year)
y my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())
At u we start with Car . When you create a child class, the parent class
must be part of the current file and must appear before the child class in
the file. At v we define the child class,
ElectricCar . The name of the parent
class must be included in parentheses in the definition of the child class.
The
__init__() method at w takes in the information required to make a Car
instance. The
super() function at x is a special function that helps Python make
connections between the parent and child class. This line tells P ython to
call the
__init__() method from ElectricCar ’s parent class, which gives an
ElectricCar instance all the attributes of its parent class. The name super
comes from a convention of calling the parent class a superclass and the
child class a subclass .
We test whether inheritance is working properly by tr ying to create an
electric car with the same kind of information we’d provide when making
a regular car. At y we make an instance of the
ElectricCar class, and store
it in
my_tesla . This line calls the __init__() method defined in ElectricCar ,
which in turn tells Python to call the
__init__() method defined in the par -
ent class
Car . We provide the arguments 'tesla' , 'model s' , and 2016 .
Aside from
__init__() , there are no attributes or methods yet that are
particular to an electric car. At this point we’re just making sure the electric
car has the appropriate
Car behav iors :
2016 Tesla Model S
The ElectricCar instance works just like an instance of Car , so now we
can begin defining attributes and methods specific to electric cars .
Inheritance in Python 2.7
In Python 2.7, inheritance is slightly different. The ElectricCar class would
look like this:
class Car(object):
def __init__(self, make, model, year):
--snip--
class ElectricCar(Car):
def __init__(self, make, model, year):
super(ElectricCar, self).__init__(make, model, year)
--snip--

174 Chapter 9
The super() function needs two arguments: a reference to the child
class and the
self object. These arguments are necessar y to help Python
make proper connections between the parent and child classes. When you
use inheritance in Python 2.7, make sure you define the parent class using
the
object syntax as well.
Defining Attributes and Methods for the Child Class
Once you have a child class that inherits from a parent class, you can add
any new attributes and methods necessar y to differentiate the child class
from the parent class. Let’s add an attribute that’s specific to electric cars (a batter y, for
example) and a method to report on this attribute. We’ll store the batter y
size and write a method that prints a description of the batter y:
class Car():
--snip--
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def __init__(self, make, model, year):
"""
Initialize attributes of the parent class.
Then initialize attributes specific to an electric car.
"""
super().__init__(make, model, year)
u self.battery_size = 70
v def describe_battery(self): """Print a statement describing the battery size."""
print("This car has a " + str(self.battery_size) + "-kWh batt
ery.")
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
At u we add a new attribute self.battery_size and set its initial value to,
s ay,
70. This attribute will be associated with all instances created from the
ElectricCar class but won’t be associated with any instances of Car . We also
describe_battery() that prints information about the
batter y at v . When we call this method, we get a description that is clearly
specific to an electric car:
2016 Tesla Model S
This car has a 70-kWh battery.
There’s no limit to how much you can specialize the ElectricCar class.
You can add as many attributes and methods as you need to model an elec -
tric car to whatever degree of accuracy you need. An attribute or method
that could belong to any car, rather than one that’s specific to an electric

Classes 175
car, should be added to the Car class instead of the ElectricCar class. Then
anyone who uses the
Car class will have that functionality available as well,
and the
ElectricCar class will only contain code for the information and
behavior specific to electric vehicles.
Overriding Methods from the Parent Class
You can override any method from the parent class that doesn’t fit what
you’re tr ying to model with the child class. To do this, you define a method
in the child class with the same name as the method you want to override
in the parent class. Python will disregard the parent class method and only
pay attention to the method you define in the child class. Say the class
Car had a method called fill_gas_tank() . This method is
meaningless for an all-electric vehicle, so you might want to override this
method. Here’s one way to do that:
def ElectricCar(Car):
--snip--
def fill_gas_tank():
"""Electric cars don't have gas tanks."""
print("This car doesn't need a gas tank!")
Now if someone tries to call fill_gas_tank() with an electric car, Python
will ignore the method
fill_gas_tank() in Car and run this code instead. When
you use inheritance, you can make your child classes retain what you need
and override anything you don’t need from the parent class.
Instances as Attributes
When modeling something from the real world in code, you may find that
you’re adding more and more detail to a class. You’ll find that you have a
growing list of attributes and methods and that your files are becoming
lengthy. In these situations, you might recognize that part of one class can
be written as a separate class. You can break your large class into smaller
classes that work together. For example, if we continue adding detail to the
ElectricCar class, we
might notice that we’re adding many attributes and methods specific to
the car’s batter y. When we see this happening, we can stop and move those
attributes and methods to a separate class called
Battery . Then we can use a
Battery instance as an attribute in the ElectricCar class:
class Car():
--snip--

u class Battery(): """A simple attempt to model a battery for an electric car."""

v def __init__(self, battery_size=70): """Initialize the battery's attributes."""
self.battery_size = battery_size

176 Chapter 9
w def describe_battery(self): """Print a statement describing the battery size."""
print("This car has a " + str(self.battery_size) + "-kWh batt
ery.")

class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def __init__(self, make, model, year):
"""
Initialize attributes of the parent class.
Then initialize attributes specific to an electric car.
"""
super().__init__(make, model, year)
x self.battery = Battery()
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
At u we define a new class called Battery that doesn’t inherit from any
other class. The
__init__() method at v has one parameter, battery_size , in
self . This is an optional parameter that sets the batter y’s size to
70 if no value is provided. The method
describe_battery() has been moved
to this class as well w .
In the
ElectricCar class, we now add an attribute called self.battery x .
This line tells Python to create a new instance of
Battery (with a default size
of 70, because we’re not specifying a value) and store that instance in the
attribute
self.battery . This will happen ever y time the __init__() method
is called; any
ElectricCar instance will now have a Battery instance created
aut om at ic a l l y. We create an electric car and store it in the variable
my_tesla . When
we want to describe the batter y, we need to work through the car’s
battery
attribute:
my_tesla.battery.describe_battery()
This line tells Python to look at the instance my_tesla , find its battery
attribute, and call the method
describe_battery() that’s associated with the
Battery instance stored in the attribute.
The output is identical to what we saw previously:
2016 Tesla Model S
This car has a 70-kWh battery.

Classes 17 7
This looks like a lot of extra work, but now we can describe the batter y
in as much detail as we want without cluttering the
ElectricCar class. Let’s
Battery that reports the range of the car based on
the batter y size:
class Car():
--snip--

class Battery():
--snip--

u def get_range(self): """Print a statement about the range this battery provides."""
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270

message = "This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)

class ElectricCar(Car):
--snip--
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
v my_tesla.battery.get_range()
The new method get_range() at u performs some simple analysis. If the
batter y’s capacity is 70 kWh,
get_range() sets the range to 240 miles, and if
the capacity is 85 kWh, it sets the range to 270 miles. It then reports this
value. When we want to use this method, we again have to call it through
the car’s
battery attribute at v .
The output tells us the range of the car based on its batter y size:
2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.
Modeling Real-World Objects
As you begin to model more complicated items like electric cars, you’ll
wrestle with interesting questions. Is the range of an electric car a property
of the batter y or of the car? If we’re only describing one car, it’s probably
fine to maintain the association of the method
get_range() with the Battery
class. But if we’re describing a manufacturer’s entire line of cars, we proba -
bly want to move
get_range() to the ElectricCar class. The get_range() method

178 Chapter 9
would still check the batter y size before determining the range, but it would
report a range specific to the kind of car it’s associated with. Alternatively,
we could maintain the association of the
get_range() method with the bat-
ter y but pass it a parameter such as
car_model . The get_range() method would
then report a range based on the batter y size and car model. This brings you to an interesting point in your growth as a program -
mer. When you wrestle with questions like these, you’re thinking at a higher
logical level rather than a syntax-focused level. You’re thinking not about
Python, but about how to represent the real world in code. When you reach
this point, you’ll realize there are often no right or wrong approaches to
modeling real-world situations. Some approaches are more efficient than
others, but it takes practice to find the most efficient representations. If
your code is working as you want it to, you’re doing well! Don’t be discour -
aged if you find you’re ripping apart your classes and rewriting them several
times using different approaches. In the quest to write accurate, efficient
code, ever yone goes through this process.
t ry It y ourself
9-6. Ice Cream Stand: An ice cream stand is a specific kind of restaurant . Write
a class called
IceCreamStand that inherits from the Restaurant class you wrote
in Exercise 9-1 (page 166) or Exercise 9-4 (page 171) . Either version of
the class will work; just pick the one you like better . Add an attribute called
flavors that stores a list of ice cream flavors . Write a method that displays
these flavors . Create an instance of
IceCreamStand , and call this method .
9 - 7. Admin: An administrator is a special kind of user . Write a class called
Admin that inherits from the User class you wrote in Exercise 9-3 (page 166)
or Exercise 9-5 (page 171) . Add an attribute,
privileges , that stores a list
of strings like
"can add post" , "can delete post" , "can ban user" , and so on .
Write a method called
show_privileges() that lists the administrator’s set of
privileges . Create an instance of
9-8. Privileges: Write a separate
Privileges class . The class should have one
attribute,
privileges , that stores a list of strings as described in Exercise 9-7 .
Move the
show_privileges() method to this class . Make a Privileges instance
as an attribute in the
Admin class . Create a new instance of Admin and use your
method to show its privileges .
9 - 9. Battery Upgrade: Use the final version of electric_car.py from this section .
Battery class called upgrade_battery() . This method
should check the battery size and set the capacity to 85 if it isn’t already .
Make an electric car with a default battery size, call
get_range() once, and
then call
get_range() a second time after upgrading the battery . You should
see an increase in the car’s range .

Classes 179
Importing Classes
when you use inheritance properly. In keeping with the overall philosophy
of Python, you’ll want to keep your files as uncluttered as possible. To help,
Python lets you store classes in modules and then import the classes you
Importing a Single Class
Let’s create a module containing just the Car class. This brings up a subtle
naming issue: we already have a file named ca r.p y in this chapter, but this
module should be named ca r.p y because it contains code representing a car.
We’ll resolve this naming issue by storing the
Car class in a module named
ca r.p y , replacing the ca r.p y file we were previously using. From now on, any
program that uses this module will need a more specific filename, such as
m y _ ca r.p y . Here’s ca r.p y with just the code from the class
Car :
car.py u """A class that can be used to represent a car."""
class Car():
"""A simple attempt to represent a car."""
def __init__(self, make, model, year):
"""Initialize attributes to describe a car."""
self.make = make
self.model = model
self.year = year

def get_descriptive_name(self):
"""Return a neatly formatted descriptive name."""
long_name = str(self.year) + ' ' + self.make + ' ' + self.mode
l
return long_name.title()

"""Print a statement showing the car's mileage."""
print("This car has " + str(self.odometer_reading) + " miles
on it.")

def update_odometer(self, mileage):
"""
Set the odometer reading to the given value.
Reject the change if it attempts to roll the odometer back.
"""
else:
print("You can't roll back an odometer!")

def increment_odometer(self, miles):

18 0 Chapter 9
At u we include a module-level docstring that briefly describes the
contents of this module. You should write a docstring for each module you
create. Now we make a separate file called m y _ ca r.p y . This file will import the
Car class and then create an instance from that class:
my_car.py u from car import Car
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
The import statement at u tells Python to open the car module and
import the class
Car . Now we can use the Car class as if it were defined in
this file. The output is the same as we saw earlier:
2016 Audi A4
This car has 23 miles on it.
Importing classes is an effective way to program. Picture how long
this program file would be if the entire
Car class were included. When you
instead move the class to a module and import the module, you still get all
the same functionality, but you keep your main program file clean and easy
to read. You also store most of the logic in separate files; once your classes
work as you want them to, you can leave those files alone and focus on the
higher-level logic of your main program.
Storing Multiple Classes in a Module
You can store as many classes as you need in a single module, although
each class in a module should be related somehow. The classes
Battery and
ElectricCar both help represent cars, so let’s add them to the module ca r.p y :
car.py """A set of classes used to represent gas and electric cars."""
class Car():
--snip--

class Battery():
"""A simple attempt to model a battery for an electric car."""
def __init__(self, battery_size=60):
"""Initialize the batteery's attributes."""
self.battery_size = battery_size
def describe_battery(self):
"""Print a statement describing the battery size."""
print("This car has a " + str(self.battery_size) + "-kWh batt
ery.")

def get_range(self):

Classes 181
"""Print a statement about the range this battery provides."""
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270

message = "This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)

class ElectricCar(Car):
"""Models aspects of a car, specific to electric vehicles."""
def __init__(self, make, model, year):
"""
Initialize attributes of the parent class.
Then initialize attributes specific to an electric car.
"""
super().__init__(make, model, year)
self.battery = Battery()
Now we can make a new file called my_electric_car.py, import the
ElectricCar class, and make an electric car:
my_electric_ from car import ElectricCar
car.py my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
This has the same output we saw earlier, even though most of the logic
is hidden away in a module:
2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.
Importing Multiple Classes from a Module
You can import as many classes as you need into a program file. If we
want to make a regular car and an electric car in the same file, we need
to import both classes,
Car and ElectricCar :
my_cars.py u from car import Car, ElectricCar
v my_beetle = Car('volkswagen', 'beetle', 2016) print(my_beetle.get_descriptive_name())
w my_tesla = ElectricCar('tesla', 'roadster', 2016) print(my_tesla.get_descriptive_name())

182 Chapter 9
You import multiple classes from a module by separating each class
with a comma u . Once you’ve imported the necessar y classes, you’re free
to make as many instances of each class as you need. In this example we make a regular Volkswagen Beetle at v and an elec -
tric Tesla Roadster at w :
2016 Volkswagen Beetle
Importing an Entire Module
You can also import an entire module and then access the classes you need
using dot notation. This approach is simple and results in code that is easy
to read. Because ever y call that creates an instance of a class includes the
module name, you won’t have naming conflicts with any names used in the
current file.
Here’s what it looks like to import the entire
car module and then create
a regular car and an electric car:
my_cars.py u import car
v my_beetle = car.Car('volkswagen', 'beetle', 2016) print(my_beetle.get_descriptive_name())
w my_tesla = car.ElectricCar('tesla', 'roadster', 2016) print(my_tesla.get_descriptive_name())
At u we import the entire car module. We then access the classes we
need through the
module_name .class_name syntax. At v we again create a
Volkswagen Beetle, and at w we create a Tesla Roadster.
Importing All Classes from a Module
You can import ever y class from a module using the following syntax:
from module_name import *
This method is not recommended for two reasons. First, it’s helpful
to be able to read the
import statements at the top of a file and get a clear
sense of which classes a program uses. With this approach it’s unclear which
classes you’re using from the module. This approach can also lead to confu -
sion with names in the file. If you accidentally import a class with the same
name as something else in your program file, you can create errors that are
hard to diagnose. I show this here because even though it’s not a recom -
mended approach, you’re likely to see it in other people’s code. If you need to import many classes from a module, you’re better off
importing the entire module and using the
module_name.class_name sy nt a x.

Classes 18 3
You won’t see all the classes used at the top of the file, but you’ll see clearly
where the module is used in the program. You’ll also avoid the potential
naming conflicts that can arise when you import ever y class in a module.
Importing a Module into a Module
to keep any one file from growing too large and avoid storing unrelated
classes in the same module. When you store your classes in several modules,
you may find that a class in one module depends on a class in another mod-
ule. When this happens, you can import the required class into the first
module. For example, let’s store the
Car class in one module and the ElectricCar
and
Battery classes in a separate module. We’ll make a new module called
electric_car.py —replacing the electric_car.py file we created earlier—and copy
just the
Battery and ElectricCar classes into this file :
electric_car.py """A set of classes that can be used to represent electric cars."""
u from car import Car class Battery():
--snip--

class ElectricCar(Car):
--snip--
The class ElectricCar needs access to its parent class Car , so we import
Car directly into the module at u . If we forget this line, Python will raise an
error when we tr y to make an
ElectricCar instance. We also need to update
the
Car module so it contains only the Car class:
car.py """A class that can be used to represent a car."""
class Car():
--snip--
Now we can import from each module separately and create whatever
kind of car we need:
my_cars.py u from car import Car
from electric_car import ElectricCar
my_beetle = Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
print(my_tesla.get_descriptive_name())

18 4 Chapter 9
At u we import Car from its module, and ElectricCar from its module.
We then create one regular car and one electric car. Both kinds of cars are
created correctly:
2016 Volkswagen Beetle
As you can see, Python gives you many options for how to structure code
in a large project. It’s important to know all these possibilities so you can
determine the best ways to organize your projects as well as understand
other people’s projects. When you’re starting out, keep your code structure simple. Tr y
doing ever ything in one file and moving your classes to separate modules
once ever ything is working. If you like how modules and files interact, tr y
storing your classes in modules when you start a project. Find an approach
that lets you write code that works, and go from there.
t ry It y ourself
9 -10 . Imported Restaurant: Using your latest Restaurant class, store it in a mod-
ule . Make a separate file that imports
Restaurant . Make a Restaurant instance,
and call one of
Restaurant ’s methods to show that the import statement is work -
ing properly .
Store the classes
User , Privileges , and Admin in one module . Create a sepa -
rate file, make an
Admin instance, and call show_privileges() to show that
everything is working correctly .
9 -12 . Multiple Modules: Store the
User class in one module, and store the
Privileges and Admin classes in a separate module . In a separate file, create
an
Admin instance and call show_privileges() to show that everything is still
working correctly .
the Python s tandard library
The Python standard library is a set of modules included with ever y Python
installation. Now that you have a basic understanding of how classes work,
you can start to use modules like these that other programmers have writ -
ten. You can use any function or class in the standard librar y by including
a simple
import statement at the top of your file. Let’s look at one class,
OrderedDict , from the module collections .

Classes 185
Dictionaries allow you to connect pieces of information, but they don’t
keep track of the order in which you add key-value pairs. If you’re creating
a dictionar y and want to keep track of the order in which key-value pairs
are added, you can use the
OrderedDict class from the collections module.
Instances of the
OrderedDict class behave almost exactly like dictionaries
except they keep track of the order in which key-value pairs are added. Let’s revisit the favorite_languages.py example from Chapter 6. This time
we’ll keep track of the order in which people respond to the poll:
favorite_ u from collections import OrderedDict
languages.py v favorite_languages = OrderedDict()
w favorite_languages['jen'] = 'python'favorite_languages['sarah'] = 'c'
favorite_languages['edward'] = 'ruby'
favorite_languages['phil'] = 'python'
x for name, language in favorite_languages.items(): print(name.title() + "'s favorite language is " +
language.title() + ".")
We begin by importing the OrderedDict class from the module
collections at u . At v we create an instance of the OrderedDict class
and store this instance in
favorite_languages . Notice there are no curly
brackets; the call to
OrderedDict() creates an empty ordered dictionar y
for us and stores it in
favorite_languages . We then add each name and lan -
guage to
favorite_languages one at a time w . Now when we loop through
favorite_languages at x, we know we’ll always get responses back in the
Jen's favorite language is Python.
Sarah's favorite language is C.
Edward's favorite language is Ruby.
Phil's favorite language is Python.
This is a great class to be aware of because it combines the main benefit
of lists (retaining your original order) with the main feature of dictionaries
(connecting pieces of information). As you begin to model real-world situa -
tions that you care about, you’ll probably come across a situation where an
standard librar y, you’ll become familiar with a number of modules like this
note You can also download modules from external sources. You’ll see a number of these
examples in Part II, where we’ll need external modules to complete each project.

18 6 Chapter 9
try It y ourself
9 -13 . OrderedDict Rewrite: Start with Exercise 6 -4 (page 108), where you
used a standard dictionary to represent a glossary . Rewrite the program using
the
OrderedDict class and make sure the order of the output matches the order
in which key-value pairs were added to the dictionary .
9 -14 . Dice: The module
random contains functions that generate random num -
bers in a variety of ways . The function
randint() returns an integer in the
range you provide . The following code returns a number between 1 and 6:
from random import randint
x = randint(1, 6)
Make a class Die with one attribute called sides , which has a default
value of 6 . Write a method called
roll_die() that prints a random number
between 1 and the number of sides the die has . Make a 6 -sided die and roll
it 10 times . Make a 10 -sided die and a 20 -sided die . Roll each die 10 times .
9 -15 . Python Module of the Week: One excellent resource for exploring the
Python standard library is a site called Python Module of the Week . Go to
looks interesting to you and read about it, or explore the documentation of
the
collections and random modules .
styling Classes
A few styling issues related to classes are worth clarifying, especially as your
programs become more complicated.
Class names should be written in CamelCaps . To do this, capitalize the
first letter of each word in the name, and don’t use underscores. Instance
and module names should be written in lowercase with underscores between
words. Ever y class should have a docstring immediately following the class defi -
nition. The docstring should be a brief description of what the class does,
and you should follow the same formatting conventions you used for writing
docstrings in functions. Each module should also have a docstring describ -
ing what the classes in a module can be used for. You can use blank lines to organize code, but don’t use them exces -
sively. Within a class you can use one blank line between methods, and
within a module you can use two blank lines to separate classes. If you need to import a module from the standard librar y and a module
that you wrote, place the import statement for the standard librar y module

Classes 187
first. Then add a blank line and the import statement for the module you
wrote. In programs with multiple import statements, this convention makes
it easier to see where the different modules used in the program come from.
summary
In this chapter you learned how to write your own classes. You learned
how to store information in a class using attributes and how to write
methods that give your classes the behavior they need. You learned to
w rite
__init__() methods that create instances from your classes with
exactly the attributes you want. You saw how to modify the attributes of
an instance directly and through methods. You learned that inheritance
can simplify the creation of classes that are related to each other, and you
learned to use instances of one class as attributes in another class to keep
each class simple. You saw how storing classes in modules and importing classes you need
into the files where they’ll be used can keep your projects organized. You
started learning about the Python standard librar y, and you saw an example
based on the
OrderedDict class from the collections module. Finally, you
learned to style your classes using Python conventions. In Chapter 10 you’ll learn to work with files so you can save the work
you’ve done in a program and the work you’ve allowed users to do. You’ll
respond to errors when they arise.

10
fIles an D exC eP tIons
Now that you’ve mastered the basic skills
you need to write organized programs
that are easy to use, it’s time to think about
making your programs even more relevant and
usable. In this chapter you’ll learn to work with files
so your programs can quickly analyze lots of data.
You’ll learn to handle errors so your programs don’t crash when they
encounter unexpected situations. You’ll learn about exceptions , which are
special objects Python creates to manage errors that arise while a program
is running. You’ll also learn about the
json module, which allows you to save
user data so it isn’t lost when your program stops running. Learning to work with files and save data will make your programs
easier for people to use. Users will be able to choose what data to enter and
when to enter it. People can run your program, do some work, and then
close the program and pick up where they left off later. Learning to handle
exceptions will help you deal with situations in which files don’t exist and
deal with other problems that can cause your programs to crash. This will

19 0 Chapter 10
it comes from innocent mistakes or from malicious attempts to break your
programs. With the skills you’ll learn in this chapter, you’ll make your pro-
grams more applicable, usable, and stable.
r eading from a f ile
An incredible amount of data is available in text files. Text files can con -
tain weather data, traffic data, socioeconomic data, literar y works, and
more. Reading from a file is particularly useful in data analysis applica -
tions, but it’s also applicable to any situation in which you want to ana -
lyze or modify information stored in a file. For example, you can write a
program that reads in the contents of a text file and rewrites the file with
formatting that allows a browser to display it. When you want to work with the information in a text file, the first step
is to read the file into memor y. You can read the entire contents of a file, or
you can work through the file one line at a time.
To begin, we need a file with a few lines of text in it. Let’s start with a file
that contains pi to 30 decimal places with 10 decimal places per line:
pi_digits.txt 3.1415926535
8979323846
2643383279
To tr y the following examples yourself, you can enter these lines in an
editor and save the file as pi_digits.txt , or you can download the file from the
book’s resources through https://www.nostarch.com/pythoncrashcourse/ . Save
the file in the same director y where you’ll store this chapter’s programs. Here’s a program that opens this file, reads it, and prints the contents
of the file to the screen:
print(contents)
The first line of this program has a lot going on. Let’s start by looking
at the
open() function. To do any work with a file, even just printing its con -
tents, you first need to open the file to access it. The
open() function needs
one argument: the name of the file you want to open. Python looks for this
file in the director y where the program that’s currently being executed is
stored. In this example, fil e _ re a d e r.p y is currently running, so Python looks
for pi_digits.txt in the director y where fil e _ re a d e r.p y is stored. The
open()
function returns an object representing the file. Here,
open('pi_digits.txt')
returns an object representing pi_digits.txt . Python stores this object in
file_object , which we’ll work with later in the program.
The key word
with closes the file once access to it is no longer needed.
Notice how we call
open() in this program but not close() . You could open

Files and Exceptions 191
and close the file by calling open() and close() , but if a bug in your program
prevents the
close() statement from being executed, the file may never
close. This may seem trivial, but improperly closed files can cause data
to be lost or corrupted. And if you call
close() too early in your program,
you’ll find yourself tr ying to work with a closed file (a file you can’t access),
which leads to more errors. It’s not always easy to know exactly when you
should close a file, but with the structure shown here, Python will figure that
out for you. All you have to do is open the file and work with it as desired,
trusting that Python will close it automatically when the time is right. Once we have a file object representing pi_digits.txt , we use the
method in the second line of our program to read the entire contents of
the file and store it as one long string in
contents . When we print the value
of
contents , we get the entire text file back:
3.1415926535
8979323846
2643383279
The only difference between this output and the original file is the
extra blank line at the end of the output. The blank line appears because
read() returns an empty string when it reaches the end of the file; this empty
string shows up as a blank line. If you want to remove the extra blank line,
you can use
rstrip() in the print st atement :
with open('pi_digits.txt') as file_object:
print(contents.rstrip())
Recall that Python’s rstrip() method removes, or strips, any whitespace
characters from the right side of a string. Now the output matches the con -
tents of the original file exactly:
3.1415926535
8979323846
2643383279
Fi le Paths
When you pass a simple filename like pi_digits.txt to the open() function,
Python looks in the director y where the file that’s currently being executed
(that is, your .py program file) is stored.
Sometimes, depending on how you organize your work, the file you want
to open won’t be in the same director y as your program file. For example,
you might store your program files in a folder called python_work ; inside
python_work , you might have another folder called text_files to distinguish your
program files from the text files they’re manipulating. Even though text_files
is in python_work , just passing
open() the name of a file in text_files won’t work,
because Python will only look in python_work and stop there; it won’t go on

19 2 Chapter 10
and look in text_files. To get Python to open files from a director y other
than the one where your program file is stored, you need to provide a file
path , which tells Python to look in a specific location on your system.
Because text_files is inside python_work , you could use a relative file path
to open a file from text_files. A relative file path tells Python to look for a given
location relative to the director y where the currently running program file
is stored. On Linux and OS X, you’d write:
with open('text_files/filename.txt') as file_object:
This line tells Python to look for the desired .txt file in the folder
text_files and assumes that text_files is located inside python_work (which it is).
On Windows systems, you use a backslash (
\) instead of a for ward slash ( /)
in the file path:
with open('text_files\filename.txt') as file_object:
You can also tell Python exactly where the file is on your computer
regardless of where the program that’s being executed is stored. This
is called an absolute file path . You use an absolute path if a relative path
doesn’t work. For instance, if you’ve put text_files in some folder other than
python_work —say, a folder called other_ files —then just passing
open() the
path
'text_files/filename.txt' won’t work because Python will only look
for that location inside python_work . You’ll need to write out a full path to
clarify where you want Python to look. Absolute paths are usually longer than relative paths, so it’s helpful to
store them in a variable and then pass that variable to
open() . On Linux and
OS X, absolute paths look like this:
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
and on Windows they look like this:
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
Using absolute paths, you can read files from any location on your sys -
tem. For now it’s easiest to store files in the same director y as your program
files or in a folder such as text_files within the director y that stores your pro -
gram files.
note Windows systems will sometimes interpret forward slashes in file paths correctly. If
you’re using Windows and you’re not getting the results you expect, make sure you try
using backslashes.

Files and Exceptions 193
When you’re reading a file, you’ll often want to examine each line of the file.
You might be looking for certain information in the file, or you might want to
modify the text in the file in some way. For example, you might want to read
through a file of weather data and work with any line that includes the word
sunny in the description of that day’s weather. In a news report, you might
look for any line with the tag
and rewrite that line with a specific
kind of formatting. You can use a
for loop on the file object to examine each line from a
file one at a time:
v with open(filename) as file_object:
w for line in file_object: print(line)
At u we store the name of the file we’re reading from in the variable
filename . This is a common convention when working with files. Because
the variable
filename doesn’t represent the actual file—it’s just a string tell -
ing Python where to find the file—you can easily swap out
'pi_digits.txt'
for the name of another file you want to work with. A fter we call
open() ,
an object representing the file and its contents is stored in the variable
file_object v . We again use the with syntax to let Python open and close
the file properly. To examine the file’s contents, we work through each line
in the file by looping over the file object w .
When we print each line, we find even more blank lines:
3.1415926535
8979323846
2643383279
These blank lines appear because an invisible newline character is
at the end of each line in the text file. The
newline each time we call it, so we end up with two newline characters at
the end of each line: one from the file and one from the
print st atement.
Using
rstrip() on each line in the print statement eliminates these extra
blank lines:
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line.rstrip())

19 4 Chapter 10
Now the output matches the contents of the file once again:
3.1415926535
8979323846
2643383279
Making a List of Lines from a File
W hen you use with , the file object returned by open() is only available inside
the
with block that contains it. If you want to retain access to a file’s contents
outside the
with block, you can store the file’s lines in a list inside the block
and then work with that list. You can process parts of the file immediately
and postpone some processing for later in the program. The following example stores the lines of pi_digits.txt in a list inside the
with block and then prints the lines outside the with block:
filename = 'pi_digits.txt'
with open(filename) as file_object:
v for line in lines: print(line.rstrip())
At u the readlines() method takes each line from the file and stores it
in a list. This list is then stored in
lines , which we can continue to work with
after the
with block ends. At v we use a simple for loop to print each line
from
lines . Because each item in lines corresponds to each line in the file,
the output matches the contents of the file exactly.
Working with a File’s Contents
A fter you’ve read a file into memor y, you can do whatever you want with
that data, so let’s briefly explore the digits of pi . First, we’ll attempt to build
a single string containing all the digits in the file with no whitespace in it:
pi_string.py filename = 'pi_digits.txt'
with open(filename) as file_object:
u pi_string = ''
v for line in lines: pi_string += line.rstrip()

w print(pi_string) print(len(pi_string))
We start by opening the file and storing each line of digits in a list, just
as we did in the previous example. At u we create a variable,
pi_string , to

Files and Exceptions 195
hold the digits of pi. We then create a loop that adds each line of digits to
pi_string and removes the newline character from each line v . At w we
print this string and also show how long the string is:
3.1415926535 8979323846 2643383279
36
The variable pi_string contains the whitespace that was on the left side
of the digits in each line, but we can get rid of that by using
of
rstrip() :
filename = 'pi_30_digits.txt'
with open(filename) as file_object:
pi_string = ''
for line in lines:
pi_string += line.strip()

print(pi_string)
print(len(pi_string))
Now we have a string containing pi to 30 decimal places. The string
is 32 characters long because it also includes the leading 3 and a decimal
point:
3.141592653589793238462643383279
32
note When Python reads from a text file, it interprets all text in the file as a string. If you
read in a number and want to work with that value in a numerical context, you’ll
have to convert it to an integer using the
int() function or convert it to a float using
the
float() function.
Large Files: One Million Digits
So far we’ve focused on analyzing a text file that contains only three lines,
but the code in these examples would work just as well on much larger
files. If we start with a text file that contains pi to 1,000,000 decimal places
instead of just 30, we can create a single string containing all these digits.
We don’t need to change our program at all except to pass it a different file.
We’ll also print just the first 50 decimal places, so we don’t have to watch a
million digits scroll by in the terminal:
pi_string.py filename = 'pi_million_digits.txt'
with open(filename) as file_object:

196 Chapter 10
pi_string = ''
for line in lines:
pi_string += line.strip()

print(pi_string[:52] + "...")
print(len(pi_string))
The output shows that we do indeed have a string containing pi to
1,000,000 decimal places:
3.14159265358979323846264338327950288419716939937510...
1000002
Python has no inherent limit to how much data you can work with; you
can work with as much data as your system’s memor y can handle.
note To run this program (and many of the examples that follow), you’ll need to download
the resources available at https://w w w.nostarch.com/pythoncrashcourse/ .
Is Your Birthday Contained in Pi?
I’ve always been curious to know if my birthday appears any where in the
digits of pi. Let’s use the program we just wrote to find out if someone’s
birthday appears any where in the first million digits of pi . We can do this
by expressing each birthday as a string of digits and seeing if that string
appears any where in
pi_string :
filename = 'pi_million_digits.txt'
with open(filename) as file_object:
pi_string = ''
for line in lines:
pi_string += line.rstrip()
u birthday = input("Enter your birthday, in the form mmddyy: ")
v if birthday in pi_string: print("Your birthday appears in the first million digits of pi!")
else:
print("Your birthday does not appear in the first million digits of
pi.")
At u we prompt for the user’s birthday, and then at v we check if that
string is in
pi_string . Let’s tr y it:
Enter your birthdate, in the form mmddyy: 120372
Your birthday appears in the first million digits of pi!
My birthday does appear in the digits of pi ! Once you’ve read from a
file, you can analyze its contents in just about any way you can imagine.

Files and Exceptions 197
try It y ourself
10 -1. Learning Python: Open a blank file in your text editor and write a few
lines summarizing what you’ve learned about Python so far . Start each line
with the phrase In Python you can... . Save the file as learning _python.txt in the
same directory as your exercises from this chapter . Write a program that reads
the file and prints what you wrote three times . Print the contents once by read-
ing in the entire file, once by looping over the file object, and once by storing
the lines in a list and then working with them outside the
with block .
10 -2 . Learning C: You can use the
replace() method to replace any word in a
string with a different word . Here’s a quick example showing how to replace
'dog' with 'cat' in a sentence:
>>> message = "I really like dogs."
>>> message.replace('dog', 'cat')
'I really like cats.'
Read in each line from the file you just created, learning _python.txt , and
replace the word Python with the name of another language, such as C . Print
each modified line to the screen .
writing to a f ile
One of the simplest ways to save data is to write it to a file. When you write
text to a file, the output will still be available after you close the terminal
containing your program’s output. You can examine output after a program
finishes running, and you can share the output files with others as well. You
can also write programs that read the text back into memor y and work with
it again later.
Writing to an Empty File
To write text to a file, you need to call open() with a second argument telling
Python that you want to write to the file. To see how this works, let’s write a
simple message and store it in a file instead of printing it to the screen:
write_ filename = 'programming.txt'
message.py u with open(filename, 'w') as file_object:
v file_object.write("I love programming.")
The call to open() in this example has two arguments u . The first argu-
ment is still the name of the file we want to open. The second argument,
'w' ,
tells Python that we want to open the file in write mode . You can open a file

19 8 Chapter 10
in read mode ( 'r' ), write mode ( 'w' ), append mode ( 'a' ), or a mode that allows
you to read and write to the file (
'r+' ). If you omit the mode argument,
Python opens the file in read-only mode by default. The
open() function automatically creates the file you’re writing to if it
doesn’t already exist. However, be careful opening a file in write mode (
'w' )
because if the file does exist, Python will erase the file before returning the
file object. At v we use the
write() method on the file object to write a string to
the file. This program has no terminal output, but if you open the file
programming.txt , you’ll see one line:
programming.txt I love programming.
This file behaves like any other file on your computer. You can open it,
write new text in it, copy from it, paste to it, and so forth.
note Python can only write strings to a text file. If you want to store numerical data in a
text file, you’ll have to convert the data to string format first using the
str() function.
Writing Multiple Lines
The write() function doesn’t add any newlines to the text you write. So if
you write more than one line without including newline characters, your
file may not look the way you want it to:
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.")
file_object.write("I love creating new games.")
If you open programming.txt , you’ll see the two lines squished together:
I love programming.I love creating new games.
Including newlines in your write() statements makes each string appear
on its own line:
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.\n")
file_object.write("I love creating new games.\n")
The output now appears on separate lines:
I love programming.
I love creating new games.

Files and Exceptions 19 9
You can also use spaces, tab characters, and blank lines to format your
output, just as you’ve been doing with terminal-based output.
Appending to a File
If you want to add content to a file instead of writing over existing content,
you can open the file in append mode . When you open a file in append mode,
Python doesn’t erase the file before returning the file object. Any lines you
write to the file will be added at the end of the file. If the file doesn’t exist
yet, Python will create an empty file for you. Let’s modif y wr ite _message.py by adding some new reasons we love pro -
gramming to the existing file programming.txt :
write_ filename = 'programming.txt'
message.py u with open(filename, 'a') as file_object:
v file_object.write("I also love finding meaning in large datasets.\
n") file_object.write("I love creating apps that can run in a browser.\
n")
At u we use the 'a' argument to open the file for appending rather
than writing over the existing file. At v we write two new lines, which are
programming.txt I love programming.
I love creating new games.
I also love finding meaning in large datasets.
I love creating apps that can run in a browser.
We end up with the original contents of the file, followed by the new
t ry It y ourself
10 -3. Guest: Write a program that prompts the user for their name . When they
respond, write their name to a file called guest.txt .
10 -4. Guest Book: Write a
while loop that prompts users for their name . When
they enter their name, print a greeting to the screen and add a line recording
their visit in a file called guest_book.txt . Make sure each entry appears on a
new line in the file .
10 -5. Programming Poll: Write a
while loop that asks people why they like
programming . Each time someone enters a reason, add their reason to a file
that stores all the responses .

200 Chapter 10
exceptions
Python uses special objects called exceptions to manage errors that arise dur-
ing a program’s execution. Whenever an error occurs that makes Python
unsure what to do next, it creates an exception object. If you write code
that handles the exception, the program will continue running. If you don’t
handle the exception, the program will halt and show a traceback , which
includes a report of the exception that was raised. Exceptions are handled with
try -except blocks. A try -except block asks
Python to do something, but it also tells Python what to do if an excep -
tion is raised. When you use
try -except blocks, your programs will continue
running even if things start to go wrong. Instead of tracebacks, which can
be confusing for users to read, users will see friendly error messages that
you write.
Handling the ZeroDivisionError Exception
Let’s look at a simple error that causes Python to raise an exception. You
probably know that it’s impossible to divide a number by zero, but let’s ask
Python to do it any way:
division.py print(5/0)
Of course Python can’t do this, so we get a traceback:
Traceback (most recent call last):
File "division.py", line 1, in
print(5/0)
u ZeroDivisionError: division by zero
The error reported at u in the traceback, ZeroDivisionError , is an excep -
tion object. Python creates this kind of object in response to a situation
where it can’t do what we ask it to. When this happens, Python stops the
program and tells us the kind of exception that was raised. We can use this
information to modify our program. We’ll tell Python what to do when this
kind of exception occurs; that way, if it happens again, we’re prepared.
Using try-except Blocks
When you think an error may occur, you can write a try -except block to
handle the exception that might be raised. You tell Python to tr y running
some code, and you tell it what to do if the code results in a particular kind
of exception. Here’s what a
try -except block for handling the ZeroDivisionError excep -
tion looks like:
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")

Files and Exceptions 201
We put print(5/0) , the line that caused the error, inside a try block. If
the code in a
try block works, Python skips over the except block. If the code
in the
try block causes an error, Python looks for an except block whose
error matches the one that was raised and runs the code in that block. In this example, the code in the
try block produces a ZeroDivisionError ,
so Python looks for an
except block telling it how to respond. Python then
runs the code in that block, and the user sees a friendly error message
You can't divide by zero!
If more code followed the try -except block, the program would continue
running because we told Python how to handle the error. Let’s look at an
example where catching an error can allow a program to continue running.
Using Exceptions to Prevent Crashes
Handling errors correctly is especially important when the program has
more work to do after the error occurs. This happens often in programs
that prompt users for input. If the program responds to invalid input appro -
priately, it can prompt for more valid input instead of crashing. Let’s create a simple calculator that does only division:
division.py print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
u first_number = input("\nFirst number: ") if first_number == 'q':
break
v second_number = input("Second number: ") if second_number == 'q':
break
This program prompts the user to input a first_number u and, if the
user does not enter q to quit, a
second_number v . We then divide these two
numbers to get an
answer w . This program does nothing to handle errors,
so asking it to divide by zero causes it to crash:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 5
Second number: 0
Traceback (most recent call last):
File "division.py", line 9, in
ZeroDivisionError: division by zero

202 Chapter 10
It’s bad that the program crashed, but it’s also not a good idea to let
users see tracebacks. Nontechnical users will be confused by them, and in
a malicious setting, attackers will learn more than you want them to know
from a traceback. For example, they’ll know the name of your program
file, and they’ll see a part of your code that isn’t working properly. A skilled
attacker can sometimes use this information to determine which kind of
attacks to use against your code.
The else Block
We can make this program more error resistant by wrapping the line that
might produce errors in a
try -except block. The error occurs on the line
that performs the division, so that’s where we’ll put the
try -except block.
This example also includes an
else block. Any code that depends on the try
block executing successfully goes in the
else block:
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
u try: answer = int(first_number) / int(second_number)
v except ZeroDivisionError: print("You can't divide by 0!")
We ask Python to tr y to complete the division operation in a try
block u, which includes only the code that might cause an error. Any
code that depends on the
try block succeeding is added to the else block.
In this case if the division operation is successful, we use the
else block to
print the result w .
The
except block tells Python how to respond when a ZeroDivisionError
arises v. If the
try statement doesn’t succeed because of a division by
zero error, we print a friendly message telling the user how to avoid this
kind of error. The program continues to run, and the user never sees a
traceback:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 5
Second number: 0
You can't divide by 0!

Files and Exceptions 203
First number: 5
Second number: 2
2.5
First number: q
The try -except -else block works like this: Python attempts to run the
code in the
try statement. The only code that should go in a try st atement
is code that might cause an exception to be raised. Sometimes you’ll have
additional code that should run only if the
try block was successful; this
code goes in the
else block. The except block tells Python what to do in case
a certain exception arises when it tries to run the code in the
try st atement.
By anticipating likely sources of errors, you can write robust programs
that continue to run even when they encounter invalid data and missing
resources. Your code will be resistant to innocent user mistakes and mali -
cious attacks.
Handling the FileNotFoundError Exception
One common issue when working with files is handling missing files. The
file you’re looking for might be in a different location, the filename may
be misspelled, or the file may not exist at all. You can handle all of these
situations in a straightfor ward way with a
try -except block.
Let’s tr y to read a file that doesn’t exist. The following program tries
to read in the contents of Alice in Wonderland , but I haven’t saved the file
alice.txt in the same director y as alice.py :
alice.py filename = 'alice.txt'
with open(filename) as f_obj:
Python can’t read from a missing file, so it raises an exception:
Traceback (most recent call last):
File "alice.py", line 3, in
with open(filename) as f_obj:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
The last line of the traceback reports a FileNotFoundError : this is the
exception Python creates when it can’t find the file it’s tr ying to open. In
this example, the
open() function produces the error, so to handle it, the try
block will begin just before the line that contains
open() :
filename = 'alice.txt'
try:
with open(filename) as f_obj:

204 Chapter 10
except FileNotFoundError:
msg = "Sorry, the file " + filename + " does not exist."
print(msg)
In this example, the code in the try block produces a FileNotFoundError ,
so Python looks for an
except block that matches that error. Python then
runs the code in that block, and the result is a friendly error message
Sorry, the file alice.txt does not exist.
The program has nothing more to do if the file doesn’t exist, so the
error-handling code doesn’t add much to this program. Let’s build on this
example and see how exception handling can help when you’re working
with more than one file.
Ana lyzi ng Text
You can analyze text files containing entire books. Many classic works of
literature are available as simple text files because they are in the pub -
lic domain. The texts used in this section come from Project Gutenberg
( http://gutenberg.org/ ). Project Gutenberg maintains a collection of liter -
ar y works that are available in the public domain, and it’s a great resource
if you’re interested in working with literar y texts in your programming
projects. Let’s pull in the text of Alice in Wonderland and tr y to count the number
of words in the text. We’ll use the string method
split() , which can build a
list of words from a string. Here’s what
split() does with a string containing
just the title
"Alice in Wonderland" :
>>> title = "Alice in Wonderland"
>>> title.split()
['Alice', 'in', 'Wonderland']
The split() method separates a string into parts wherever it finds a
space and stores all the parts of the string in a list. The result is a list of
words from the string, although some punctuation may also appear with
some of the words. To count the number of words in Alice in Wonderland ,
we’ll use
split() on the entire text. Then we’ll count the items in the list to
get a rough idea of the number of words in the text:
filename = 'alice.txt'
try:
with open(filename) as f_obj:
except FileNotFoundError:
msg = "Sorry, the file " + filename + " does not exist."
print(msg)

Files and Exceptions 205
else:
# Count the approximate number of words in the file.
u words = contents.split()
v num_words = len(words)
w print("The file " + filename + " has about " + str(num_words) +
" words.")
I moved the file alice.txt to the correct director y, so the try block will
work this time. At u we take the string
contents , which now contains the
entire text of Alice in Wonderland as one long string, and use the
split()
method to produce a list of all the words in the book. When we use
len() on
this list to examine its length, we get a good approximation of the number
of words in the original string v . At w we print a statement that reports
how many words were found in the file. This code is placed in the
else block
because it will work only if the code in the
try block was executed success -
fully. The output tells us how many words are in alice.txt :
The file alice.txt has about 29461 words.
The count is a little high because extra information is provided by the
publisher in the text file used here, but it’s a good approximation of the
length of Alice in Wonderland .
Working with Multiple Files
Let’s add more books to analyze. But before we do, let’s move the bulk of
this program to a function called
count_words() . By doing so, it will be easier
to run the analysis for multiple books:
word_count.py def count_words(filename):
u """Count the approximate number of words in a file.""" try:
with open(filename) as f_obj:
except FileNotFoundError:
msg = "Sorry, the file " + filename + " does not exist."
print(msg)
else:
# Count approximate number of words in the file.
words = contents.split()
num_words = len(words)
print("The file " + filename + " has about " + str(num_words)
+
" words.")
filename = 'alice.txt'
count_words(filename)
Most of this code is unchanged. We simply indented it and moved it
into the body of
count_words() . It’s a good habit to keep comments up to date
when you’re modifying a program, so we changed the comment to a doc -
string and reworded it slightly u .

206 Chapter 10
Now we can write a simple loop to count the words in any text we want
to analyze. We do this by storing the names of the files we want to analyze
in a list, and then we call
count_words() for each file in the list. We’ll tr y to
count the words for Alice in Wonderland , Siddhartha, Moby Dick, and Little
Women , which are all available in the public domain. I’ve intentionally left
siddhartha.txt out of the director y containing word _ count.py , so we can see
how well our program handles a missing file:
def count_words(filename):
--snip--
filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_wom
en.txt']
for filename in filenames:
count_words(filename)
The missing siddhartha.txt file has no effect on the rest of the program’s
execution:
The file alice.txt has about 29461 words.
Sorry, the file siddhartha.txt does not exist.
The file moby_dick.txt has about 215136 words.
The file little_women.txt has about 189079 words.
Using the try -except block in this example provides two significant
advantages. We prevent our users from seeing a traceback, and we let the
program continue analyzing the texts it’s able to find. If we don’t catch
the
FileNotFoundError that siddhartha.txt raised, the user would see a full
traceback, and the program would stop running after tr ying to analyze
Siddhartha . It would never analyze Moby Dick or Little Women.
Failing Silently
In the previous example, we informed our users that one of the files
was unavailable. But you don’t need to report ever y exception you catch.
Sometimes you’ll want the program to fail silently when an exception occurs
and continue on as if nothing happened. To make a program fail silently, you
write a
try block as usual, but you explicitly tell Python to do nothing in the
except block. Python has a pass statement that tells it to do nothing in a block:
def count_words(filename):
"""Count the approximate number of words in a file."""
try:
--snip--
except FileNotFoundError:
u pass else:
--snip--
filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_wom
en.txt']
for filename in filenames:
count_words(filename)

Files and Exceptions 207
The only difference between this listing and the previous one is the
pass statement at u. Now when a FileNotFoundError is raised, the code in
the
except block runs, but nothing happens. No traceback is produced,
and there’s no output in response to the error that was raised. Users see
the word counts for each file that exists, but they don’t see any indication
The file alice.txt has about 29461 words.
The file moby_dick.txt has about 215136 words.
The file little_women.txt has about 189079 words.
The pass statement also acts as a placeholder. It’s a reminder that you’re
choosing to do nothing at a specific point in your program’s execution
and that you might want to do something there later. For example, in this
program we might decide to write any missing filenames to a file called
missing_files.txt . Our users wouldn’t see this file, but we’d be able to read
the file and deal with any missing texts.
Deciding Which Errors to Report
How do you know when to report an error to your users and when to fail
silently? If users know which texts are supposed to be analyzed, they might
appreciate a message informing them why some texts were not analyzed. If
users expect to see some results but don’t know which books are supposed
to be analyzed, they might not need to know that some texts were unavail -
able. Giving users information they aren’t looking for can decrease the
usability of your program. Python’s error-handling structures give you fine-
grained control over how much to share with users when things go wrong;
it’s up to you to decide how much information to share. Well-written, properly tested code is not ver y prone to internal errors,
such as syntax or logical errors. But ever y time your program depends on
something external, such as user input, the existence of a file, or the avail -
ability of a network connection, there is a possibility of an exception being
raised. A little experience will help you know where to include exception
handling blocks in your program and how much to report to users about
errors that arise.
t ry It y ourself
10 - 6. Addition: One common problem when prompting for numerical input
occurs when people provide text instead of numbers . When you try to convert
the input to an
int , you’ll get a TypeError . Write a program that prompts for
two numbers . Add them together and print the result . Catch the
TypeError if
either input value is not a number, and print a friendly error message . Test your
program by entering two numbers and then by entering some text instead of a
number .
(continued)

208 Chapter 10
1 0 - 7. Addition Calculator: Wrap your code from Exercise 10 - 6 in a while loop
so the user can continue entering numbers even if they make a mistake and
enter text instead of a number .
10 -8. Cats and Dogs: Make two files, cats.txt and dogs.txt . Store at least three
names of cats in the first file and three names of dogs in the second file . Write
a program that tries to read these files and print the contents of the file to the
screen . Wrap your code in a
try-except block to catch the FileNotFound error,
and print a friendly message if a file is missing . Move one of the files to a dif-
ferent location on your system, and make sure the code in the
except block
executes properly .
10 - 9. Silent Cats and Dogs: Modify your
except block in Exercise 10 -8 to fail
silently if either file is missing .
10 -10. Common Words: Visit Project Gutenberg ( http://gutenberg.org/ )
and find a few texts you’d like to analyze . Download the text files for these
works, or copy the raw text from your browser into a text file on your
computer .
You can use the
count() method to find out how many times a word or
phrase appears in a string . For example, the following code counts the number
of times
'row' appears in a string:
>>> line = "Row, row, row your boat"
>>> line.count('row')
2
>>> line.lower().count('row')
3
Notice that converting the string to lowercase using lower() catches
all appearances of the word you’re looking for, regardless of how it’s
formatted .
Write a program that reads the files you found at Project Gutenberg and
determines how many times the word
'the' appears in each text .
storing data
Many of your programs will ask users to input certain kinds of information.
You might allow users to store preferences in a game or provide data for a
visualization. Whatever the focus of your program is, you’ll store the infor -
mation users provide in data structures such as lists and dictionaries. When
users close a program, you’ll almost always want to save the information
they entered. A simple way to do this involves storing your data using the
json module.

Files and Exceptions 209
The json module allows you to dump simple Python data structures into a
file and load the data from that file the next time the program runs. You can
also use
json to share data between different Python programs. Even better,
the JSON data format is not specific to Python, so you can share data you
store in the JSON format with people who work in many other programming
languages. It’s a useful and portable format, and it’s easy to learn.
note The JSON ( JavaScript Object Notation) format was originally developed for JavaScript.
However, it has since become a common format used by many languages, including
Python.
Let’s write a short program that stores a set of numbers and another pro -
gram that reads these numbers back into memor y. The first program will
use
json.dump() to store the set of numbers, and the second program will use
The
json.dump() function takes two arguments: a piece of data to
store and a file object it can use to store the data. Here’s how you can use
json.dump() to store a list of numbers:
number_ import json
writer.py numbers = [2, 3, 5, 7, 11, 13]
u filename = 'numbers.json'
v with open(filename, 'w') as f_obj:
w json.dump(numbers, f_obj)
We first import the json module and then create a list of numbers to
work with. At u we choose a filename in which to store the list of numbers.
It’s customar y to use the file extension .json to indicate that the data in
the file is stored in the JSON format. Then we open the file in write mode,
which allows
json to write the data to the file v . At w we use the json.dump()
function to store the list
numbers in the file numbe rs.json .
This program has no output, but let’s open the file numbe rs.json and
look at it. The data is stored in a format that looks just like Python:
[2, 3, 5, 7, 11, 13]
Now we’ll write a program that uses json.load() to read the list back into
memor y:
number_ import json
v with open(filename) as f_obj:
print(numbers)

210 Chapter 10
At u we make sure to read from the same file we wrote to. This time
when we open the file, we open it in read mode because Python only needs
to read from the file v . At w we use the
information stored in numbe rs.json , and we store it in the variable
numbers .
Finally we print the recovered list of numbers and see that it’s the same list
created in number_writer.py :
[2, 3, 5, 7, 11, 13]
This is a simple way to share data between two programs.
Saving data with json is useful when you’re working with user-generated
data, because if you don’t store your user’s information somehow, you’ll
lose it when the program stops running. Let’s look at an example where we
prompt the user for their name the first time they run a program and then
remember their name when they run the program again. Let’s start by storing the user’s name:
remember_ import json
with open(filename, 'w') as f_obj:
w print("We'll remember you when you come back, " + username + "!")

At u we prompt for a username to store. Next, we use json.dump() ,
passing it a username and a file object, to store the username in a file v .
Then we print a message informing the user that we’ve stored their
information w:
We'll remember you when you come back, Eric!
Now let’s write a new program that greets a user whose name has
greet_user.py import json
with open(filename) as f_obj:
v print("Welcome back, " + username + "!")

Files and Exceptions 2 11
into the variable
welcome them back v :
Welcome back, Eric!
We need to combine these two programs into one file. When someone
runs remember_me.py , we want to retrieve their username from memor y if
try block that attempts to recover the
except block
prompt for a username and store it in username.json for next time:
remember_ import json
# Otherwise, prompt for the username and store it.
try:
u with open(filename) as f_obj:
w except FileNotFoundError:
y with open(filename, 'w') as f_obj: json.dump(username, f_obj)
print("We'll remember you when you come back, " + username + "!
")
else:
print("Welcome back, " + username + "!")
There’s no new code here; blocks of code from the last two examples
are just combined into one file. At u we tr y to open the file username.json .
If this file exists, we read the username back into memor y v and print a
message welcoming back the user in the
else block. If this is the first time
the user runs the program, username.json won’t exist and a
FileNotFoundError
will occur w . Python will move on to the
except block where we prompt the
user to enter their username x . We then use
json.dump() to store the user-
name and print a greeting y .
Whichever block executes, the result is a username and an appropriate
greeting. If this is the first time the program runs, this is the output:
We'll remember you when you come back, Eric!
Other w ise:
Welcome back, Eric!
This is the output you see if the program was already run at least once.

212 Chapter 10
Refactoring
Often, you’ll come to a point where your code will work, but you’ll recog-
nize that you could improve the code by breaking it up into a series of func -
tions that have specific jobs. This process is called refactoring . Refactoring
makes your code cleaner, easier to understand, and easier to extend. We can refactor remember_me.py by moving the bulk of its logic into one
or more functions. The focus of remember_me.py is on greeting the user, so
let’s move all of our existing code into a function called
greet_user() :
remember_ import json
me.py def greet_user():
u """Greet the user by name.""" filename = 'username.json'
try:
with open(filename) as f_obj:
except FileNotFoundError:
with open(filename, 'w') as f_obj:
print("We'll remember you when you come back, " + username
+ "!")
else:
print("Welcome back, " + username + "!")
greet_user()
Because we’re using a function now, we update the comments with a
docstring that reflects how the program currently works u . This file is a
little cleaner, but the function
greet_user() is doing more than just greeting
the user—it’s also retrieving a stored username if one exists and prompting
for a new username if one doesn’t exist. Let’s refactor
greet_user() so it’s not doing so many different tasks.
We’ll start by moving the code for retrieving a stored username to a sepa -
rate function:
import json
try:
with open(filename) as f_obj:
except FileNotFoundError:
v return None else:

Files and Exceptions 213
def greet_user():
"""Greet the user by name."""
else:
with open(filename, 'w') as f_obj:
print("We'll remember you when you come back, " + username
+ "!")
greet_user()
The new function get_stored_username() has a clear purpose, as stated
in the docstring at u . This function retrieves a stored username and returns
the username if it finds one. If the file username.json doesn’t exist, the func-
tion returns
None v . This is good practice: a function should either return
the value you’re expecting, or it should return
None . This allows us to per -
form a simple test with the return value of the function. At w we print a
welcome back message to the user if the attempt to retrieve a username
was successful, and if it doesn’t, we prompt for a new username. We should factor one more block of code out of
greet_user() . If the
username doesn’t exist, we should move the code that prompts for a
new username to a function dedicated to that purpose:
import json
--snip--
with open(filename, 'w') as f_obj:
def greet_user():
"""Greet the user by name."""
print("Welcome back, " + username + "!")
else:
print("We'll remember you when you come back, " + username + "!")
greet_user()

214 Chapter 10
Each function in this final version of remember_me.py has a single, clear
purpose. We call
greet_user() , and that function prints an appropriate mes -
sage: it either welcomes back an existing user or greets a new user. It does
this by calling
get_stored_username() , which is responsible only for retrieving
a stored username if one exists. Finally,
if necessar y, which is responsible only for getting a new username and stor -
ing it. This compartmentalization of work is an essential part of writing
clear code that will be easy to maintain and extend.
t ry It y ourself
1 0 -11 . Favorite Number: Write a program that prompts for the user’s favorite
number . Use
json.dump() to store this number in a file . Write a separate pro-
gram that reads in this value and prints the message, “I know your favorite
number! It’s _____ .”
10 -12 . Favorite Number Remembered: Combine the two programs from
Exercise 10 -11 into one file . If the number is already stored, report the favorite
number to the user . If not, prompt for the user’s favorite number and store it in a
file . Run the program twice to see that it works .
10 -13 . Verify User: The final listing for remember_me.py assumes either that the
user has already entered their username or that the program is running for the
first time . We should modify it in case the current user is not the person who
last used the program .
Before printing a welcome back message in
greet_user() , ask the user if
this is the correct username . If it’s not, call
summary
In this chapter, you learned how to work with files. You learned to read an
entire file at once and read through a file’s contents one line at a time. You
learned to write to a file and append text onto the end of a file. You read
about exceptions and how to handle the exceptions you’re likely to see in
your programs. Finally, you learned how to store Python data structures so
you can save information your users provide, preventing them from having
to start over each time they run a program.
In Chapter 11 you’ll learn efficient ways to test your code. This will help
you trust that the code you develop is correct, and it will help you identify
bugs that are introduced as you continue to build on the programs you’ve
w ritten.

11
test Ing y our CoD e
When you write a function or a class, you
can also write tests for that code. Testing
proves that your code works as it’s supposed
to in response to all the input types it’s designed
to receive. When you write tests, you can be confident
that your code will work correctly as more people
begin to use your programs. You’ll also be able to test
gram’s existing behavior. Ever y programmer makes mistakes, so ever y
programmer must test their code often, catching problems before users
encounter them. In this chapter you’ll learn to test your code using tools in Python’s
unittest module. You’ll learn to build a test case and check that a set of
inputs results in the output you want. You’ll see what a passing test looks
like and what a failing test looks like, and you’ll learn how a failing test can
you’ll start to understand how many tests to write for a project.

216 Chapter 11
testing a f unction
To learn about testing, we need code to test. Here’s a simple function that
takes in a first and last name, and returns a neatly formatted full name:
name_ def get_formatted_name(first, last):
function.py """Generate a neatly formatted full name."""
full_name = first + ' ' + last
return full_name.title()
The function get_formatted_name() combines the first and last name
with a space in between to complete a full name, and then capitalizes and
returns the full name. To check that
get_formatted_name() works, let’s make
a program that uses this function. The program names.py lets users enter a
first and last name, and see a neatly formatted full name:
names.py from name_function import get_formatted_name
print("Enter 'q' at any time to quit.")
while True:
first = input("\nPlease give me a first name: ")
if first == 'q':
break
last = input("Please give me a last name: ")
if last == 'q':
break

formatted_name = get_formatted_name(first, last)
print("\tNeatly formatted name: " + formatted_name + '.')
This program imports get_formatted_name() from name_function.py . The
user can enter a series of first and last names, and see the formatted full
names that are generated:
Enter 'q' at any time to quit.
Please give me a first name: janis
Please give me a last name: joplin
Neatly formatted name: Janis Joplin.
Please give me a first name: bob
Please give me a last name: dylan
Neatly formatted name: Bob Dylan.
Please give me a first name: q
We can see that the names generated here are correct. But let’s say we
want to modify
get_formatted_name() so it can also handle middle names.
As we do so, we want to make sure we don’t break the way the function
handles names that have only a first and last name. We could test our code

by running names.py and entering a name like Janis Joplin ever y time we
modif y
get_formatted_name() , but that would become tedious. Fortunately,
Python provides an efficient way to automate the testing of a function’s
output. If we automate the testing of
get_formatted_name() , we can always be
confident that the function will work when given the kinds of names we’ve
written tests for.
Unit Tests and Test Cases
The module unittest from the Python standard librar y provides tools for
testing your code. A unit test verifies that one specific aspect of a function’s
behavior is correct. A test case is a collection of unit tests that together prove
that a function behaves as it’s supposed to, within the full range of situa -
tions you expect it to handle. A good test case considers all the possible
kinds of input a function could receive and includes tests to represent each
of these situations. A test case with full coverage includes a full range of unit
tests covering all the possible ways you can use a function. Achieving full
coverage on a large project can be daunting. It’s often good enough to write
tests for your code’s critical behaviors and then aim for full coverage only if
the project starts to see widespread use.
A Passing Test
The syntax for setting up a test case takes some getting used to, but once
you’ve set up the test case it’s straightfor ward to add more unit tests for your
functions. To write a test case for a function, import the
unittest module
and the function you want to test. Then create a class that inherits from
unittest.TestCase , and write a series of methods to test different aspects of
your function’s behav ior. Here’s a test case with one method that verifies that the function
get_formatted_name() works correctly when given a first and last name:
test_name_ import unittest
function.py from name_function import get_formatted_name
u class NamesTestCase(unittest.TestCase): """Tests for 'name_function.py'."""

def test_first_last_name(self):
"""Do names like 'Janis Joplin' work?"""
v formatted_name = get_formatted_name('janis', 'joplin')
w self.assertEqual(formatted_name, 'Janis Joplin')
unittest.main()
First, we import unittest and the function we want to test, get_formatted_
name()
. At u we create a class called NamesTestCase , which will contain a series
of unit tests for
get_formatted_name() . You can name the class anything you

218 Chapter 11
want, but it’s best to call it something related to the function you’re about to
test and to use the word Te s t in the class name. This class must inherit from
the class
unittest.TestCase so Python knows how to run the tests you write.
NamesTestCase contains a single method that tests one aspect of
get_formatted_name() . We call this method test_first_last_name() because
we’re verifying that names with only a first and last name are formatted cor -
rectly. Any method that starts with
test_ will be run automatically when we
run test_name_function.py . Within this test method, we call the function
we want to test and store a return value that we’re interested in testing. In
this example we call
get_formatted_name() with the arguments 'janis' and
'joplin' , and store the result in formatted_name v .
At w we use one of
unittest ’s most useful features: an assert method.
Assert methods verify that a result you received matches the result you
expected to receive. In this case, because we know
get_formatted_name() is
supposed to return a capitalized, properly spaced full name, we expect
the value in
formatted_name to be Janis Joplin . To check if this is true, we
use
unittest ’s assertEqual() method and pass it formatted_name and 'Janis
Joplin'
. The line
self.assertEqual(formatted_name, 'Janis Joplin')
says, “Compare the value in formatted_name to the string 'Janis Joplin' . If
they are equal as expected, fine. But if they don’t match, let me know!” The line
unittest.main() tells Python to run the tests in this file. When
we run test_name_function.py , we get the following output:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
The dot on the first line of output tells us that a single test passed. The
next line tells us that Python ran one test, and it took less than 0.001 sec -
onds to run. The final
OK tells us that all unit tests in the test case passed.
This output indicates that the function
get_formatted_name() will always
work for names that have a first and last name unless we modify the func -
tion. When we modify
get_formatted_name() , we can run this test again. If
the test case passes, we know the function will still work for names like
Janis Joplin.
A Failing Test
What does a failing test look like? Let’s modify get_formatted_name() so it can
handle middle names, but we’ll do so in a way that breaks the function for
names with just a first and last name, like Janis Joplin.

Here’s a new version of get_formatted_name() that requires a middle name
argument:
name_ def get_formatted_name(first, middle, last):
function.py """Generate a neatly formatted full name."""
full_name = first + ' ' + middle + ' ' + last
return full_name.title()
This version should work for people with middle names, but when we
test it, we see that we’ve broken the function for people with just a first
and last name. This time, running the file test_name_function.py gives this
output:
u E
======================================================================
v ERROR: test_first_last_name (__main__.NamesTestCase) ----------------------------------------------------------------------
w Traceback (most recent call last): File "test_name_function.py", line 8, in test_first_last_name
formatted_name = get_formatted_name('janis', 'joplin')
TypeError: get_formatted_name() missing 1 required positional argument
: 'last'
----------------------------------------------------------------------
x Ran 1 test in 0.000s
y FAILED (errors=1)
There’s a lot of information here because there’s a lot you might need
to know when a test fails. The first item in the output is a single
E u , which
tells us one unit test in the test case resulted in an error. Next, we see
that
test_first_last_name() in NamesTestCase caused an error v . K now ing
which test failed is critical when your test case contains many unit tests.
At w we see a standard traceback, which reports that the function call
get_formatted_name('janis', 'joplin') no longer works because it’s missing a
required positional argument. We also see that one unit test was run x . Finally, we see an additional
message that the overall test case failed and that one error occurred when
running the test case y . This information appears at the end of the output
so you see it right away; you don’t want to scroll up through a long output
listing to find out how many tests failed.
Responding to a Failed Test
What do you do when a test fails? Assuming you’re checking the right con -
ditions, a passing test means the function is behaving correctly and a fail -
ing test means there’s an error in the new code you wrote. So when a test

220 Chapter 11
fails, don’t change the test. Instead, fix the code that caused the test to fail.
Examine the changes you just made to the function, and figure out how
those changes broke the desired behavior.In this case
get_formatted_name() used to require only two parameters: a
first name and a last name. Now it requires a first name, middle name, and
last name. The addition of that mandator y middle name parameter broke
the desired behavior of
get_formatted_name() . The best option here is to
make the middle name optional. Once we do, our test for names like
Janis
Joplin
should pass again, and we should be able to accept middle names as
well. Let’s modify
get_formatted_name() so middle names are optional and
then run the test case again. If it passes, we’ll move on to making sure the
function handles middle names properly. To make middle names optional, we move the parameter
middle to the
end of the parameter list in the function definition and give it an empty
default value. We also add an
if test that builds the full name properly,
depending on whether or not a middle name is provided:
name_ def get_formatted_name(first, last, middle=''):
function.py """Generate a neatly formatted full name."""
if middle:
full_name = first + ' ' + middle + ' ' + last
else:
full_name = first + ' ' + last
return full_name.title()
In this new version of get_formatted_name() , the middle name is optional.
If a middle name is passed to the function (
if middle: ), the full name will
contain a first, middle, and last name. Other wise, the full name will con -
sist of just a first and last name. Now the function should work for both
kinds of names. To find out if the function still works for names like
Janis
Joplin
, let’s run test_name_function.py again:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
The test case passes now. This is ideal; it means the function works for
names like
Janis Joplin again without us having to test the function manu -
ally. Fixing our function was easy because the failed test helped us identify
the new code that broke existing behavior.

Now that we know get_formatted_name() works for simple names again, let’s
write a second test for people who include a middle name. We do this by
adding another method to the class
NamesTestCase :
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""Tests for 'name_function.py'."""

def test_first_last_name(self):
"""Do names like 'Janis Joplin' work?"""
formatted_name = get_formatted_name('janis', 'joplin')
self.assertEqual(formatted_name, 'Janis Joplin')

def test_first_last_middle_name(self):
"""Do names like 'Wolfgang Amadeus Mozart' work?"""
u formatted_name = get_formatted_name( 'wolfgang', 'mozart', 'amadeus')
unittest.main()
We name this new method test_first_last_middle_name() . The method
name must start with test_ so the method runs automatically when we run
test_name_function.py . We name the method to make it clear which behavior
of
get_formatted_name() we’re testing. As a result, if the test fails, we know
right away what kinds of names are affected. It’s fine to have long method
names in your
TestCase classes. They need to be descriptive so you can make
sense of the output when your tests fail, and because Python calls them
automatically, you’ll never have to write code that calls these methods. To test the function, we call
get_formatted_name() with a first, last, and
middle name u , and then we use
assertEqual() to check that the returned
full name matches the full name (first, middle, and last) that we expect.
When we run test_name_function.py again, both tests pass:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Great! We now know that the function still works for names like Janis
Joplin
, and we can be confident that it will work for names like Wolfgang
as well.

222 Chapter 11
try It y ourself
11 -1 . City, Country: Write a function that accepts two parameters: a city name
and a country name . The function should return a single string of the form
City, Country , such as Santiago, Chile . Store the function in a module called
city _functions.py .
Create a file called test_cities.py that tests the function you just wrote
(remember that you need to import
unittest and the function you want to test) .
Write a method called
test_city_country() to verify that calling your function
with values such as
'santiago' and 'chile' results in the correct string . Run
test_cities.py , and make sure
test_city_country() passes .
11 - 2 . Population: Modify your function so it requires a third parameter,
population . It should now return a single string of the form City, Country –
population xxx
, such as Santiago, Chile – population 5000000 . Run
test_cities.py again . Make sure
test_city_country() fails this time .
Modify the function so the
population parameter is optional . Run
test_cities.py again, and make sure
test_city_country() passes again .
Write a second test called
test_city_country_population() that veri -
fies you can call your function with the values
'santiago' , 'chile' , and
'population=5000000' . Run test_cities.py again, and make sure this new test
passes .
testing a Class
In the first part of this chapter, you wrote tests for a single function. Now
you’ll write tests for a class. You’ll use classes in many of your own pro -
grams, so it’s helpful to be able to prove that your classes work correctly. If
you have passing tests for a class you’re working on, you can be confident
that improvements you make to the class won’t accidentally break its cur -
rent behavior.
A Variety of Assert Methods
Python provides a number of assert methods in the unittest.TestCase class.
As mentioned earlier, assert methods test whether a condition you believe is
true at a specific point in your code is indeed true. If the condition is true
is confirmed; you can be confident that no errors exist. If the condition you
assume is true is actually not true, Python raises an exception. Table 11-1 describes six commonly used assert methods. With these
methods you can verify that returned values equal or don’t equal expected
values, that values are
True or False , and that values are in or not in a given

list. You can use these methods only in a class that inherits from unittest
.TestCase
, so let’s look at how we can use one of these methods in the con -
text of testing an actual class.
Table 11-1: Assert Methods Available from the unittest Module
Method Use
assertEqual(a, b)
Verify that a == b
assertNotEqual(a, b) Verify that a != b
assertTrue(x) Verify that x is True
assertFalse(x) Verify that x is False
assertIn(item, list) Verify that item is in list
assertNotIn(item, list) Verify that item is not in list
A Class to Test
Testing a class is similar to testing a function—much of your work involves
testing the behavior of the methods in the class. But there are a few dif -
ferences, so let’s write a class to test. Consider a class that helps administer
anony mous sur veys :
survey.py class AnonymousSurvey():
"""Collect anonymous answers to a survey question."""

u def __init__(self, question): """Store a question, and prepare to store responses."""
self.question = question
self.responses = []

v def show_question(self): """Show the survey question."""
print(question)

w def store_response(self, new_response): """Store a single response to the survey."""
self.responses.append(new_response)

x def show_results(self): """Show all the responses that have been given."""
print("Survey results:")
for response in responses:
print('- ' + response)
This class starts with a sur vey question that you provide u and includes
an empty list to store responses. The class has methods to print the sur vey
question v, add a new response to the response list w , and print all the
responses stored in the list x . To create an instance from this class, all you

224 Chapter 11
have to provide is a question. Once you have an instance representing a par-
ticular sur vey, you display the sur vey question with
show_question() , store a
response using
store_response() , and show results with show_results() .
To show that the
AnonymousSurvey class works, let’s write a program that
uses the class:
language_ from survey import AnonymousSurvey
survey.py # Define a question, and make a survey.
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
# Show the question, and store responses to the question.
my_survey.show_question()
print("Enter 'q' at any time to quit.\n")
while True:
response = input("Language: ")
if response == 'q':
break
my_survey.store_response(response)
# Show the survey results.
print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()
This program defines a question ( "What language did you first learn
to speak?"
) and creates an AnonymousSurvey object with that question. The
program calls
show_question() to display the question and then prompts for
responses. Each response is stored as it is received. When all responses have
been entered (the user inputs
q to quit), show_results() prints the sur vey
result s :
What language did you first learn to speak?
Enter 'q' at any time to quit.
Language: English
Language: Spanish
Language: English
Language: Mandarin
Language: q
Thank you to everyone who participated in the survey!
Survey results:
- English
- Spanish
- English
- Mandarin

This class works for a simple anonymous sur vey. But let’s say we want to
improve
AnonymousSurvey and the module it’s in, survey . We could allow each
user to enter more than one response. We could write a method to list only
unique responses and to report how many times each response was given.
We could write another class to manage nonanonymous sur veys. Implementing such changes would risk affecting the current behavior
of the class
AnonymousSurvey . For example, it’s possible that while tr ying to
allow each user to enter multiple responses, we could accidentally change
how single responses are handled. To ensure we don’t break existing behav -
ior as we develop this module, we can write tests for the class.
Testing the AnonymousSurvey Class
Let’s write a test that verifies one aspect of the way AnonymousSurvey behaves.
We’ll write a test to verify that a single response to the sur vey question is
stored properly. We’ll use the
assertIn() method to verify that the response
is in the list of responses after it’s been stored:
test_ import unittest
survey.py from survey import AnonymousSurvey
u class TestAnonmyousSurvey(unittest.TestCase): """Tests for the class AnonymousSurvey"""

v def test_store_single_response(self): """Test that a single response is stored properly."""
question = "What language did you first learn to speak?"
w my_survey = AnonymousSurvey(question) my_survey.store_response('English')

x self.assertIn('English', my_survey.responses)
unittest.main()
We start by importing the unittest module and the class we want to
test,
AnonymousSurvey . We call our test case TestAnonymousSurvey , which again
inherits from
unittest.TestCase u . The first test method will verify that
when we store a response to the sur vey question, the response ends up in
the sur vey’s list of responses. A good descriptive name for this method is
test_store_single_response() v . If this test fails, we’ll know from the method
name shown in the output of the failing test that there was a problem stor -
ing a single response to the sur vey. To test the behavior of a class, we need to make an instance of the
class. At w we create an instance called
my_survey with the question "What
language did you first learn to speak?"
We store a single response, English ,
using the
store_response() method. Then we verify that the response was
stored correctly by asserting that
English is in the list my_survey.responses x .

226 Chapter 11
When we run test_survey.py, the test passes:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
This is good, but a sur vey is useful only if it generates more than one
response. Let’s verify that three responses can be stored correctly. To do
this, we add another method to
TestAnonymousSurvey :
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey"""

def test_store_single_response(self):
"""Test that a single response is stored properly."""
--snip--

def test_store_three_responses(self):
"""Test that three individual responses are stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
u responses = ['English', 'Spanish', 'Mandarin'] for response in responses:
my_survey.store_response(response)

v for response in responses: self.assertIn(response, my_survey.responses)
unittest.main()
We call the new method test_store_three_responses() . We create a sur -
vey object just like we did in
test_store_single_response() . We define a list
containing three different responses u , and then we call
store_response()
for each of these responses. Once the responses have been stored, we write
another loop and assert that each response is now in
my_survey.responses v .
When we run test_survey.py again, both tests (for a single response and
for three responses) pass:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
This works perfectly. However, these tests are a bit repetitive, so we’ll
use another feature of
unittest to make them more efficient.

The setUp() Method
In test_survey.py we created a new instance of AnonymousSurvey in each test
method, and we created new responses in each method. The
unittest.TestCase
class has a
setUp() method that allows you to create these objects once and
then use them in each of your test methods. When you include a
setUp()
method in a
TestCase class, Python runs the setUp() method before running
each method starting with test_ . Any objects created in the
setUp() method
are then available in each test method you write. Let’s use
setUp() to create a sur vey instance and a set of responses that
can be used in
test_store_single_response() and test_store_three_responses() :
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey."""

def setUp(self):
"""
Create a survey and a set of responses for use in all test metho
ds.
"""
question = "What language did you first learn to speak?"
u self.my_survey = AnonymousSurvey(question)
v self.responses = ['English', 'Spanish', 'Mandarin']
def test_store_single_response(self):
"""Test that a single response is stored properly."""
self.my_survey.store_response(self.responses[0])
self.assertIn(self.responses[0], self.my_survey.responses)

def test_store_three_responses(self):
"""Test that three individual responses are stored properly."""
for response in self.responses:
self.my_survey.store_response(response)
for response in self.responses:
self.assertIn(response, self.my_survey.responses)
unittest.main()
The method setUp() does two things: it creates a sur vey instance u ,
and it creates a list of responses v . Each of these is prefixed by
self , so
they can be used any where in the class. This makes the two test methods
simpler, because neither one has to make a sur vey instance or a response.
The method
test_store_single_response() verifies that the first response in
self.responses — self.responses[0] —can be stored correctly, and test_store_
single_response()
verifies that all three responses in self.responses can be
stored correctly. When we run test_survey.py again, both tests still pass. These tests would
be particularly useful when tr ying to expand
AnonymousSurvey to handle mul -
tiple responses for each person. A fter modifying the code to accept multiple

228 Chapter 11
responses, you could run these tests and make sure you haven’t affected the
ability to store a single response or a series of individual responses.When you’re testing your own classes, the
setUp() method can make
your test methods easier to write. You make one set of instances and attri -
butes in
setUp() and then use these instances in all your test methods. This
is much easier than making a new set of instances and attributes in each
test method.
note When a test case is running, Python prints one character for each unit test as it is
completed. A passing test prints a dot, a test that results in an error prints an
E, and
a test that results in a failed assertion prints an
F. This is why you’ll see a different
number of dots and characters on the first line of output when you run your test cases.
If a test case takes a long time to run because it contains many unit tests, you can
watch these results to get a sense of how many tests are passing.
t ry It y ourself
11 - 3 . Employee: Write a class called Employee . The __init__() method should
take in a first name, a last name, and an annual salary, and store each of these
as attributes . Write a method called
give_raise() that adds $5000 to the annual salary by default but also accepts a different raise amount . Write a test case for Employee . Write two test methods, test_give_ default_raise() and test_give_custom_raise() . Use the setUp() method so you don’t have to create a new employee instance in each test method . Run your test case, and make sure both tests pass . summary In this chapter you learned to write tests for functions and classes using tools in the unittest module. You learned to write a class that inherits from unittest.TestCase , and you learned to write test methods that verify specific behaviors your functions and classes should exhibit. You learned to use the setUp() method to efficiently create instances and attributes from your classes that can be used in all the test methods for a class. Testing is an important topic that many beginners don’t learn. You don’t have to write tests for all the simple projects you tr y as a beginner. But as soon as you start to work on projects that involve significant development effort, you should test the critical behaviors of your functions and classes. You’ll be more confident that new work on your project won’t break the parts that work, and this will give you the freedom to make improvements to your code. If you accidentally break existing functionality, you’ll know right away, so you can still fix the problem easily. Responding to a failed test that you ran is much easier than responding to a bug report from an unhappy user. Testing Your Code 229 Other programmers respect your projects more if you include some ini- tial tests. They’ll feel more comfortable experimenting with your code and be more willing to work with you on projects. If you want to contribute to a project that other programmers are working on, you’ll be expected to show that your code passes existing tests and you’ll usually be expected to write tests for new behavior you introduce to the project. Play around with tests to become familiar with the process of testing your code. Write tests for the most critical behaviors of your functions and classes, but don’t aim for full coverage in early projects unless you have a specific reason to do so. Part II ProjeCts Congratulations! You now know enough about Python to start building interactive and meaningful projects. Creating your own projects will teach you new skills and solidify your understanding of the concepts introduced in Part I. Part II contains three types of projects, and you can choose to do any or all of these projects in whichever order you like. Here’s a brief description of each project to help you decide which to dig into first. a lien Invasion: Making a Game with Python In the Alien Invasion project (Chapters 12, 13, and 14), you’ll use the Pygame package to develop a 2D game in which the aim is to shoot down a fleet of aliens as they drop down the screen in levels that increase in speed and difficulty. At the end of the project, you’ll have learned skills that will enable you to develop your own 2D games in Pygame. Data Visualization The Data Visualization project starts in Chapter 15, in which you’ll learn to generate data and create a series of functional and beautiful visualizations of that data using matplotlib and Pygal. Chapter 16 teaches you to access data from online sources and feed it into a visualization package to create plots of weather data and a world population map. Finally, Chapter 17 232 Par t II shows you how to write a program to automatically download and visualize data. Learning to make visualizations allows you to explore the field of data mining, which is a highly sought-after skill in the world today. Web applications In the Web Applications project (Chapters 18, 19, and 20), you’ll use the Django package to create a simple web application that allows users to keep a journal about any number of topics they’ve been learning about. Users will create an account with a username and password, enter a topic, and then make entries about what they’re learning. You’ll also learn how to deploy your app so anyone in the world can access it.A fter completing this project, you’ll be able to start building your own simple web applications, and you’ll be ready to delve into more thorough resources on building applications with Django. PrOjEC t 1 alIen In VasIon 12 a shIP that fI res Bullets Let’s build a game! We’ll use Pygame, a collection of fun, powerful Python modules that manage graphics, animation, and even sound, making it easier for you to build sophis - ticated games. With Pygame handling tasks like draw - ing images to the screen, you can skip much of the tedious, difficult coding and focus on the higher-level logic of game dynamics. In this chapter, you’ll set up Pygame and then create a ship that moves right and left, and fires bullets in response to player input. In the next two chapters, you’ll create a fleet of aliens to destroy, and then continue to make refinements, such as setting limits on the number of ships you can use and adding a scoreboard. From this chapter you’ll also learn to manage large projects that span multiple files. We’ll refactor a lot of code and manage file contents to keep our project organized and the code efficient. 236 Chapter 12 Making games is an ideal way to have fun while learning a language. It’s deeply satisfying to watch others play a game you wrote, and writing a simple game will help you understand how professional games are written. As you work through this chapter, enter and run the code to understand how each block of code contributes to overall gameplay. Experiment with different values and settings to gain a better understanding of how to refine interactions in your own games. note Alien Invasion will span a number of different files, so make a new folder on your sys - tem called alien_invasion . Be sure to save all files for the project to this folder so your import statements will work correctly. Planning your Project When building a large project, it’s important to prepare a plan before you begin to write your code. Your plan will keep you focused and make it more likely that you’ll complete the project. Let’s write a description of the overall gameplay. Although this descrip - tion doesn’t cover ever y detail of Alien Invasion, it provides a clear idea of how to start building the game: In A lien Invasion, the player controls a ship that appears at the bottom center of the screen. The player can move the ship right and left using the arrow keys and shoot bullets using the spacebar. When the game begins, a fleet of aliens fills the sky and moves across and down the screen. The player shoots and destroys the aliens. If the player shoots all the aliens, a new fleet appears that moves faster than the previous fleet. If any alien hits the player’s ship or reaches the bottom of the screen, the player loses a ship. If the player loses three ships, the game ends. For the first phase of development, we’ll make a ship that can move right and left. The ship should be able to fire bullets when the player presses the spacebar. A fter setting up this behavior, we can turn our attention to the aliens and refine the gameplay. Installing Pygame Before you begin coding, install Pygame. Here’s how to do so on Linux, OS X, and Microsoft Windows. If you’re using Python 3 on Linux or if you’re using OS X, you’ll need to use pip to install Pygame. pip is a program that handles the downloading and installing of Python packages for you. The following sections will show you how to install packages with pip. If you’re using Python 2.7 on Linux or if you’re using Windows, you won’t need pip to install Pygame. Instead, move on to “Installing Pygame on Linux” on page 238 or “Installing Pygame on Windows” on page 240. A Ship That Fires Bullets 237 note Instructions for installing pip on all systems are included in the sections that follow because you’ll need pip for the data visualization and web application projects. These instructions are also included in the online resources at htt ps ://w w w.nost a rch .com/pythoncrashcourse/ . If you have trouble with the instructions here, see if the online instructions work for you. Installing Python Packages with pip The most recent versions of Python come with pip installed, so first check whether pip is already on your system. With Python 3, pip is sometimes called pip3. Checking for pip on Linux and OS X Open a terminal window and enter the following command:$ pip --version
u pip 7.0.3 from /usr/local/lib/python3.5/dist-packages (python 3.5) $If you have only one version of Python installed on your system and you see output similar to this, move on to either “Installing Pygame on Linux” on page 238 or “Installing Pygame on OS X” on page 239. If you get an error message, tr y using pip3 instead of pip . If neither version is installed on your system, go to “Installing pip” on page 238. If you have more than one version of Python on your system, verify that pip is associated with the version of Python you’re using—for example, python 3.5 at u . If pip is associated with the correct version of Python, move on to “Installing Pygame on Linux” on page 238 or “Installing Pygame on OS X” on page 239. If pip is associated with the wrong version of P ython, tr y using pip3 instead of pip . If neither command works for the version of Python you’re using, go to “Installing pip” on page 238. Checking for pip on Windows Open a terminal window and enter the following command:$ python -m pip --version
u pip 7.0.3 from C:\Python35\lib\site-packages (python 3.5) $If your system has only one version of Python installed and you see output similar to this, move on to “Installing Pygame on Windows” on page 240. If you get an error message, tr y using pip3 instead of pip . If neither version is installed on your system, move on to “Installing pip” on page 238. If your system has more than one version of Python installed, verify that pip is associated with the version of Python you’re using—for example, python 3.5 at u . If pip is associated with the correct version of Python, move on to “Installing Pygame on Windows” on page 240. If pip is associated with 238 Chapter 12 the wrong version of Python, tr y using pip3 instead of pip . If neither com- mand works for the version of Python you’re using, move on to “Installing pip” next. Installing pip To install pip, go to https://bootstrap.pypa.io/get- pip.py . Save the file if prompted to do so. If the code for get-pip.py appears in your browser, copy and paste the program into your text editor and save the file as get-pip.py . Once get-pip.py is saved on your computer, you’ll need to run it with administrative privileges because pip will be installing new pack - ages to your system. note If you can’t find get-pip.py , go to https ://pip.py pa.io/ , click Installation in the left panel, and then under “Install pip,” follow the link to get-pip.py . Installing pip on Linux and OS X Use the following command to run get-pip.py with administrative privileges:$ sudo python get-pip.py
note If you use the command python3 to start a terminal session, you should use sudo
python3 get-pip.py
here.
A fter the program runs, use the command
pip --version (or pip3
--version
) to make sure pip was installed correctly.
Installing pip on Windows
Use the following command to run get-pip.py :
$python get-pip.py If you use a different command to run Python in a terminal, make sure you use that command to run get-pip.py . For example, your command might be python3 get-pip.py or C:\Python35\python get-pip.py . A fter the program runs, run the command python -m pip --version to make sure pip was installed successfully. Installing Pygame on Linux If you’re using Python 2.7, install Pygame using the package manager. Open a terminal window and run the following command, which will download and install Pygame onto your system:$ sudo apt-get install python-pygame

A Ship That Fires Bullets 239
Test your installation in a terminal session by entering the following:
$python >>> import pygame >>> If no output appears, Python has imported Pygame and you’re ready to move on to “Starting the Game Project” on page 240. If you’re running Python 3, two steps are required: installing the libraries Pygame depends on, and downloading and installing Pygame. Enter the following to install the libraries Pygame needs. (If you use a command such as python3.5 on your system, replace python3-dev w ith python3.5-dev .)$ sudo apt-get install python3-dev mercurial
$sudo apt-get install libsdl-image1.2-dev libsdl2-dev libsdl-ttf2.0-dev This will install the libraries needed to run Alien Invasion successfully. If you want to enable some more advanced functionality in Pygame, such as the ability to add sounds, you can also add the following libraries:$ sudo apt-get install libsdl-mixer1.2-dev libportmidi-dev
$sudo apt-get install libswscale-dev libsmpeg-dev libavformat-dev libavco de-dev$ sudo apt-get install python-numpy
Now install Pygame by entering the following (use pip3 if that’s appro -
$pip install --user hg+http://bitbucket.org/pygame/pygame The output will pause for a moment after informing you which libraries Pygame found. Press enter , even though some libraries are missing. You should see a message stating that Pygame installed successfully. To confirm the installation, run a Python terminal session and tr y to import Pygame by entering the following:$ python3
>>> import pygame
>>>
If this works, move on to “Starting the Game Project” on page 240.
Installing Pygame on OS X
You’ll need Homebrew to install some packages that Pygame depends on. If
you haven’t already installed Homebrew, see Appendix A for instructions. To install the libraries that Pygame depends on, enter the following:
$brew install hg sdl sdl_image sdl_ttf 240 Chapter 12 This will install the libraries needed to run Alien Invasion. You should see output scroll by as each librar y is installed. If you also want to enable more advanced functionality, such as includ - ing sound in games, you can install two additional libraries:$ brew install sdl_mixer portmidi
Use the following command to install Pygame (use pip rather than pip3
if you’re running Python 2.7):
$pip3 install --user hg+http://bitbucket.org/pygame/pygame Start a Python terminal session and import Pygame to check whether the installation was successful (enter python rather than python3 if you’re running Python 2.7):$ python3
>>> import pygame
>>>
If the import statement works, move on to “Starting the Game Project”
below.
Installing Pygame on Windows
The Pygame project is hosted on a code-sharing site called Bitbucket. To
install Pygame on your version of Windows, find a Windows installer at
Python you’re running. If you don’t see an appropriate installer listed at
Bitbucket, check http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame .
A fter you’ve downloaded the appropriate file, run the installer if it’s a
.exe file.
If you have a file ending in .whl , copy the file to your project direc -
tor y. Open a command window, navigate to the folder that you copied the
installer to, and use pip to run the installer:
> python -m pip install --user pygame-1.9.2a0-cp35-none-win32.whl
s tarting the g ame Project
Now we’ll start building our game by first creating an empty Pygame win -
dow to which we can later draw our game elements, such as the ship and the
aliens. We’ll also have our game respond to user input, set the background
color, and load a ship image.

A Ship That Fires Bullets 241
Creating a Pygame Window and Responding to User Input
First, we’ll create an empty Pygame window. Here’s the basic structure of a
game written in Pygame:
alien_ import sys
invasion.py import pygame
def run_game():
# Initialize game and create a screen object.
u pygame.init()
v screen = pygame.display.set_mode((1200, 800)) pygame.display.set_caption("Alien Invasion")
# Start the main loop for the game.
w while True:
# Watch for keyboard and mouse events.
x for event in pygame.event.get():
y if event.type == pygame.QUIT: sys.exit()

# Make the most recently drawn screen visible.
z pygame.display.flip()
run_game()
First, we import the sys and pygame modules. The pygame module con -
tains the functionality needed to make a game. We’ll use the
sys module to
exit the game when the player quits. Alien Invasion starts as the function
run_game() . The line pygame.init()
at u initializes background settings that Pygame needs to work properly.
At v , we call
pygame.display.set_mode() to create a display window called
screen , on which we’ll draw all of the game’s graphical elements. The argu -
ment
(1200, 800) is a tuple that defines the dimensions of the game window.
By passing these dimensions to
pygame.display.set_mode() , we create a game
window 1200 pixels wide by 800 pixels high. (You can adjust these values
depending on the size of your display.) The
screen object is called a surface. A surface in Pygame is a part
of the screen where you display a game element. Each element in the
game, like the aliens or the ship, is a surface. The surface returned by
display.set_mode() represents the entire game window. When we activate
the game’s animation loop, this surface is automatically redrawn on ever y
pass through the loop. The game is controlled by a
while loop w that contains an event loop
and code that manages screen updates. An event is an action that the user
performs while playing the game, such as pressing a key or moving the
mouse. To make our program respond to events, we’ll write an event loop to
listen for an event and perform an appropriate task depending on the kind
of event that occurred. The
for loop at x is an event loop.

242 Chapter 12
To access the events detected by Pygame, we’ll use the pygame.event.get()
method. Any keyboard or mouse event will cause the
for loop to run. Inside
the loop, we’ll write a series of
if statements to detect and respond to specific
events. For example, when the player clicks the game window’s close button, a
pygame.QUIT event is detected and we call sys.exit() to exit the game y .
The call to
pygame.display.flip() at z tells Pygame to make the most
recently drawn screen visible. In this case it draws an empty screen each time
through the
while loop to erase the old screen so that only the new screen is
visible. When we move the game elements around,
pygame.display.flip() will
continually update the display to show the new positions of elements and
hide the old ones, creating the illusion of smooth movement. The last line in this basic game structure calls
run_game() , which initial -
izes the game and starts the main loop. Run this code now, and you should see an empty Pygame window.
Setting the Background Color
Pygame creates a black screen by default, but that’s boring. Let’s set a differ -
ent background color:
alien_ --snip--
invasion.py def run_game():
--snip--
pygame.display.set_caption("Alien Invasion")
# Set the background color.
u bg_color = (230, 230, 230)
# Start the main loop for the game.
while True:
# Watch for keyboard and mouse events.
--snip--
# Redraw the screen during each pass through the loop.
v screen.fill(bg_color)
# Make the most recently drawn screen visible.
pygame.display.flip()
run_game()
First, we create a background color and store it in bg_color u. This color
needs to be specified only once, so we define its value before entering the
main
while loop.
Colors in Pygame are specified as RGB colors: a mix of red, green, and
blue. Each color value can range from 0 to 255. The color value (255, 0, 0)
is red, (0, 255, 0) is green, and (0, 0, 255) is blue. You can mix RGB values
to create 16 million colors. The color value (230, 230, 230) mixes equal
amounts of red, blue, and green, which produces a light gray background
color.

A Ship That Fires Bullets 243
At v, we fill the screen with the background color using the screen.fill()
method, which takes only one argument: a color.
Creating a Settings Class
Each time we introduce new functionality into our game, we’ll typically
the code, let’s write a module called
settings that contains a class called
Settings to store all the settings in one place. This approach allows us
to pass around one settings object instead of many individual settings.
In addition, it makes our function calls simpler and makes it easier to mod -
ify the game’s appearance as our project grows. To modify the game, we’ll
simply change some values in settings.py instead of searching for different
settings throughout our files. Here’s the initial
Settings class:
settings.py class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
To make an instance of Settings and use it to access our settings, modify
alien_invasion.py as follows:
alien_ --snip--
invasion.py import pygame
from settings import Settings
def run_game():
# Initialize pygame, settings, and screen object.
pygame.init()
u ai_settings = Settings()
v screen = pygame.display.set_mode( (ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Start the main loop for the game.
while True:
--snip--
# Redraw the screen during each pass through the loop.
w screen.fill(ai_settings.bg_color)
# Make the most recently drawn screen visible.
pygame.display.flip()
run_game()

244 Chapter 12
We import Settings into the main program file, and then create an
instance of
Settings and store it in ai_settings after making the call to
pygame.init() u . When we create a screen v , we use the screen_width and
screen_height attributes of ai_settings , and then we use ai_settings to access
the background color when filling the screen at w as well.
a dding the s hip Image
Now let’s add the ship to our game. To draw the player’s ship on screen,
we’ll load an image and then use the Pygame method
blit() to draw the
image. When choosing artwork for your games, be sure to pay attention to
licensing. The safest and cheapest way to start is to use freely licensed
graphics that you can modify from a website like http://pixabay.com/ .
You can use almost any type of image file in your game, but it’s easiest
if you use a bitmap ( .bmp) file because Pygame loads bitmaps by default.
Although you can configure Pygame to use other file types, some file types
depend on certain image libraries that must be installed on your computer.
(Most images you’ll find are in .jpg , .png, or .g if formats, but you can convert
them to bitmaps using tools like Photoshop, GIMP, and Paint.) Pay particular attention to the background color in your chosen image.
Tr y to find a file with a transparent background that you can replace with
any background color using an image editor. Your games will look best
if the image’s background color matches your game’s background color.
Alternatively, you can match your game’s background to the image’s
background. For Alien Invasion, you can use the file ship.bmp (Figure 12-1), which
is available in the book’s resources through https://www.nostarch.com/
pythoncrashcourse /. The file’s background color matches the settings we’re
using in this project. Make a folder called images inside your main project
folder ( alien_invasion ). Save the file ship.bmp in the images folder.
Figure 12-1: The ship for Alien Invasion

A Ship That Fires Bullets 245
Creating the Ship Class
A fter choosing an image for the ship, we need to display it onscreen. To use
our ship, we’ll write a module called
ship , which contains the class Ship . This
class will manage most of the behavior of the player’s ship.
ship.py import pygame
class Ship():
def __init__(self, screen):
"""Initialize the ship and set its starting position."""
self.screen = screen
# Load the ship image and get its rect.
v self.rect = self.image.get_rect()
w self.screen_rect = screen.get_rect()
# Start each new ship at the bottom center of the screen.
x self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom
y def blitme(self): """Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
First, we import the pygame module. The __init__() method of Ship takes
two parameters: the
self reference and the screen where we’ll draw the ship.
To load the image, we call
pygame.image.load() u . This function returns a
surface representing the ship, which we store in
self.image .
Once the image is loaded, we use
get_rect() to access the surface’s rect
attribute v . One reason Pygame is so efficient is that it lets you treat game
elements like rectangles ( rects), even if they’re not exactly shaped like rect -
angles. Treating an element as a rectangle is efficient because rectangles
are simple geometric shapes. This approach usually works well enough that
no one playing the game will notice that we’re not working with the exact
shape of each game element. When working with a
rect object, you can use the x- and y-coordinates
of the top, bottom, left, and right edges of the rectangle, as well as the
center. You can set any of these values to determine the current position
of the rect. When you’re centering a game element, work with the
center , centerx , or
centery attributes of a rect. When you’re working at an edge of the screen,
work with the
top , bottom , left , or right attributes. When you’re adjusting
the horizontal or vertical placement of the rect, you can just use the
x and
y attributes, which are the x- and y-coordinates of its top-left corner. These
attributes spare you from having to do calculations that game developers
formerly had to do manually, and you’ll find you’ll use them often.

246 Chapter 12
note In Pygame, the origin (0, 0) is at the top-left corner of the screen, and coordinates
increase as you go down and to the right. On a 1200 by 800 screen, the origin is at
the top-left corner, and the bottom-right corner has the coordinates (1200, 800).
We’ll position the ship at the bottom center of the screen. To do so,
first store the screen’s rect in
self.screen_rect w , and then make the value
of
self.rect.centerx (the x-coordinate of the ship’s center) match the centerx
attribute of the screen’s rect x . Make the value of
self.rect.bottom (t he
y-coordinate of the ship’s bottom) equal to the value of the screen rect’s
bottom attribute. Pygame will use these rect attributes to position the ship
image so it’s centered horizontally and aligned with the bottom of the
screen. At y we define the
blitme() method, which will draw the image to the
screen at the position specified by
self.rect .
Drawing the Ship to the Screen
Now let’s update alien_invasion.py so it creates a ship and calls the ship’s
blitme() method:
alien_ --snip--
invasion.py from settings import Settings
from ship import Ship
def run_game():
--snip--
pygame.display.set_caption("Alien Invasion")
# Make a ship.
u ship = Ship(screen)
# Start the main loop for the game.
while True:
--snip--
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
v ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
run_game()
We import Ship and then make an instance of Ship (named ship ) after
the screen has been created. It must come before the main
while loop u so
we don’t make a new instance of the ship on each pass through the loop.
We draw the ship onscreen by calling
ship.blitme() after filling the back -
ground, so the ship appears on top of the background v .
W hen you run alien_invasion.py now, you should see an empty game
screen with our rocket ship sitting at the bottom center, as shown in
Figure 12-2.

A Ship That Fires Bullets 247
Figure 12-2: Alien Invasion with the ship at the bottom center of the screen
refactoring: the game_functions m odule
In larger projects, you’ll often refactor code you’ve written before add -
ing more code. R efactoring simplifies the structure of the code you’ve
already written, making it easier to build on. In this section we’ll create
a new module called
game_functions , which will store a number of func -
tions that make Alien Invasion work. The
game_functions module will pre -
vent alien_invasion.py from becoming too lengthy and will make the logic
in alien_invasion.py easier to follow.
The check_events() Function
We’ll start by moving the code that manages events to a separate function
called
check_events() . This will simplify run_game() and isolate the event man -
agement loop. Isolating the event loop allows you to manage events sepa -
rately from other aspects of the game, like updating the screen. Place
check_events() in a separate module called game_functions :
game_ import sys
functions.py import pygame
def check_events():
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()

248 Chapter 12
This module imports sys and pygame , which are used in the event check-
ing loop. The function needs no parameters at this point, and the body is
copied from the event loop in alien_invasion.py .
Now let’s modif y alien_invasion.py so it imports the
game_functions module,
and we’ll replace the event loop with a call to
check_events() :
alien_ import pygame
invasion.py from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
--snip--
# Start the main loop for the game.
while True:
gf.check_events()

# Redraw the screen during each pass through the loop.
--snip--
We no longer need to import sys directly into the main program file,
because it’s only being used in the
game_functions module now. We give the
imported
game_functions module the alias gf for simplification.
The update_screen() Function
Let’s move the code for updating the screen to a separate function called
update_screen() in game_functions.py to further simplify run_game() :
game_ --snip--
functions.py def check_events():
--snip--
def update_screen(ai_settings, screen, ship):
"""Update images on the screen and flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
The new update_screen() function takes three parameters: ai_settings ,
screen , and ship . Now we need to update the while loop from alien_invasion.py
with a call to
update_screen() :
alien_ --snip--
invasion.py # Start the main loop for the game.

A Ship That Fires Bullets 249
while True:
gf.check_events()
gf.update_screen(ai_settings, screen, ship)
run_game()
These two functions make the while loop simpler and will make further
development easier. Instead of working inside
run_game() , we can do most of
our work in the module
game_functions .
Because we wanted to start out working with code in a single file, we
didn’t introduce the
game_functions module right away. This approach gives
you an idea of a realistic development process: you start out writing your
code as simply as possible, and refactor it as your project becomes more
complex. Now that our code is restructured to make it easier to add to, we can
work on the dynamic aspects of the game!
t ry It y ourself
12 -1. Blue Sky: Make a Pygame window with a blue background .
12 -2 . Game Character: Find a bitmap image of a game character you like or
convert an image to a bitmap . Make a class that draws the character at the
center of the screen and match the background color of the image to the back -
ground color of the screen, or vice versa .
Piloting the ship
Let’s give the player the ability to move the ship right and left. To do this,
we’ll write code that responds when the player presses the right or left
arrow key. We’ll focus on movement to the right first, and then we’ll apply
the same principles to control movement to the left. As you do this, you’ll
learn how to control the movement of images on the screen.
Responding to a Keypress
Whenever the player presses a key, that keypress is registered in Pygame as
an event. Each event is picked up by the
pygame.event.get() method, so we
need to specify in our
check_events() function what kind of events to check
for. Each keypress is registered as a
KEYDOWN event.
When a
KEYDOWN event is detected, we need to check whether the key
that was pressed is one that triggers a certain event. For example, if the

250 Chapter 12
right arrow key is pressed, we increase the ship’s rect.centerx value to
move the ship to the right:
game_ def check_events(ship):
functions.py """Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
u elif event.type == pygame.KEYDOWN:
v if event.key == pygame.K_RIGHT: # Move the ship to the right.
w ship.rect.centerx += 1
We give the check_events() function a ship parameter, because the ship
needs to move to the right when the right arrow key is pressed. Inside
check_events() we add an elif block to the event loop to respond when
Pygame detects a
KEYDOWN event u . We check if the key pressed is the right
arrow key (
pygame.K_RIGHT ) by reading the event.key attribute v . If the
right arrow key was pressed, we move the ship to the right by increasing
the value of
ship.rect.centerx by 1 w .
We need to update the call to
check_events() in alien_invasion.py so it
passes
ship as an argument:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ship)
gf.update_screen(ai_settings, screen, ship)
If you run alien_invasion.py now, you should see the ship move to the
right one pixel ever y time you press the right arrow key. That’s a start, but
it’s not an efficient way to control the ship. Let’s improve this control by
allow ing continuous movement.
Allowing Continuous Movement
When the player holds down the right arrow key, we want the ship to
continue moving right until the player releases the key. We’ll have our
game detect a
pygame.KEYUP event so we’ll know when the right arrow key is
released; then we’ll use the
KEYDOWN and KEYUP events together with a flag
called
moving_right to implement continuous motion.
When the ship is motionless, the
moving_right flag will be False . When the
right arrow key is pressed, we’ll set the flag to
True , and when it’s released,
we’ll set the flag to
False again.
The
Ship class controls all attributes of the ship, so we’ll give it an attri -
bute called
moving_right and an update() method to check the status of the
moving_right flag. The update() method will change the position of the ship if
the flag is set to
True . We’ll call this method any time we want to update the
position of the ship.

A Ship That Fires Bullets 251
Here are the changes to the Ship class:
ship.py class Ship():
def __init__(self, screen):
--snip--
# Start each new ship at the bottom center of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Movement flag
u self.moving_right = False
v def update(self): """Update the ship's position based on the movement flag."""
if self.moving_right:
self.rect.centerx += 1
def blitme(self):
--snip--
We add a self.moving_right attribute in the __init__() method and set it
to
False initially u . Then we add update() , which moves the ship right if the
flag is
True v .
Now modify
check_events() so that moving_right is set to True when the
right arrow key is pressed and
False when the key is released:
game_ def check_events(ship):
functions.py """Respond to keypresses and mouse events."""
for event in pygame.event.get():
--snip--
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
u ship.moving_right = True
v elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT:
ship.moving_right = False
At u , we modify how the game responds when the player presses the
right arrow key: instead of changing the ship’s position directly, we merely
set
moving_right to True . At v , we add a new elif block, which responds to
KEYUP events. When the player releases the right arrow key ( K_RIGHT ), we set
moving_right to False .
Finally, we modify the
while loop in alien_invasion.py so it calls the ship’s
update() method on each pass through the loop:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)

252 Chapter 12
The ship’s position will update after we’ve checked for keyboard
events and before we update the screen. This allows the ship’s position to
be updated in response to player input and ensures the updated position
is used when drawing the ship to the screen. W hen you run alien_invasion.py and hold down the right arrow key, the
ship should move continuously to the right until you release the key.
Moving Both Left and Right
Now that the ship can move continuously to the right, adding movement to
the left is easy. We’ll again modify the
Ship class and the check_events() func -
tion. Here are the relevant changes to
__init__() and update() in Ship :
ship.py def __init__(self, screen):
--snip--
# Movement flags
self.moving_right = False
self.moving_left = False

def update(self):
"""Update the ship's position based on movement flags."""
if self.moving_right:
self.rect.centerx += 1
if self.moving_left:
self.rect.centerx -= 1
In __init__() , we add a self.moving_left flag. In update() , we use two sep-
arate
if blocks rather than an elif in update() to allow the ship’s rect.centerx
value to be increased and then decreased if both arrow keys are held down.
This results in the ship standing still. If we used
elif for motion to the left,
the right arrow key would always have priority. Doing it this way makes the
movements more accurate when switching from left to right, when the
player might momentarily hold down both keys. We have to make two adjustments to
check_events() :
game_ def check_events(ship):
functions.py """Respond to keypresses and mouse events."""
for event in pygame.event.get():
--snip--
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False

A Ship That Fires Bullets 253
If a KEYDOWN event occurs for the K_LEFT key, we set moving_left to True . If
a
KEYUP event occurs for the K_LEFT key, we set moving_left to False . We can
use
elif blocks here because each event is connected to only one key. If the
player presses both keys at once, two separate events will be detected. If you run alien_invasion.py now, you should be able to move the ship
continuously to the right and left. If you hold down both keys, the ship
should stop moving. Next, we’ll further refine the movement of the ship. Let’s adjust the
ship’s speed and limit how far the ship can move so it doesn’t disappear off
the sides of the screen.
Currently, the ship moves one pixel per cycle through the while loop, but
we can take finer control of the ship’s speed by adding a
ship_speed_factor
attribute to the
Settings class. We’ll use this attribute to determine how far
to move the ship on each pass through the loop. Here’s the new attribute in
settings.py :
settings.py class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
--snip--
# Ship settings
self.ship_speed_factor = 1.5
We set the initial value of ship_speed_factor to 1.5 . When we want to
move the ship, we’ll adjust its position by 1.5 pixels rather than 1 pixel. We’re using decimal values for the speed setting to give us finer con -
trol of the ship’s speed when we increase the tempo of the game later on.
However, rect attributes such as
centerx store only integer values, so we need
to make some modifications to
Ship :
ship.py class Ship():
u def __init__(self, ai_settings, screen): """Initialize the ship and set its starting position."""
self.screen = screen
v self.ai_settings = ai_settings --snip--
# Start each new ship at the bottom center of the screen.
--snip--

# Store a decimal value for the ship's center.
w self.center = float(self.rect.centerx)
# Movement flags
self.moving_right = False
self.moving_left = False

254 Chapter 12

def update(self):
"""Update the ship's position based on movement flags."""
# Update the ship's center value, not the rect.
if self.moving_right:
x self.center += self.ai_settings.ship_speed_factor if self.moving_left:
self.center -= self.ai_settings.ship_speed_factor

# Update rect object from self.center.
y self.rect.centerx = self.center
def blitme(self):
--snip--
At u , we add ai_settings to the list of parameters for __init__() , so
the ship will have access to its speed setting. We then turn the
ai_settings
parameter into an attribute, so we can use it in
update() v . Now that we’re
adjusting the position of the ship by fractions of a pixel, we need to store
the position in a variable that can store a decimal value. You can use a
decimal value to set a
rect ’s attribute, but the rect will store only the inte -
ger portion of that value. To store the ship’s position accurately, we define
a new attribute
self.center , which can hold decimal values w . We use the
float() function to convert the value of self.rect.centerx to a decimal and
store this value in
self.center .
Now when we change the ship’s position in
update() , the value of
self.center is adjusted by the amount stored in ai_settings.ship_speed_
factor
x . A fter self.center has been updated, we use the new value to
update
self.rect.centerx , which controls the position of the ship y . Only
the integer portion of
self.center will be stored in self.rect.centerx , but
that’s fine for displaying the ship. We need to pass
ai_settings as an argument when we create an instance
of
Ship in alien_invasion.py :
alien_ --snip--
invasion.py def run_game():
--snip--
# Make a ship.
ship = Ship(ai_settings, screen)
--snip--
Now any value of ship_speed_factor greater than one will make the
ship move faster. This will be helpful in making the ship respond quickly
enough to shoot down aliens, and it will let us change the tempo of the
game as the player progresses in gameplay.

A Ship That Fires Bullets 255
Limiting the Ship’s Range
At this point the ship will disappear off either edge of the screen if you hold
down an arrow key long enough. Let’s correct this so the ship stops moving
when it reaches the edge of the screen. We do this by modifying the
update()
method in
Ship :
ship.py def update(self):
"""Update the ship's position based on movement flags."""
# Update the ship's center value, not the rect.
u if self.moving_right and self.rect.right < self.screen_rect.right: self.center += self.ai_settings.ship_speed_factor
v if self.moving_left and self.rect.left > 0: self.center -= self.ai_settings.ship_speed_factor

# Update rect object from self.center.
self.rect.centerx = self.center
This code checks the position of the ship before changing the value of
self.center . The code self.rect.right returns the x-coordinate value of the
right edge of the ship’s
rect . If this value is less than the value returned
by
self.screen_rect.right , the ship hasn’t reached the right edge of the
screen u. The same goes for the left edge: if the value of the left side of
the
rect is greater than zero, the ship hasn’t reached the left edge of the
screen v. This ensures the ship is within these bounds before adjusting
the value of
self.center .
If you run alien_invasion.py now, the ship should stop moving at either
edge of the screen.
Refactoring check_events()
The check_events() function will increase in length as we continue to
develop the game, so let’s break
check_events() into two more functions:
one that handles
KEYDOWN events and another that handles KEYUP event s :
game_ def check_keydown_events(event, ship):
functions.py """Respond to keypresses."""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
def check_keyup_events(event, ship):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False

256 Chapter 12
def check_events(ship):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
We make two new functions: check_keydown_events() and check_keyup_
events()
. Each needs an event parameter and a ship parameter. The bodies
of these two functions are copied from
check_events() , and we’ve replaced
the old code with calls to the new functions. The
check_events() function is
simpler now with this cleaner code structure, which will make it easier to
develop further responses to player input.
a Quick r ecap
In the next section, we’ll add the ability to shoot bullets, which involves
a new file called bull et.py and some modifications to some of the files we
already have. Right now, we have four files containing a number of classes,
functions, and methods. To be clear about how the project is organized,
let’s review each of these files before adding more functionality.
alien_invasion.py
The main file, alien_invasion.py , creates a number of important objects used
throughout the game: the settings are stored in
ai_settings , the main dis -
play surface is stored in
screen , and a ship instance is created in this file as
well. Also stored in alien_invasion.py is the main loop of the game, which is
a
while loop that calls check_events() , ship.update() , and update_screen() .
alien_invasion.py is the only file you need to run when you want to play
Alien Invasion. The other files— settings.py, game_functions.py , ship.py —
contain code that is imported, directly or indirectly, into this file.
settings.py
The settings.py file contains the Settings class. This class only has an
__init__() method, which initializes attributes controlling the game’s
appearance and the ship’s speed.
game_functions.py
The game_functions.py file contains a number of functions that carr y out
the bulk of the work in the game. The
check_events() function detects rel -
evant events, such as keypresses and releases, and processes each of these
types of events through the helper functions
check_keydown_events() and

A Ship That Fires Bullets 257
check_keyup_events() . For now, these functions manage the movement of
the ship. The
game_functions module also contains update_screen() , which
redraws the screen on each pass through the main loop.
ship.py
The ship.py file contains the Ship class. Ship has an __init__() method, an
update() method to manage the ship’s position, and a blitme() method
to draw the ship to the screen. The actual image of the ship is stored in
ship.bmp , which is in the images folder.
try It y ourself
12 - 3 . Rocket: Make a game that begins with a rocket in the center of the
screen . Allow the player to move the rocket up, down, left, or right using the
four arrow keys . Make sure the rocket never moves beyond any edge of the
screen .
12 - 4 . Keys: Make a Pygame file that creates an empty screen . In the event
loop, print the
event.key attribute whenever a pygame.KEYDOWN event is detected .
Run the program and press various keys to see how Pygame responds .
shooting Bullets
Now let’s add the ability to shoot bullets. We’ll write code that fires a bullet
(a small rectangle) when the player presses the spacebar. Bullets will then
travel straight up the screen until they disappear off the top of the screen.
First, update settings.py to include the values we’ll need for a new Bullet
class, at the end of the
__init__() method:
settings.py def __init__(self):
--snip--
# Bullet settings
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
These settings create dark gray bullets with a width of 3 pixels and a
height of 15 pixels. The bullets will travel slightly slower than the ship.

258 Chapter 12
Creating the Bullet Class
Now create a bull et.py file to store our Bullet class. Here’s the first part of
bull et.py :
bullet.py import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship"""
def __init__(self, ai_settings, screen, ship):
"""Create a bullet object at the ship's current position."""
super(Bullet, self).__init__()
self.screen = screen
# Create a bullet rect at (0, 0) and then set correct position
.
u self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
v self.rect.centerx = ship.rect.centerx
w self.rect.top = ship.rect.top
# Store the bullet's position as a decimal value.
x self.y = float(self.rect.y)
y self.color = ai_settings.bullet_color self.speed_factor = ai_settings.bullet_speed_factor
The Bullet class inherits from Sprite , which we import from the
pygame.sprite module. When you use sprites, you can group related ele -
ments in your game and act on all the grouped elements at once. To
create a bullet instance,
__init__() needs the ai_settings , screen , and ship
instances, and we call
super() to inherit properly from Sprite .
note The call super(Bullet, self).__init__() uses Python 2.7 syntax. This works in
Python 3 too, or you can also write this call more simply as
super().__init__() .
At u , we create the bullet’s
rect attribute. The bullet is not based on an
image so we have to build a rect from scratch using the
pygame.Rect() class.
This class requires the x- and y-coordinates of the top-left corner of the
rect , and the width and height of the rect . We initialize the rect at (0, 0),
but we’ll move it to the correct location in the next two lines, because the
bullet’s position is dependent on the ship’s position. We get the width and
height of the bullet from the values stored in
ai_settings .
At v , we set the bullet’s
centerx to be the same as the ship’s rect.centerx .
The bullet should emerge from the top of the ship, so we set the top of the
bullet’s
rect to match the top of the ship’s rect , making it look like the bul -
let is fired from the ship w .
We store a decimal value for the bullet’s y-coordinate so we can make
fine adjustments to the bullet’s speed x . At y, we store the bullet’s color
and speed settings in
self.color and self.speed_factor .

A Ship That Fires Bullets 259
Here’s the second part of bull et.py, update() and draw_bullet() :
bullet.py def update(self):
"""Move the bullet up the screen."""
# Update the decimal position of the bullet.
u self.y -= self.speed_factor # Update the rect position.
v self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen."""
w pygame.draw.rect(self.screen, self.color, self.rect)
The update() method manages the bullet’s position. When a bullet
is fired, it moves up the screen, which corresponds to a decreasing
y-coordinate value; so to update the position, we subtract the amount
stored in
self.speed_factor from self.y u . We then use the value of self.y
to set the value of
self.rect.y v . The speed_factor attribute allows us to
increase the speed of the bullets as the game progresses or as needed to
refine the game’s behavior. Once fired, a bullet’s x-coordinate value never
changes, so it will only travel vertically in a straight line. When we want to draw a bullet, we’ll call
draw_bullet() . The draw.rect()
function fills the part of the screen defined by the bullet’s
rect with the
color stored in
self.color w .
Storing Bullets in a Group
Now that we have a Bullet class and the necessar y settings defined, we can
write code to fire a bullet each time the player presses the spacebar. First,
we’ll create a group in alien_invasion.py to store all the live bullets so we
can manage the bullets that have already been fired. This group will be an
instance of the class
pygame.sprite.Group , which behaves like a list with some
extra functionality that’s helpful when building games. We’ll use this group
to draw bullets to the screen on each pass through the main loop and to
update each bullet’s position:
alien_ import pygame
invasion.py from pygame.sprite import Group
--snip--
def run_game():
--snip--
# Make a ship.
ship = Ship(ai_settings, screen)
# Make a group to store bullets in.
u bullets = Group()
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()

260 Chapter 12
v bullets.update() gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
We import Group from pygame.sprite . At u , we make an instance of Group
and call it
bullets . This group is created outside of the while loop so we
don’t create a new group of bullets each time the loop cycles.
note If you make a group like this inside the loop, you’ll be creating thousands of groups
of bullets and your game will probably slow to a crawl. If your game freezes up, look
carefully at what’s happening in your main
while loop.
We pass
bullets to check_events() and update_screen() . We’ll need to work
w ith
bullets in check_events() when the spacebar is pressed, and we’ll need
to update the bullets that are being drawn to the screen in
update_screen() .
W hen you call
update() on a group v , the group automatically calls
update() for each sprite in the group. The line bullets.update() calls
bullet.update() for each bullet we place in the group bullets .
Firing Bullets
In game_functions.py , we need to modify check_keydown_events() to fire a bullet
when the spacebar is pressed. We don’t need to change
check_keyup_events()
because nothing happens when the key is released. We also need to modify
update_screen() to make sure each bullet is redrawn to the screen before we
call
flip() . Here are the relevant changes to game_functions.py :
game_ --snip--
functions.py from bullet import Bullet
u def check_keydown_events(event, ai_settings, screen, ship, bullets): --snip--
v elif event.key == pygame.K_SPACE: # Create a new bullet and add it to the bullets group.
new_bullet = Bullet(ai_settings, screen, ship)
--snip--
w def check_events(ai_settings, screen, ship, bullets): """Respond to keypresses and mouse events."""
for event in pygame.event.get():
--snip--
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bull
ets)
--snip--
x def update_screen(ai_settings, screen, ship, bullets): --snip--
# Redraw all bullets behind ship and aliens.
y for bullet in bullets.sprites(): bullet.draw_bullet()

A Ship That Fires Bullets 2 61
ship.blitme()
--snip--
The group bullets is passed to check_keydown_events() u . W hen the
player presses the spacebar, we create a new bullet (a
Bullet instance that we
name
new_bullet ) and add it to the group bullets v using the add() method;
the code
bullets.add(new_bullet) stores the new bullet in the group bullets .
bullets as a parameter in the definition of check_
events()
w , and we need to pass bullets as an argument in the call to
check_keydown_events() as well.
We give the
bullets parameter to update_screen() at x , which draws
the bullets to the screen. The
bullets.sprites() method returns a list of all
sprites in the group
bullets . To draw all fired bullets to the screen, we loop
through the sprites in
bullets and call draw_bullet() on each one y .
If you run alien_invasion.py now, you should be able to move the ship
right and left, and fire as many bullets as you want. The bullets travel up the
screen and disappear when they reach the top, as shown in Figure 12-3. You
can alter the size, color, and speed of the bullets in settings.py .
Figure 12-3: The ship after firing a series of bullets
Deleting Old Bullets
At the moment, the bullets disappear when they reach the top, but only
because Pygame can’t draw them above the top of the screen. The bullets
actually continue to exist; their y-coordinate values just grow increasingly
negative. This is a problem, because they continue to consume memor y and
processing power.

262 Chapter 12
We need to get rid of these old bullets, or the game will slow down from
doing so much unnecessar y work. To do this, we need to detect when the
bottom value of a bullet’s rect has a value of 0, which indicates the bullet has
passed off the top of the screen:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()

# Get rid of bullets that have disappeared.
u for bullet in bullets.copy():
v if bullet.rect.bottom <= 0:
w bullets.remove(bullet)
x print(len(bullets))
gf.update_screen(ai_settings, screen, ship, bullets)
You shouldn’t remove items from a list or group within a for loop, so
we have to loop over a copy of the group. We use the
copy() method to set
up the
for loop u , which enables us to modify bullets inside the loop. We
check each bullet to see whether it has disappeared off the top of the screen
at v . If it has, we remove it from
bullets w. At x we insert a print st atement
to show how many bullets currently exist in the game and verify that they’re
being deleted. If this code works correctly, we can watch the terminal output while fir -
ing bullets and see that the number of bullets decreases to zero after each
set of bullets has cleared the top of the screen. A fter you run the game and
verify that bullets are deleted properly, remove the
print statement. If you
leave it in, the game will slow down significantly because it takes more time
to write output to the terminal than it does to draw graphics to the game
w indow.
Limiting the Number of Bullets
Many shooting games limit the number of bullets a player can have on the
screen at one time to encourage players to shoot accurately. We’ll do the
same in A lien Invasion. First, store the number of bullets allowed in settings.py :
settings.py # Bullet settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3

A Ship That Fires Bullets 263
This limits the player to three bullets at a time. We’ll use this setting
in game_functions.py to check how many bullets exist before creating a new
bullet in
check_keydown_events() :
game_ def check_keydown_events(event, ai_settings, screen, ship, bullets):
functions.py --snip--
elif event.key == pygame.K_SPACE:
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
When the spacebar is pressed, we check the length of bullets . If
len(bullets) is less than three, we create a new bullet. But if three bullets
are already active, nothing happens when the spacebar is pressed. If you run
the game now, you should be able to fire bullets only in groups of three.
Creating the update_bullets() Function
We want to keep our main alien_invasion.py program file as simple as
possible, so now that we’ve written and checked the bullet management
code we can move it to the
game_functions module. We’ll create a new func -
tion called
update_bullets() and add it to the end of game_functions.py :
game_ def update_bullets(bullets):
functions.py """Update position of bullets and get rid of old bullets."""
# Update bullet positions.
bullets.update()
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
The code for update_bullets() is cut and pasted from alien_invasion.py ;
the only parameter it needs is the group
bullets .
The
while loop in alien_invasion.py looks simple again:
alien_ # Start the main loop for the game.
invasion.py while True:
u gf.check_events(ai_settings, screen, ship, bullets)
v ship.update()
w gf.update_bullets(bullets)
x gf.update_screen(ai_settings, screen, ship, bullets)
We’ve made it so that our main loop contains only minimal code so
we can quickly read the function names and understand what’s happen -
ing in the game. The main loop checks for player input at u , and then it
updates the position of the ship at v and any bullets that have been fired
at w . We then use the updated positions to draw a new screen at x .

264 Chapter 12
Creating the fire_bullet() Function
Let’s move the code for firing a bullet to a separate function so we
can use a single line of code to fire a bullet and keep the
elif block in
check_keydown_events() simple:
game_ def check_keydown_events(event, ai_settings, screen, ship, bullets):
functions.py """Respond to keypresses."""
--snip--
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
def fire_bullet(ai_settings, screen, ship, bullets):
"""Fire a bullet if limit not reached yet."""
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
The function fire_bullet() simply contains the code that was used to
fire a bullet when the spacebar is pressed, and we add a call to
fire_bullet()
in
check_keydown_events() when the spacebar is pressed.
Run alien_invasion.py one more time, and make sure you can still fire
bullets without errors.
t ry It y ourself
12 - 5 . Sideways Shooter: Write a game that places a ship on the left side of the
screen and allows the player to move the ship up and down . Make the ship fire
a bullet that travels right across the screen when the player presses the space -
bar . Make sure bullets are deleted once they disappear off the screen .
summary
In this chapter, you learned to make a plan for a game. You learned the basic
structure of a game written in Pygame. You learned to set a background color
and store settings in a separate class where they can be made available to all
parts of the game. You saw how to draw an image to the screen and give the
player control over the movement of game elements. You learned to create
elements that move on their own, like bullets flying up a screen, and how to
delete objects that are no longer needed. You learned to refactor code in a
project on a regular basis to facilitate ongoing development.
In Chapter 13, we’ll add aliens to Alien Invasion. By the end of
Chapter 13, you’ll be able to shoot down aliens, hopefully before they

13
alIens!
In this chapter we’ll add aliens to Alien
Invasion. First, we’ll add one alien near
the top of the screen, and then we’ll gener -
ate a whole fleet of aliens. We’ll make the fleet
advance sideways and down, and we’ll get rid of any
aliens hit by a bullet. Finally, we’ll limit the number of
ships a player has and end the game when the player
runs out of ships.
and about managing a larger project. You’ll also learn to detect collisions
between game objects, like bullets and aliens. Detecting collisions helps
you define interactions between elements in your games: you can confine a
character inside the walls of a maze or pass a ball between two characters.
We’ll also continue to work from a plan that we revisit occasionally to main -
tain the focus of our code-writing sessions. Before we start writing new code to add a fleet of aliens to the screen,
let’s look at the project and update our plan.

266 Chapter 13
reviewing y our Project
When you’re beginning a new phase of development on a larger project, it’s
always a good idea to revisit your plan and clarify what you want to accom -
plish with the code you’re about to write. In this chapter we will:
• Examine our code and determine if we need to refactor before imple -
menting new features.
• Add a single alien to the top-left corner of the screen with appropriate
spacing around it.
• Use the spacing around the first alien and the overall screen size to
determine how many aliens can fit on the screen. We’ll write a loop to
create aliens to fill the upper portion of the screen.
• Make the fleet move sideways and down until the entire fleet is shot
down, an alien hits the ship, or an alien reaches the ground. If the
whole fleet is shot down, we’ll create a new fleet. If an alien hits the
ship or the ground, we’ll destroy the ship and create a new fleet.
• Limit the number of ships the player can use, and end the game when
the player has used up the allotment of ships.
We’ll refine this plan as we implement features, but this is sufficient to
st art w ith. You should also review code when you’re about to begin working on a
new series of features in a project. Because each new phase typically makes
a project more complex, it’s best to clean up cluttered or inefficient code. Although we don’t have much cleanup to do right now because we’ve
been refactoring as we go, it’s annoying to use the mouse to close the game
each time we run it to test a new feature. Let’s quickly add a keyboard short -
cut to end the game when the user presses Q :
game_ def check_keydown_events(event, ai_settings, screen, ship, bullets):
functions.py --snip--
elif event.key == pygame.K_q:
sys.exit()
In check_keydown_events() we add a new block that ends the game when Q
is pressed. This is a fairly safe change because the Q key is far from the arrow
keys and the spacebar, so it’s unlikely a player will accidentally press Q and
quit the game. Now, when testing, you can press Q to close the game rather
than using your mouse to close the window.
Creating the f irst alien
Placing one alien on the screen is like placing a ship on the screen.
The behavior of each alien is controlled by a class called
Alien , which
we’ll structure like the
Ship class. We’ll continue using bitmap images
for simplicity. You can find your own image for an alien or use the one

Aliens! 267
shown in Figure 13 -1, which is available in the book’s resources through
https://www.nostarch.com/pythoncrashcourse/ . This image has a gray back-
ground, which matches the screen’s background color. Make sure to save
the image file you choose in the images folder.
Figure 13-1: The alien we’ll use to build
the fleet
Creating the Alien Class
Now we’ll write the Alien class:
alien.py import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""A class to represent a single alien in the fleet."""
def __init__(self, ai_settings, screen):
"""Initialize the alien and set its starting position."""
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the alien image and set its rect attribute.
self.rect = self.image.get_rect()
# Start each new alien near the top left of the screen.
u self.rect.x = self.rect.width self.rect.y = self.rect.height
# Store the alien's exact position.
self.x = float(self.rect.x)
def blitme(self):
"""Draw the alien at its current location."""
self.screen.blit(self.image, self.rect)

268 Chapter 13
Most of this class is like the Ship class except for the placement of the
alien. We initially place each alien near the top-left corner of the screen,
adding a space to the left of it that’s equal to the alien’s width and a space
above it equal to its height u .
Creating an Instance of the Alien
Now we create an instance of Alien in alien_invasion.py:
alien_ --snip--
invasion.py from ship import Ship
from alien import Alien
import game_functions as gf
def run_game():
--snip--
# Make an alien.
alien = Alien(ai_settings, screen)
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, alien, bullets)
run_game()
Here we’re importing the new Alien class and creating an instance of
Alien just before entering the main while loop. Because we’re not changing
the alien’s position yet, we aren’t adding anything new inside the loop; how -
ever, we do modify the call to
update_screen() to pass it the alien instance.
Making the Alien Appear Onscreen
To make the alien appear onscreen, we call its blitme() method in
update_screen() :
game_ def update_screen(ai_settings, screen, ship, alien, bullets):
functions.py --snip--

# Redraw all bullets behind ship and aliens.
for bullet in bullets:
bullet.draw_bullet()
ship.blitme()
alien.blitme()

# Make the most recently drawn screen visible.
pygame.display.flip()

Aliens! 269
We draw the alien onscreen after the ship and the bullets have been
drawn, so the aliens will be the top layer of the screen. Figure 13 -2 shows
the first alien on the screen.
Figure 13-2: The first alien appears.
Now that the first alien appears correctly, we’ll write the code to draw
an entire fleet.
Building the a lien fleet
To draw a fleet, we need to figure out how many aliens can fit across the
screen and how many rows of aliens can fit down the screen. We’ll first fig -
ure out the horizontal spacing between aliens and create a row; then we’ll
determine the vertical spacing and create an entire fleet.
Determining How Many Aliens Fit in a Row
To figure out how many aliens fit in a row, let’s look at how much horizontal
space we have. The screen width is stored in
ai_settings.screen_width , but we
need an empty margin on either side of the screen. We’ll make this margin
the width of one alien. Because we have two margins, the available space for
aliens is the screen width minus two alien widths:
available_space_x = ai_settings.screen_width – (2 * alien_width)

270 Chapter 13
We also need to set the spacing between aliens; we’ll make it one alien
width. The space needed to display one alien is twice its width: one width
for the alien and one width for the empty space to its right. To find the
number of aliens that fit across the screen, we divide the available space by
two times the width of an alien:
number_aliens_x = available_space_x / (2 * alien_width)
We’ll include these calculations when we create the fleet.
note One great aspect about calculations in programming is that you don’t have to be sure
your formula is correct when you first write it. You can try it out and see if it works.
At worst, you’ll have a screen that’s overcrowded with aliens or has too few aliens.
You can revise your calculation based on what you see on the screen.
Creating Rows of Aliens
To create a row, first create an empty group called aliens in alien_invasion .py
to hold all of our aliens, and then call a function in game_functions.py to
create a fleet:
alien_ import pygame
invasion.py from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
--snip--
# Make a ship, a group of bullets, and a group of aliens.
ship = Ship(ai_settings, screen)
bullets = Group()
u aliens = Group()
# Create the fleet of aliens.
v gf.create_fleet(ai_settings, screen, aliens)
# Start the main loop for the game.
while True:
--snip—
w gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
Because we’re no longer creating aliens directly in alien_invasion.py , we
don’t need to import the
Alien class into this file.
Create an empty group to hold all of the aliens in the game u . Then,
call the new function
create_fleet() v , which we’ll write shortly, and pass
it the
ai_settings , the screen object, and the empty group aliens . Next, mod -
ify the call to
update_screen() to give it access to the group of aliens w .

Aliens! 271
We also need to modify update_screen() :
game_ def update_screen(ai_settings, screen, ship, aliens, bullets):
functions.py --snip--
ship.blitme()
aliens.draw(screen)

# Make the most recently drawn screen visible.
pygame.display.flip()
W hen you call draw() on a group, Pygame automatically draws each ele -
ment in the group at the position defined by its
rect attribute. In this case,
aliens.draw(screen) draws each alien in the group to the screen.
Creating the Fleet
Now we can create the fleet. Here’s the new function create_fleet() , which
we place at the end of game_functions.py . We also need to import the
Alien
class, so make sure you add an
import statement at the top of the file:
game_ --snip--
functions.py from bullet import Bullet
from alien import Alien
--snip--
def create_fleet(ai_settings, screen, aliens):
"""Create a full fleet of aliens."""
# Create an alien and find the number of aliens in a row.
# Spacing between each alien is equal to one alien width.
u alien = Alien(ai_settings, screen)
v alien_width = alien.rect.width
w available_space_x = ai_settings.screen_width - 2 * alien_width
x number_aliens_x = int(available_space_x / (2 * alien_width))
# Create the first row of aliens.
y for alien_number in range(number_aliens_x): # Create an alien and place it in the row.
z alien = Alien(ai_settings, screen) alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
We’ve already thought through most of this code. We need to know the
alien’s width and height in order to place aliens, so we create an alien at u
before we perform calculations. This alien won’t be part of the fleet, so
don’t add it to the group
aliens . At v we get the alien’s width from its rect
attribute and store this value in
alien_width so we don’t have to keep working
through the
rect attribute. At w we calculate the horizontal space available
for aliens and the number of aliens that can fit into that space. The only change here from our original formulas is that we’re using
int() to ensure we end up with an integer number of aliens x because
we don’t want to create partial aliens, and the
range() function needs an

272 Chapter 13
integer. The int() function drops the decimal part of a number, effectively
rounding down. (This is helpful because we’d rather have a little extra
space in each row than an overly crowded row.) Next, set up a loop that counts from 0 to the number of aliens we
need to make y . In the main body of the loop, create a new alien and
then set its x-coordinate value to place it in the row z . Each alien is
pushed to the right one alien width from the left margin. Next, we mul -
tiply the alien width by 2 to account for the space each alien takes up,
including the empty space to its right, and we multiply this amount by
the alien’s position in the row. Then we add each new alien to the group
aliens .
When you run Alien Invasion, you should see the first row of aliens
appear, as in Figure 13 -3.
Figure 13-3: The first row of aliens
The first row is offset to the left, which is actually good for gameplay
because we want the fleet to move right until it hits the edge of the screen,
then drop down a bit, then move left, and so forth. Like the classic game
Space Invaders , this movement is more interesting than having the fleet drop
straight down. We’ll continue this motion until all aliens are shot down or
until an alien hits the ship or the bottom of the screen.
note Depending on the screen width you’ve chosen, the alignment of the first row of aliens
may look slightly different on your system.

Aliens! 273
Refactoring create_fleet()
If we were finished creating a fleet, we’d probably leave create_fleet()
as is, but we have more work to do, so let’s clean up the function a bit.
Here’s
create_fleet() with two new functions: get_number_aliens_x() and
create_alien() :
game_   u def get_number_aliens_x(ai_settings, alien_width):
functions.py """Determine the number of aliens that fit in a row."""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x

def create_alien(ai_settings, screen, aliens, alien_number):
"""Create an alien and place it in the row."""
alien = Alien(ai_settings, screen)
v alien_width = alien.rect.width alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x

def create_fleet(ai_settings, screen, aliens):
"""Create a full fleet of aliens."""
# Create an alien and find the number of aliens in a row.
alien = Alien(ai_settings, screen)
w number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.widt
h)
# Create the first row of aliens.
for alien_number in range(number_aliens_x):
x create_alien(ai_settings, screen, aliens, alien_number)
The body of get_number_aliens_x() is exactly as it was in create_fleet() u .
The body of
create_alien() is also unchanged from create_fleet() except
that we use the alien that was just created to get the alien width v . At w we
replace the code for determining the horizontal spacing with a call to
get_
number_aliens_x()
, and we remove the line referring to alien_width , because
that’s now handled inside
create_alien() . At x we call create_alien() . This
refactoring will make it easier to add new rows and create an entire fleet.
To finish the fleet, determine the number of rows that fit on the screen
and then repeat the loop (for creating the aliens in one row) that number
of times. To determine the number of rows, we find the available vertical
space by subtracting the alien height from the top, the ship height from the
bottom, and two alien heights from the bottom of the screen:
available_space_y = ai_settings.screen_height – 3 * alien_height –
ship_height

2 74 Chapter 13
The result will create some empty space above the ship, so the player
has some time to start shooting aliens at the beginning of each level. Each row needs some empty space below it, which we’ll make equal to
the height of one alien. To find the number of rows, we divide the available
space by two times the height of an alien. (Again, if these calculations are
off, we’ll see it right away and adjust until we have reasonable spacing.)
number_rows = available_height_y / (2 * alien_height)
Now that we know how many rows fit in a fleet, we can repeat the code
for creating a row:
game_   u def get_number_rows(ai_settings, ship_height, alien_height):
functions.py """Determine the number of rows of aliens that fit on the screen."""
v available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows

def create_alien(ai_settings, screen, aliens, alien_number, row_number)
:
--snip--
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
w alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number aliens.add(alien)
def create_fleet(ai_settings, screen, ship, aliens):
--snip--
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width
)
number_rows = get_number_rows(ai_settings, ship.rect.height,
alien.rect.height)

# Create the fleet of aliens.
x for row_number in range(number_rows): for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, aliens, alien_number,
row_number)
To calculate the number of rows we can fit on the screen, we write
our
available_space_y and number_rows calculations into the function get_
number_rows()
u , which is similar to get_number_aliens_x() . The calculation is
wrapped in parentheses so the outcome can be split over two lines, which
results in lines of 79 characters or less as is recommended v . We use
int()
because we don’t want to create a partial row of aliens. To create multiple rows, we use two nested loops: one outer and one
inner loop x . The inner loop creates the aliens in one row. The outer loop
counts from 0 to the number of rows we want; Python will use the code for
making a single row and repeat it
number_rows times.

Aliens! 275
To nest the loops, write the new for loop and indent the code you want
to repeat. (Most text editors make it easy to indent and unindent blocks of
code, but for help see Appendix B.) Now when we call
create_alien() , we
include an argument for the row number so each row can be placed farther
down the screen. The definition of
create_alien() needs a parameter to hold the row num -
ber. Within
create_alien() , we change an alien’s y-coordinate value when it’s
not in the first row w by starting with one alien’s height to create empty space
at the top of the screen. Each row starts two alien heights below the last row,
so we multiply the alien height by two and then by the row number. The first
row number is 0, so the vertical placement of the first row is unchanged. All
subsequent rows are placed farther down the screen. The definition of
create_fleet() also has a new parameter for the ship
object, which means we need to include the
ship argument in the call to
create_fleet() in alien_invasion.py :
alien_ # Create the fleet of aliens.
invasion.py gf.create_fleet(ai_settings, screen, ship, aliens)
When you run the game now, you should see a fleet of aliens, as in
Figure 13 - 4.
Figure 13- 4: The full fleet appears.
In the next section, we’ll make the fleet move!

276 Chapter 13
Try I T y o urself
13 -1. Stars: Find an image of a star. Make a grid of stars appear on the screen.
13 -2 . Better Stars: You can make a more realistic star pattern by introducing
randomness when you place each star. Recall that you can get a random num-
ber like this:
from random import randint
random_number = randint(-10,10)
This code returns a random integer between −10 and 10. Using your code
in Exercise 13-1, adjust each star’s position by a random amount.
Making the Fleet Move
Now let’s make our fleet of aliens move to the right across the screen until
it hits the edge, and then make it drop a set amount and move in the other
direction. We’ll continue this movement until all aliens have been shot
down, one collides with the ship, or one reaches the bottom of the screen.
Let’s begin by making the fleet move to the right.
Moving the Aliens Right
To move the aliens, we’ll use an update() method in alien.py , which we’ll
call for each alien in the group of aliens. First, add a setting to control the
speed of each alien:
settings.py def __init__(self):
--snip--
# Alien settings
self.alien_speed_factor = 1
Then, use this setting to implement update() :
alien.py def update(self):
"""Move the alien right."""
u self.x += self.ai_settings.alien_speed_factor
v self.rect.x = self.x
Each time we update an alien’s position, we move it to the right by the
amount stored in
alien_speed_factor . We track the alien’s exact position
with the
self.x attribute, which can hold decimal values u . We then use
the value of
self.x to update the position of the alien’s rect v .

Aliens! 277
In the main while loop, we have calls to update the ship and bullets.
Now we need to update the position of each alien as well:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_aliens(aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
We update the aliens’ positions after the bullets have been updated,
because we’ll soon be checking to see whether any bullets hit any aliens. Finally, add the new function
update_aliens() at the end of the file
game _functions.py :
game_ def update_aliens(aliens):
functions.py """Update the postions of all aliens in the fleet."""
aliens.update()
We use the update() method on the aliens group, which automatically
calls each alien’s
update() method. When you run Alien Invasion now, you
should see the fleet move right and disappear off the side of the screen.
Creating Settings for Fleet Direction
Now we’ll create the settings that will make the fleet move down the screen
and to the left when it hits the right edge of the screen. Here’s how to imple -
ment this behavior:
settings.py # Alien settings
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
The setting fleet_drop_speed controls how quickly the fleet drops down
the screen each time an alien reaches either edge. It’s helpful to separate
this speed from the aliens’ horizontal speed so you can adjust the two
speeds independently. To implement the setting
fleet_direction , we could use a text value, such
as
'left' or 'right' , but we’d end up with if-elif statements testing for the
fleet direction. Instead, because we have only two directions to deal with,
let’s use the values 1 and −1 and switch between them each time the fleet
changes direction. (Using numbers also makes sense because moving right
involves adding to each alien’s x-coordinate value, and moving left involves
subtracting from each alien’s x-coordinate value.)

278 Chapter 13
Checking to See Whether an Alien Has Hit the Edge
Now we need a method to check whether an alien is at either edge, and
we need to modify
update() to allow each alien to move in the appropriate
direction:
alien.py def check_edges(self):
"""Return True if alien is at edge of screen."""
screen_rect = self.screen.get_rect()
u if self.rect.right >= screen_rect.right: return True
v elif self.rect.left <= 0: return True

def update(self):
"""Move the alien right or left."""
w self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x = self.x
We can call the new method check_edges() on any alien to see if it’s at
the left or right edge. The alien is at the right edge if the
right attribute of
it s
rect is greater than or equal to the right attribute of the screen’s rect u .
It’s at the left edge if its
left value is less than or equal to 0 v .
We modify the method
update() to allow motion to the left or right w
by multiplying the alien’s speed factor by the value of
fleet_direction . If
fleet_direction is 1, the value of alien_speed_factor will be added to the
alien’s current position, moving the alien to the right; if
fleet_direction
is −1, the value will be subtracted from the alien’s position, moving the
alien to the left.
Dropping the Fleet and Changing Direction
When an alien reaches the edge, the entire fleet needs to drop down and
change direction. We therefore need to make some substantial changes
in game_functions.py because that’s where we check to see if any aliens
are at the left or right edge. We’ll make this happen by writing the func -
tions
check_fleet_edges() and change_fleet_direction() , and then modifying
update_aliens() :
game_ def check_fleet_edges(ai_settings, aliens):
functions.py """Respond appropriately if any aliens have reached an edge."""
u for alien in aliens.sprites(): if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break

Aliens! 279
def change_fleet_direction(ai_settings, aliens):
"""Drop the entire fleet and change the fleet's direction."""
for alien in aliens.sprites():
v alien.rect.y += ai_settings.fleet_drop_speed ai_settings.fleet_direction *= -1

def update_aliens(ai_settings, aliens):
"""
Check if the fleet is at an edge,
and then update the postions of all aliens in the fleet.
"""
w check_fleet_edges(ai_settings, aliens) aliens.update()
In check_fleet_edges() , we loop through the fleet and call check_edges() on
each alien u . If
check_edges() returns True , we know an alien is at an edge and
the whole fleet needs to change direction, so we call
change_fleet_direction()
and break out of the loop. In
change_fleet_direction() , we loop through all
the aliens and drop each one using the setting
fleet_drop_speed v ; then we
change the value of
fleet_direction by multiplying its current value by −1.
We’ve modified the function
update_aliens() to determine whether any
aliens are at an edge by calling
check_fleet_edges() w . This function needs
an
ai_settings parameter, so we include an argument for ai_settings in the
call to
update_aliens() :
alien_ # Start the main loop for the game.

invasion.py

while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_aliens(ai_settings, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
If you run the game now, the fleet should move back and forth between
the edges of the screen and drop down ever y time it hits an edge. Now we
can begin shooting down aliens and watch for any aliens that hit the ship
or reach the bottom of the screen.
Try I T y o urself
13 -3 . Raindrops: Find an image of a raindrop and create a grid of raindrops.
Make the raindrops fall toward the bottom of the screen until they disappear.
13-4. Steady Rain: Modify your code in Exercise 13-3 so that when a row of
raindrops disappears off the bottom of the screen, a new row appears at the
top of the screen and begins to fall.

280 Chapter 13
shooting a liens
We’ve built our ship and a fleet of aliens, but when the bullets reach
the aliens, they simply pass through because we aren’t checking for col -
lisions. In game programming, collisions happen when game elements
overlap. To make the bullets shoot down aliens, we’ll use the method
sprite.groupcollide() to look for collisions between members of two
groups.
Detecting Bullet Collisions
We want to know right away when a bullet hits an alien so we can make an
alien disappear as soon as it’s hit. To do this, we’ll look for collisions imme -
diately after updating a bullet’s position. The
sprite.groupcollide() method compares each bullet’s rect with each
a l ien’s
rect and returns a dictionar y containing the bullets and aliens that
have collided. Each key in the dictionar y is a bullet, and the corresponding
value is the alien that was hit. (We’ll use this dictionar y when we implement
a scoring system in Chapter 14.) Use this code to check for collisions in the
update_bullets() function:
game_ def update_bullets(aliens, bullets):
functions.py """Update position of bullets and get rid of old bullets."""
--snip--
# Check for any bullets that have hit aliens.
# If so, get rid of the bullet and the alien.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True
)
The new line we added loops through each bullet in the group bullets
and then loops through each alien in the group
aliens . W henever the rect s
of a bullet and alien overlap,
groupcollide() adds a key-value pair to the dic -
tionar y it returns. The two
True arguments tell Pygame whether to delete
the bullets and aliens that have collided. (To make a high-powered bullet
that’s able to travel to the top of the screen, destroying ever y alien in its
path, you could set the first Boolean argument to
False and keep the second
Boolean argument set to
True . The aliens hit would disappear, but all bullets
would stay active until they disappeared off the top of the screen.) We pass the argument
aliens in the call to update_bullets() :
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(aliens, bullets)
gf.update_aliens(ai_settings, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)

Aliens! 281
When you run Alien Invasion now, aliens you hit should disappear.
Figure 13 -5 shows a fleet that has been partially shot down.
Figure 13-5: We can shoot aliens!
Making Larger Bullets for Testing
You can test many features of the game simply by running the game, but
some features are tedious to test in the normal version of a game. For
example, it’s a lot of work to shoot down ever y alien on the screen mul -
tiple times to test if your code responds to an empty fleet correctly. To test particular features, you can change certain game settings to
focus on a particular area. For example, you might shrink the screen so
there are fewer aliens to shoot down or increase the bullet speed and give
yourself lots of bullets at once. My favorite change for testing Alien Invasion is to use super wide
bullets that remain active even after they’ve hit an alien (see Figure 13 - 6).
Tr y setting
bullet_width to 300 to see how quickly you can shoot down the
fleet! Changes like these will help you test the game more efficiently and
possibly spark ideas for giving players bonus powers. ( Just remember to
restore the settings to normal once you’re finished testing a feature.)

282 Chapter 13
Figure 13- 6: Extra-powerful bullets make some aspects of the game easier to test.
Repopulating the Fleet
One key feature of Alien Invasion is that the aliens are relentless: ever y time
the fleet is destroyed, a new fleet should appear.To make a new fleet of aliens appear after a fleet has been destroyed, first
check to see whether the group
aliens is empty. If it is, we call create_fleet() .
We’ll perform this check in
update_bullets() because that’s where individual
aliens are destroyed:
game_ def update_bullets(ai_settings, screen, ship, aliens, bullets):
functions.py --snip--
# Check for any bullets that have hit aliens.
# If so, get rid of the bullet and the alien.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True
)

u if len(aliens) == 0: # Destroy existing bullets and create new fleet.
v bullets.empty() create_fleet(ai_settings, screen, ship, aliens)
At u we check whether the group aliens is empty. If it is, we get rid
of any existing bullets by using the
empty() method, which removes all the
remaining sprites from a group v . We also call
create_fleet() , which fills
the screen with aliens again.

Aliens! 283
The definition of update_bullets() now has the additional parameters
ai_settings , screen , and ship , so we need to update the call to update_bullets()
in alien_invasion.py :
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
gf.update_aliens(ai_settings, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Now a new fleet appears as soon as you destroy the current fleet.
Speeding Up the Bullets
If you’ve tried firing at the aliens in the game’s current state, you may have
noticed that the bullets have slowed down a bit. This is because Pygame
is now doing more work on each pass through the loop. We can increase
the speed of the bullets by adjusting the value of
bullet_speed_factor in
settings.py . If we increase this value (to 3, for example), the bullets should
travel up the screen at a reasonable speed again:
settings.py # Bullet settings
self.bullet_speed_factor = 3
self.bullet_width = 3
--snip--
The best value for this setting depends on the speed of your system, so
find a value that works for you.
Refactoring update_bullets()
Let’s refactor update_bullets() so it’s not doing so many different tasks. We’ll
move the code for dealing with bullet-alien collisions to a separate function:
game_ def update_bullets(ai_settings, screen, ship, aliens, bullets):
functions.py --snip--
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bu
llets)

def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bu
llets):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True
)

284 Chapter 13
if len(aliens) == 0:
# Destroy existing bullets and create new fleet.
bullets.empty()
create_fleet(ai_settings, screen, ship, aliens)
We’ve created a new function, check_bullet_alien_collisions() , to look
for collisions between bullets and aliens, and to respond appropriately if
the entire fleet has been destroyed. This keeps
update_bullets() from grow-
ing too long and simplifies further development.
Try I T y o urself
13 -5. Catch: Create a game that places a character that you can move left and
right at the bottom of the screen. Make a ball appear at a random position at
the top of the screen and fall down the screen at a steady rate. If your charac-
ter “catches” the ball by colliding with it, make the ball disappear. Make a new
ball each time your character catches the ball or whenever the ball disappears
off the bottom of the screen.
Ending the Game
What’s the fun and challenge in a game if you can’t lose? If the player doesn’t
shoot down the fleet quickly enough, we’ll have the aliens destroy the ship if
they hit it. At the same time, we’ll limit the number of ships a player can use
and we’ll destroy the ship when an alien reaches the bottom of the screen.
We’ll end the game when the player has used up all their ships.
Detecting Alien-Ship Collisions
We’ll start by checking for collisions between aliens and the ship so we can
respond appropriately when an alien hits it. We’ll check for alien-ship colli-
sions immediately after updating the position of each alien:
game_ def update_aliens(ai_settings, ship, aliens):

functions.py

"""
Check if the fleet is at an edge,
and then update the postions of all aliens in the fleet.
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()

# Look for alien-ship collisions.
u if pygame.sprite.spritecollideany(ship, aliens):
v print("Ship hit!!!")

Aliens! 285
The method spritecollideany() takes two arguments: a sprite and a
group. The method looks for any member of the group that’s collided with
the sprite and stops looping through the group as soon as it finds one mem -
ber that has collided with the sprite. Here, it loops through the group
aliens
and returns the first alien it finds that has collided with
ship .
If no collisions occur,
spritecollideany() returns None and the if block
at u won’t execute. If it finds an alien that’s collided with the ship, it returns
that alien and the
if block executes: it prints Ship hit!!! v. (When an alien
hits the ship, we’ll need to do a number of tasks: we’ll need to delete all
remaining aliens and bullets, recenter the ship, and create a new fleet.
Before we write code to do all this, we need to know that our approach for
detecting alien-ship collisions works correctly. Writing a
print statement is a
simple way to ensure we’re detecting collisions properly.) Now we need to pass
ship to update_aliens() :
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
gf.update_aliens(ai_settings, ship, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Now when you run Alien Invasion, Ship hit!!! should appear in the ter-
minal whenever an alien runs into the ship. When testing this feature, set
alien_drop_speed to a higher value such as 50 or 100 so that the aliens will
Responding to Alien-Ship Collisions
Now we need to figure out what happens when an alien collides with the
ship instance and creating a new one, we’ll
count how many times the ship has been hit by tracking statistics for the
game. (Tracking statistics will also be useful for scoring.) Let’s write a new class,
GameStats , to track game statistics, and save it as
game_stats.py :
game_stats.py class GameStats():
"""Track statistics for Alien Invasion."""

def __init__(self, ai_settings):
"""Initialize statistics."""
self.ai_settings = ai_settings
u self.reset_stats()
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.ai_settings.ship_limit
We’ll make one GameStats instance for the entire time Alien Invasion is
running, but we’ll need to reset some statistics each time the player starts

286 Chapter 13
a new game. To do this, we’ll initialize most of the statistics in the method
reset_stats() instead of directly in __init__() . We’ll call this method from
__init__() so the statistics are set properly when the GameStats instance is first
created u , but we’ll also be able to call
reset_stats() any time the player
starts a new game. Right now we have only one statistic,
ships_left , the value of which will
change throughout the game. The number of ships the player starts with is
stored in settings.py as
ship_limit :
settings.py # Ship settings
self.ship_speed_factor = 1.5
self.ship_limit = 3
We also need to make a few changes in alien_invasion.py , to create an
instance of
GameStats :
alien_ --snip--
invasion.py from settings import Settings
u from game_stats import GameStats --snip--
def run_game():
--snip--
pygame.display.set_caption("Alien Invasion")

# Create an instance to store game statistics.
v stats = GameStats(ai_settings) --snip--
# Start the main loop for the game.
while True:
--snip--
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
w gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets) --snip--
We import the new GameStats class u , make a stats instance v , and then
stats , screen , and ship arguments in the call to update_aliens() w .
We’ll use these arguments to track the number of ships the player has left
and to build a new fleet when an alien hits the ship. When an alien hits the ship, we subtract one from the number of ships
left, destroy all existing aliens and bullets, create a new fleet, and reposi -
tion the ship in the middle of the screen. (We’ll also pause the game for
a moment so the player can notice the collision and regroup before a new
fleet appears.) Let’s put most of this code in the function
ship_hit() :
game_ import sys
functions.py u from time import sleep
import pygame
--snip--

Aliens! 287
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""Respond to ship being hit by alien."""
# Decrement ships_left.
v stats.ships_left -= 1
# Empty the list of aliens and bullets.
w aliens.empty() bullets.empty()

# Create a new fleet and center the ship.
x create_fleet(ai_settings, screen, ship, aliens) ship.center_ship()

# Pause.
y sleep(0.5)
z def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
--snip--
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
We first import the sleep() function from the time module to pause the
game u . The new function
ship_hit() coordinates the response when the
ship is hit by an alien. Inside
ship_hit() , the number of ships left is reduced
by 1 v , after which we empty the groups
aliens and bullets w.
Next, we create a new fleet and center the ship x . (We’ll add the method
center_ship() to Ship in a moment.) Finally, we pause after the updates have
been made to all the game elements but before any changes have been drawn
to the screen so the player can see that their ship has been hit y . The screen
will freeze momentarily, and the player will see that the alien has hit the ship.
W hen the
sleep() function ends, the code will move on to the update_screen()
function, which will draw the new fleet to the screen. We also update the definition of
update_aliens() to include the param -
eters
stats , screen , and bullets z so it can pass these values in the call to
ship_hit() .
Here’s the new method
center_ship() ; add it to the end of ship.py :
ship.py def center_ship(self):
"""Center the ship on the screen."""
self.center = self.screen_rect.centerx
To center the ship, we set the value of the ship’s center attribute to
match the center of the screen, which we get through the
screen_rect
attribute.
note Notice that we never make more than one ship; we make only one ship instance for the
whole game and recenter it whenever the ship has been hit. The statistic
ships_left
will tell us when the player has run out of ships.

288 Chapter 13
Run the game, shoot a few aliens, and let an alien hit the ship. The
game should pause, and a new fleet should appear with the ship centered
at the bottom of the screen again.
Aliens that Reach the Bottom of the Screen
If an alien reaches the bottom of the screen, we’ll respond the same way we
do when an alien hits the ship. Add a new function to perform this check,
and call it from
update_aliens() :
game_ def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bulle
ts):
functions.py """Check if any aliens have reached the bottom of the screen."""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
u if alien.rect.bottom >= screen_rect.bottom: # Treat this the same as if the ship got hit.
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
break

def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
--snip--
# Look for aliens hitting the bottom of the screen.
v check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bull
ets)
The function check_aliens_bottom() checks to see whether any aliens
have reached the bottom of the screen. An alien reaches the bottom when
it s
rect.bottom value is greater than or equal to the screen’s rect.bottom attri -
bute u. If an alien reaches the bottom, we call
ship_hit() . If one alien hits
the bottom, there’s no need to check the rest, so we break out of the loop
after calling
ship_hit() .
We call
check_aliens_bottom() after updating the positions of all the
aliens and after looking for alien-ship collisions v . Now a new fleet will
appear ever y time the ship is hit by an alien or an alien reaches the bottom
of the screen.
Game Over!
Alien Invasion feels more complete now, but the game never ends. The
value of
ships_left just grows increasingly negative. Let’s add a game_active
flag as an attribute to
GameStats to end the game when the player runs out
of ships :
game_stats.py def __init__(self, settings):
--snip--
# Start Alien Invasion in an active state.
self.game_active = True

Aliens! 289
Now we add code to ship_hit() that sets game_active to False if the player
has used up all their ships:
game_ def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
functions.py """Respond to ship being hit by alien."""
if stats.ships_left > 0:
# Decrement ships_left.
stats.ships_left -= 1
--snip--
# Pause.
sleep(0.5)
else:
stats.game_active = False
Most of ship_hit() is unchanged. We’ve moved all of the existing code
into an
if block, which tests to make sure the player has at least one ship
remaining. If so, we create a new fleet, pause, and move on. If the player has
no ships left, we set
game_active to False .
Identifying when Parts of the g ame should r un
In alien_invasion.py we need to identify the parts of the game that should
always run and the parts that should run only when the game is active:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, ship, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullet
s)
gf.update_aliens(ai_settings, stats, screen, ship, aliens,
bullets)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
In the main loop, we always need to call check_events() , even if the game
is inactive. For example, we still need to know if the user presses Q to quit
the game or clicks the button to close the window. We also continue updat -
ing the screen so we can make changes to the screen while waiting to see
whether the player chooses to start a new game. The rest of the function
calls only need to happen when the game is active, because when the game
is inactive, we don’t need to update the positions of game elements. Now when you play Alien Invasion, the game should freeze when you’ve
used up all of your ships.

290 Chapter 13
Try I T y o urself
13 - 6 . Game Over: Using your code from Exercise 13-5 (page 284), keep track
of the number of times the player misses the ball. When they’ve missed the ball
three times, end the game.
Summary
In this chapter you learned how to add a large number of identical ele -
ments to a game by creating a fleet of aliens. You learned how to use
nested loops to create a grid of elements, and you made a large set of
game elements move by calling each element’s
update() method. You
learned to control the direction of objects on the screen and how to
respond to events, such as when the fleet reaches the edge of the screen.
You also learned how to detect and respond to collisions when bullets hit
aliens and aliens hit the ship. Finally, you learned how to track the statistics
in a game and use a
game_active flag to determine when the game was over.
In the final chapter of this project, we’ll add a Play button so the
player can choose when to start their first game and whether to play again
when the game ends. We’ll speed up the game each time the player shoots
down

the entire fleet, and we’ll add a scoring system. The final result will
be

a fully playable game!

14
sCor Ing
In this chapter we’ll finish the Alien
Invasion game. We’ll add a Play button to
start a game on demand or to restart a game
once it ends. We’ll also change the game so it
speeds up when the player moves up a level, and we’ll
implement a scoring system. By the end of the chap -
ter, you’ll know enough to start writing games that
increase in difficulty as a player progresses and that
show scores.

292 Chapter 14
In this section we’ll add a Play button that appears before a game begins
and reappears when the game ends so the player can play again.
Right now the game begins as soon as you run alien_invasion.py . L et’s
start the game in an inactive state and then prompt the player to click a Play
button to begin. To do this, enter the following in game_stats.py :
game_stats.py def __init__(self, ai_settings):
"""Initialize statistics."""
self.ai_settings = ai_settings
self.reset_stats()
# Start game in an inactive state.
self.game_active = False
def reset_stats(self):
--snip--
Now the game should start in an inactive state with no way for the
player to start it until we make a Play button.
Creating a Button Class
Because Pygame doesn’t have a built-in method for making buttons, we’ll
write a
Button class to create a filled rectangle with a label. You can use this
code to make any button in a game. Here’s the first part of the
Button class;
save it as button.py :
button.py import pygame.font
class Button():
u def __init__(self, ai_settings, screen, msg): """Initialize button attributes."""
self.screen = screen
self.screen_rect = screen.get_rect()

# Set the dimensions and properties of the button.
v self.width, self.height = 200, 50 self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
w self.font = pygame.font.SysFont(None, 48)
# Build the button's rect object and center it.
x self.rect = pygame.Rect(0, 0, self.width, self.height) self.rect.center = self.screen_rect.center

# The button message needs to be prepped only once.
y self.prep_msg(msg)

Scoring 293
First we import the pygame.font module, which lets Pygame render
text to the screen. The
__init__() method takes the parameters self , the
ai_settings and screen objects, and msg , which contains the text for the
button u . We set the button dimensions at v , and then we set
button_color
to color the button’s
rect object bright green and set text_color to render
the text in white. At w we prepare a
font attribute for rendering text. The None argument
tells Pygame to use the default font, and
48 determines the size of the text.
To center the button on the screen, we create a
rect for the button x and
set its
center attribute to match that of the screen.
Pygame works with text by rendering the string you want to display as
an image. At y we call
prep_msg() to handle this rendering.
Here’s the code for
prep_msg() :
button.py def prep_msg(self, msg):
"""Turn msg into a rendered image and center text on the button.
"""
u self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
v self.msg_image_rect = self.msg_image.get_rect() self.msg_image_rect.center = self.rect.center
The prep_msg() method needs a self parameter and the text to be ren -
dered as an image (
msg ). The call to font.render() turns the text stored in
msg into an image, which we then store in msg_image u . The font.render()
method also takes a Boolean value to turn antialiasing on or off (antialias -
ing makes the edges of the text smoother). The remaining arguments are
the specified font color and background color. We set antialiasing to
True
and set the text background to the same color as the button. (If you don’t
include a background color, Pygame will tr y to render the font with a trans -
parent background.) At v we center the text image on the button by creating a
rect from the
image and setting its
center attribute to match that of the button.
Finally, we create a
draw_button() method that we can call to display the
button onscreen:
button.py def draw_button(self):
# Draw blank button and then draw message.
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
We call screen.fill() to draw the rectangular portion of the button.
Then we call
screen.blit() to draw the text image to the screen, passing it
an image and the
rect object associated with the image. This completes the
Button class.

294 Chapter 14
Drawing the Button to the Screen
We’ll use the Button class to create a Play button. Because we need only
one Play button, we’ll create the button directly in alien_invasion.py as
shown here:
alien_ --snip--
invasion.py from game_stats import GameStats
from button import Button
--snip--
def run_game():
--snip--
pygame.display.set_caption("Alien Invasion")

# Make the Play button.
u play_button = Button(ai_settings, screen, "Play") --snip--
# Start the main loop for the game.
while True:
--snip--
v gf.update_screen(ai_settings, screen, stats, ship, aliens, bul
lets, play_button)
run_game()
We import Button and create an instance called play_button u, and then
we pass
play_button to update_screen() so the button appears when the screen
Next, modify
update_screen() so the Play button appears only when the
game is inactive:
game_ def update_screen(ai_settings, screen, stats, ship, aliens, bullets,
functions.py play_button):
"""Update images on the screen and flip to the new screen."""
--snip--

# Draw the play button if the game is inactive.
if not stats.game_active:
play_button.draw_button()

# Make the most recently drawn screen visible.
pygame.display.flip()
To make the Play button visible above all other elements on the screen,
we draw it after all other game elements have been drawn and before flip -
ping to a new screen. Now when you run Alien Invasion you should see a
Play button in the center of the screen, as shown in Figure 14 -1.

Scoring 295
Figure 14-1: A Play button appears when the game is inactive.
Starting the Game
To start a new game when the player clicks Play, add the following code to
game_functions.py to monitor mouse events over the button:
game_ def check_events(ai_settings, screen, stats, play_button, ship, bullets
):
functions.py """Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip--
u elif event.type == pygame.MOUSEBUTTONDOWN:
v mouse_x, mouse_y = pygame.mouse.get_pos()
w check_play_button(stats, play_button, mouse_x, mouse_y)
def check_play_button(stats, play_button, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
x if play_button.rect.collidepoint(mouse_x, mouse_y): stats.game_active = True
We’ve updated the definition of check_events() to accept the stats
and
play_button parameters. We’ll use stats to access the game_active flag and
play_button to check whether the Play button has been clicked.
Pygame detects a
MOUSEBUTTONDOWN event when the player clicks any where
on the screen u , but we want to restrict our game to respond to mouse clicks
only on the Play button. To accomplish this, we use
pygame.mouse.get_pos() ,
which returns a tuple containing the x- and y-coordinates of the mouse
cursor when the mouse button is clicked v . We send these values to the
function
check_play_button() w , which uses collidepoint() to see if the point
of the mouse click overlaps the region defined by the Play button’s
rect x .
If so, we set
game_active to True , and the game begins!

296 Chapter 14
The call to check_events() in alien_invasion.py needs to pass two addi -
tional arguments,
stats and play_button :
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, stats, play_button, ship,
bullets)
--snip--
At this point, you should be able to start and play a full game. When
the game ends, the value of
game_active should become False and the Play
button should reappear.
Resetting the Game
The code we just wrote works the first time the player clicks Play but not
once the first game ends, because the conditions that caused the game to
end haven’t been reset. To reset the game each time the player clicks Play, we need to reset the
game statistics, clear out the old aliens and bullets, build a new fleet, and
center the ship, as shown here:
game_ def check_play_button(ai_settings, screen, stats, play_button, ship, al
iens,
functions.py bullets, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
if play_button.rect.collidepoint(mouse_x, mouse_y):
# Reset the game statistics.
u stats.reset_stats() stats.game_active = True

# Empty the list of aliens and bullets.
v aliens.empty() bullets.empty()

# Create a new fleet and center the ship.
w create_fleet(ai_settings, screen, ship, aliens) ship.center_ship()
ai_settings , stats , ship , aliens , and bullets . It needs these objects to reset
the settings that have changed during the game and to refresh the visual
elements of the game. At u we reset the game statistics, which gives the player three new
ships. Then we set
game_active to True (so the game will begin as soon as
the code in this function finishes running), empty the
aliens and bullets
groups v, and create a new fleet and center the ship w .

Scoring 297
The definition of check_events() needs to be modified, as does the call
to
check_play_button() :
game_ def check_events(ai_settings, screen, stats, play_button, ship, aliens,
functions.py bullets):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip--
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
u check_play_button(ai_settings, screen, stats, play_button,
ship, aliens, bullets, mouse_x, mouse_y)
The definition of check_events() needs the aliens parameter, which it will
pass to
check_play_button() . We then update the call to check_play_button() so
it passes the appropriate arguments u .
Now update the call to
check_events() in alien_invasion.py so it passes the
aliens argument:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, stats, play_button, ship,
aliens, bullets)
--snip--
The game will now reset properly each time you click Play, allowing you
to play it as many times as you want!
Deactivating the Play Button
One issue with our Play button is that the button region on the screen will
continue to respond to clicks even when the Play button isn’t visible. Click
the Play button area by accident once a game has begun and the game will
restart! To fix this, set the game to start only when
game_active is False :
game_ def check_play_button(ai_settings, screen, stats, play_button, ship, al
iens,
functions.py bullets, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
u button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
v if button_clicked and not stats.game_active: # Reset the game statistics.
--snip--
The flag button_clicked stores a True or False value u , and the game
will restart only if Play is clicked and the game is not currently active v.
To test this behavior, start a new game and repeatedly click where the Play
button should be. If ever ything works as expected, clicking the Play button
area should have no effect on the gameplay.

298 Chapter 14
Hiding the Mouse Cursor
We want the mouse cursor visible in order to begin play, but once play
begins it only gets in the way. To fix this, we’ll make it invisible once the
game becomes active:
game_ def check_play_button(ai_settings, screen, stats, play_button, ship, al
iens,
functions.py bullets, mouse_x, mouse_y):
"""Start new game when the player clicks Play."""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
--snip--
Passing False to set_visible() tells Pygame to hide the cursor when the
mouse is over the game window. We’ll make the cursor reappear once the game ends so the player can
click Play to begin a new game. Here’s the code to do that:
game_ def ship_hit(ai_settings, screen, stats, ship, aliens, bullets):
functions.py """Respond to ship being hit by alien."""
if stats.ships_left > 0:
--snip--
else:
stats.game_active = False
pygame.mouse.set_visible(True)
We make the cursor visible again as soon as the game becomes inactive,
which happens in
ship_hit() . Attention to details like this makes your game
seem more professional and allows the player to focus on playing rather
than figuring out the user interface.
t ry It y ourself
14 -1. Press P to Play: Because Alien Invasion uses keyboard input to control the
ship, it’s best to start the game with a keypress . Add code that lets the player
press P to start . It may help to move some code from
check_play_button() to a
start_game() function that can be called from both check_play_button() and
check_keydown_events() .
14 -2 . Target Practice: Create a rectangle at the right edge of the screen that
moves up and down at a steady rate . Then have a ship appear on the left
side of the screen that the player can move up and down while firing bullets
at the moving, rectangular target . Add a Play button that starts the game, and
when the player misses the target three times, end the game and make the Play
button reappear . Let the player restart the game with this Play button .

Scoring 299
leveling u p
In our current game, once a player shoots down the entire alien fleet, the
player reaches a new level, but the game difficulty doesn’t change. Let’s
liven things up a bit and make the game more challenging by increasing
the speed of the game each time a player clears the screen.
Modifying the Speed Settings
We’ll first reorganize the Settings class to group the game settings into
static and changing ones. We’ll also make sure that settings that change
over the course of a game reset when we start a new game. Here’s the
__init__() method for settings.py :
settings.py def __init__(self):
"""Initialize the game's static settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)

# Ship settings
self.ship_limit = 3

# Bullet settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3

# Alien settings
self.fleet_drop_speed = 10

# How quickly the game speeds up
u self.speedup_scale = 1.1
v self.initialize_dynamic_settings()
We continue to initialize the settings that stay constant in the __init__()
method. At u we add a
speedup_scale setting to control how quickly the
game speeds up: a value of 2 will double the game speed ever y time the
player reaches a new level; a value of 1 will keep the speed constant. A speed
value like 1.1 should increase the speed enough to make the game challeng -
ing but not impossible. Finally, we call
initialize_dynamic_settings() to ini -
tialize the values for attributes that need to change throughout the course
of a game v .
Here’s the code for
initialize_dynamic_settings() :
settings.py def initialize_dynamic_settings(self):
"""Initialize settings that change throughout the game."""
self.ship_speed_factor = 1.5
self.bullet_speed_factor = 3

300 Chapter 14
self.alien_speed_factor = 1
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
This method sets the initial values for the ship, bullet, and alien speeds.
We’ll increase these speeds as the player progresses in the game and reset
them each time the player starts a new game. We include
fleet_direction in
this method so the aliens always move right at the beginning of a new game.
To increase the speeds of the ship, bullets, and aliens each time the player
reaches a new level, use
increase_speed() :
settings.py def increase_speed(self):
"""Increase speed settings."""
self.ship_speed_factor *= self.speedup_scale
self.bullet_speed_factor *= self.speedup_scale
self.alien_speed_factor *= self.speedup_scale
To increase the speed of these game elements, we multiply each speed
setting by the value of
speedup_scale .
We increase the game’s tempo by calling
increase_speed() in check_
bullet_alien_collisions()
when the last alien in a fleet has been shot down
but before creating a new fleet:
game_ def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bu
llets):
functions.py --snip--
if len(aliens) == 0:
# Destroy existing bullets, speed up game, and create new fleet.
bullets.empty()
ai_settings.increase_speed()
create_fleet(ai_settings, screen, ship, aliens)
Changing the values of the speed settings ship_speed_factor , alien_speed_
factor
, and bullet_speed_factor is enough to speed up the entire game!
Resetting the Speed
We need to return any changed settings to their initial values each time the
player starts a new game, or each new game would start with the increased
speed settings of the previous game:
game_ def check_play_button(ai_settings, screen, stats, play_button, ship, al
iens,
functions.py bullets, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:
# Reset the game settings.
ai_settings.initialize_dynamic_settings()
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
--snip--

Scoring 301
Playing Alien Invasion should be more fun and challenging now. Each
time you clear the screen, the game should speed up and become slightly
more difficult. If the game becomes too difficult too quickly, decrease the
value of
settings.speedup_scale , or if the game isn’t challenging enough,
increase the value slightly. Find a sweet spot by ramping up the difficulty in
a reasonable amount of time. The first couple of screens should be easy, the
next few challenging but doable, and subsequent screens almost impossibly
difficult.
t ry It y ourself
(page 298) . Make the target move faster as the game progresses, and restart
at the original speed when the player clicks Play .
scoring
Let’s implement a scoring system to track the game’s score in real time, as
well as to display the high score, level, and the number of ships remaining.
The score is a game statistic, so we’ll add a
score attribute to GameStats :
game_stats.py class GameStats():
--snip--
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
To reset the score each time a new game starts, we initialize score in
reset_stats() rather than __init__() .
Displaying the Score
To display the score on the screen, we first create a new class, Scoreboard . For
now this class will just display the current score, but we’ll use it to report
the high score, level, and number of ships remaining as well. Here’s the first
part of the class; save it as scoreboard.py :
scoreboard.py import pygame.font
class Scoreboard():
"""A class to report scoring information."""
u def __init__(self, ai_settings, screen, stats): """Initialize scorekeeping attributes."""
self.screen = screen

302 Chapter 14
self.screen_rect = screen.get_rect()
self.ai_settings = ai_settings
self.stats = stats

# Font settings for scoring information.
v self.text_color = (30, 30, 30)
w self.font = pygame.font.SysFont(None, 48)
# Prepare the initial score image.
x self.prep_score()
Because Scoreboard writes text to the screen, we begin by importing the
pygame.font module. Next, we give __init__() the parameters ai_settings ,
screen , and stats so it can report the values we’re tracking u . Then, we set a
text color v and instantiate a font object w .
To turn the text to be displayed into an image, we call
prep_score() x ,
which we define here:
scoreboard.py def prep_score(self):
"""Turn the score into a rendered image."""
u score_str = str(self.stats.score)
v self.score_image = self.font.render(score_str, True, self.text_color, self.ai_settings.bg_color)

# Display the score at the top right of the screen.
w self.score_rect = self.score_image.get_rect()
x self.score_rect.right = self.screen_rect.right - 20
y self.score_rect.top = 20
In prep_score() , we first turn the numerical value stats.score into a
string u , and then pass this string to
render() , which creates the image v .
To display the score clearly onscreen, we pass the screen’s background color
to
render() as well as a text color.
We’ll position the score in the upper-right corner of the screen and
have it expand to the left as the score increases and the width of the num -
ber grows. To make sure the score always lines up with the right side of the
screen, we create a
rect called score_rect w and set its right edge 20 pixels
from the right screen edge x . We then place the top edge 20 pixels
down from the top of the screen y .
Finally, we create a
show_score() method to display the rendered score
image:
scoreboard.py def show_score(self):
"""Draw score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
This method draws the score image to the screen at the location speci -
fied by
score_rect .

Scoring 303
Making a Scoreboard
To display the score, we’ll create a Scoreboard instance in alien_invasion.py:
alien_ --snip--
invasion.py from game_stats import GameStats
from scoreboard import Scoreboard
--snip--
def run_game():
--snip--
# Create an instance to store game statistics and create a scoreboar
d.
stats = GameStats(ai_settings)
u sb = Scoreboard(ai_settings, screen, stats) --snip--
# Start the main loop for the game.
while True:
--snip--
v gf.update_screen(ai_settings, screen, stats, sb, ship, aliens,
bullets, play_button)
run_game()
We import the new Scoreboard class and make an instance called sb after
creating the
stats instance u . We then pass sb to update_screen() so the score
can be drawn to the screen v .
To display the score, modify
update_screen() like this:
game_ def update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets
,
functions.py play_button):
--snip--
# Draw the score information.
sb.show_score()

# Draw the play button if the game is inactive.
if not stats.game_active:
play_button.draw_button()

# Make the most recently drawn screen visible.
pygame.display.flip()
We add sb to the list of parameters that define update_screen() and call
show_score() just before the Play button is drawn.
W hen you run Alien Invasion now, you should see 0 at the top right of
the screen. (For now we just want to make sure that the score appears in the
right place before developing the scoring system further.) Figure 14 -2 shows
the score as it appears before the game starts.

304 Chapter 14
Figure 14-2: The score appears at the top-right corner of the screen.
Now to assign point values to each alien!
Updating the Score as Aliens Are Shot Down
To write a live score to the screen, we update the value of stats.score when-
ever an alien is hit, and then call
prep_score() to update the score image.
But first, let’s determine how many points a player gets each time they shoot
down an alien:
settings.py def initialize_dynamic_settings(self):
--snip--

# Scoring
self.alien_points = 50
We’ll increase the point value of each alien as the game progresses. To
make sure this point value is reset each time a new game starts, we set the
value in
initialize_dynamic_settings() .
Update the score each time an alien is shot down in
check_bullet_alien_
collisions()
:
game_ def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
functions.py aliens, bullets):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True
)

Scoring 305
if collisions:
u stats.score += ai_settings.alien_points sb.prep_score()
--snip--
We update the definition of check_bullet_alien_collisions() to include
the
stats and sb parameters so it can update the score and the scoreboard.
When a bullet hits an alien, Pygame returns a
collisions dictionar y. We
check whether the dictionar y exists, and if it does, the alien’s value is
added to the score u . We then call
prep_score() to create a new image for
the updated score. We need to modify
update_bullets() to make sure the appropriate argu -
ments are passed between functions:
game_ def update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullet
s):
functions.py """Update position of bullets and get rid of old bullets."""
--snip--
check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets)
The definition of update_bullets() needs the additional parameters
stats and sb. The call to check_bullet_alien_collisions() needs to include
the
stats and sb arguments as well.
We also need to modify the call to
update_bullets() in the main
while loop:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, stats, play_button, ship,
aliens, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens,
bullets)
--snip--
The call to update_bullets() needs the stats and sb arguments.
Now when you play Alien Invasion, you should be able to rack up points!
Making Sure to Score All Hits
As currently written, our code could miss some aliens. For example, if two
bullets collide with aliens during the same pass through the loop or if
we make an extra wide bullet to hit multiple aliens, the player will receive
points only for one of the aliens killed. To fix this, let’s refine the way that
alien bullet collisions are detected.

306 Chapter 14
In check_bullet_alien_collisions() , any bullet that collides with an alien
becomes a key in the
collisions dictionar y. The value associated with each
bullet is a list of aliens it has collided with. We loop through the
collisions
dictionar y to make sure we award points for each alien hit:
game_ def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
functions.py aliens, bullets):
--snip--
if collisions:
u for aliens in collisions.values(): stats.score += ai_settings.alien_points * len(aliens)
sb.prep_score()
--snip--
If the collisions dictionar y has been defined, we loop through all
values in the
collisions dictionar y. Remember that each value is a list of
aliens hit by a single bullet. We multiply the value of each alien by the
number of aliens in each list and add this amount to the current score.
To test this, change the width of a bullet to 300 pixels and verify that you
receive points for each alien you hit with your extra wide bullets; then
return the bullet width to normal.
Increasing Point Values
Because the game gets more difficult each time a player reaches a new level,
aliens in later levels should be worth more points. To implement this func -
tionality, we’ll add code to increase the point value when the game’s speed
increases:
settings.py class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
--snip--
# How quickly the game speeds up
self.speedup_scale = 1.1
# How quickly the alien point values increase
u self.score_scale = 1.5
self.initialize_dynamic_settings()

def increase_speed(self):
"""Increase speed settings and alien point values."""
self.ship_speed_factor *= self.speedup_scale
self.bullet_speed_factor *= self.speedup_scale
self.alien_speed_factor *= self.speedup_scale

v self.alien_points = int(self.alien_points * self.score_scale)

We define a rate at which points increase, which we call score_scale u .
A small increase in speed (1.1) makes the game grow challenging quickly,

Scoring 307
but in order to see a notable difference in scoring you need to change the
alien point value by a larger amount (1.5). Now when we increase the speed
of the game, we also increase the point value of each hit v. We use the
int()
function to increase the point value by whole integers. To see the value of each alien, add a
print statement to the method
increase_speed() in Settings :
settings.py def increase_speed(self):
--snip--
self.alien_points = int(self.alien_points * self.score_scale)
print(self.alien_points)
You should see the new point value in the terminal ever y time you
reach a new level.
note Be sure to remove the print statement after verifying that the point value is increas -
ing, or it may affect the performance of your game and distract the player.
Rounding the Score
Most arcade-style shooting games report scores as multiples of 10, so let’s
follow that lead with our scoring. Let’s also format the score to include
comma separators in large numbers. We’ll make this change in
Scoreboard :
scoreboard.py def prep_score(self):
"""Turn the score into a rendered image."""
u rounded_score = int(round(self.stats.score, -1))
v score_str = "{:,}".format(rounded_score) self.score_image = self.font.render(score_str, True, self.text_
color,
self.ai_settings.bg_color)
--snip--
The round() function normally rounds a decimal number to a set num -
ber of decimal places given as the second argument. However, if you pass
a negative number as the second argument,
round() will round the value to
the nearest 10, 100, 1000, and so on. The code at u tells Python to round the
value of
stats.score to the nearest 10 and store it in rounded_score .
note In Python 2.7, round() always returns a decimal value, so we use int() to make sure
the score is reported as an integer. If you’re using Python 3, you can leave out the call
to
int() .
At v , a string formatting directive tells Python to insert commas into
numbers when converting a numerical value to a string—for example, to
output
1,000,000 instead of 1000000 . Now when you run the game, you should
see a neatly formatted, rounded score even when you rack up lots of points,
as shown in Figure 14 -3.

308 Chapter 14
Figure 14-3: Rounded score with comma separators
High Scores
Ever y player wants to beat a game’s high score, so let’s track and report high
scores to give players something to work toward. We’ll store high scores in
GameStats :
game_stats.py def __init__(self, ai_settings):
--snip--
# High score should never be reset.
self.high_score = 0
Because the high score should never be reset, we initialize high_score in
__init__() rather than in reset_stats() .
Now we’ll modify
the
__init__() method:
scoreboard.py def __init__(self, ai_settings, screen, stats):
--snip--
# Prepare the initial score images.
self.prep_score()
u self.prep_high_score()
The high score will be displayed separately from the score, so we need a
new method,
prep_high_score() , to prepare the high score image u .
Here’s the
prep_high_score() method:
scoreboard.py def prep_high_score(self):
"""Turn the high score into a rendered image."""
u high_score = int(round(self.stats.high_score, -1))

Scoring 309
v high_score_str = "{:,}".format(high_score)
w self.high_score_image = self.font.render(high_score_str, True, self.text_color, self.ai_settings.bg_color)

# Center the high score at the top of the screen.
self.high_score_rect = self.high_score_image.get_rect()
x self.high_score_rect.centerx = self.screen_rect.centerx
y self.high_score_rect.top = self.score_rect.top
We round the high score to the nearest 10 u and format it with
commas v . We then generate an image from the high score w , center the
high score
rect horizontally x , and set its top attribute to match the top of
the score image y .
The
show_score() method now draws the current score at the top right
and the high score at the top center of the screen:
scoreboard.py def show_score(self):
"""Draw the score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
To check for high scores, we’ll write a new function, check_high_score() ,
in game_functions.py :
game_ def check_high_score(stats, sb):
functions.py """Check to see if there's a new high score."""
u if stats.score > stats.high_score: stats.high_score = stats.score
sb.prep_high_score()
The function check_high_score() takes two parameters, stats and sb. It
uses
stats to check the current score and the high score, and it needs sb to
modify the high score image when necessar y. At u we check the current
score against the high score. If the current score is greater, we update the
value of
high_score and call prep_high_score() to update the image of the
high score. We need to call
check_high_score() each time an alien is hit after updat -
ing the score in
check_bullet_alien_collisions() :
game_ def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
functions.py aliens, bullets):
--snip--
if collisions:
for aliens in collisions.values():
stats.score += ai_settings.alien_points * len(aliens)
sb.prep_score()
check_high_score(stats, sb)
--snip--
We call check_high_score() when the collisions dictionar y is present, and
we do so after updating the score for all the aliens that have been hit.

310 Chapter 14
The first time you play Alien Invasion your score will be the high
score, so it will be displayed as both the current and high score. But when
you start a second game, your high score should appear in the middle and
your current score at the right, as shown in Figure 14 - 4.
Figure 14- 4: The high score is shown at the top center of the screen.
Displaying the Level
To display the player’s level in the game, we first need an attribute in
GameStats representing the current level. To reset the level at the start of
each new game, initialize it in
reset_stats() :
game_stats.py def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
self.level = 1
To have Scoreboard display the current level ( just below the current
score), we call a new method,
prep_level() , from __init__() :
scoreboard.py def __init__(self, ai_settings, screen, stats):
--snip--
# Prepare the initial score images.
self.prep_score()
self.prep_high_score()
self.prep_level()

Scoring 311
Here’s prep_level() :
scoreboard.py def prep_level(self):
"""Turn the level into a rendered image."""
u self.level_image = self.font.render(str(self.stats.level), T
rue, self.text_color, self.ai_settings.bg_color)

# Position the level below the score.
self.level_rect = self.level_image.get_rect()
v self.level_rect.right = self.score_rect.right
w self.level_rect.top = self.score_rect.bottom + 10
The method prep_level() creates an image from the value stored in
stats.level u and sets the image’s right attribute to match the score’s right
attribute v . It then sets the
top attribute 10 pixels beneath the bottom of
the score image to leave space between the score and the level w .
We also need to update
show_score() :
scoreboard.py def show_score(self):
"""Draw scores and ships to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
This adds a line to draw the level image to the screen.
We’ll increment
stats.level and update the level image in check_bullet_
alien_collisions()
:
game_ def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
functions.py aliens, bullets):
--snip--
if len(aliens) == 0:
# If the entire fleet is destroyed, start a new level.
bullets.empty()
ai_settings.increase_speed()

# Increase level.
u stats.level += 1
v sb.prep_level()
create_fleet(ai_settings, screen, ship, aliens)
If a fleet is destroyed, we increment the value of stats.level u and call
prep_level() to make sure the new level is displayed correctly v .
To make sure the scoring and level images are updated properly at the
start of a new game, trigger a reset when the Play button is clicked:
game_ def check_play_button(ai_settings, screen, stats, sb, play_button, ship
,
functions.py aliens, bullets, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:

312 Chapter 14
--snip--
# Reset the game statistics.
stats.reset_stats()
stats.game_active = True

# Reset the scoreboard images.
u sb.prep_score() sb.prep_high_score()
sb.prep_level()

# Empty the list of aliens and bullets.
aliens.empty()
bullets.empty()

--snip--
The definition of check_play_button() needs the sb object. To reset the
scoreboard images, we call
prep_score() , prep_high_score() , and prep_level()
after resetting the relevant game settings u .
Now pass
scoreboard object:
game_ def check_events(ai_settings, screen, stats, sb, play_button, ship, ali
ens,
functions.py bullets):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip--
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
u check_play_button(ai_settings, screen, stats, sb, play_but
ton, ship, aliens, bullets, mouse_x, mouse_y)
The definition of check_events() needs sb as a parameter, so the call to
check_play_button() can include sb as an argument u .
Finally, update the call to
check_events() in alien_invasion.py so it passes
sb as well:
alien_ # Start the main loop for the game.
invasion.py while True:
gf.check_events(ai_settings, screen, stats, sb, play_button, ship,
aliens, bullets)
--snip--
Now you can see how many levels you’ve completed, as shown in
Figure 14 -5.

Scoring 313
Figure 14-5: The current level is reported just below the current score.
note In some classic games, the scores have labels, such as Score, High Score, and Level.
We’ve omitted these labels because the meaning of each number becomes clear once
you’ve played the game. To include these labels, add them to the score strings just
before the calls to
font.render() in Scoreboard .
Displaying the Number of Ships
Finally, let’s display the number of ships the player has left, but this time, let’s
use a graphic. To do so, we’ll draw ships in the upper-left corner of the screen
to represent how many ships are left, like many classic arcade games do.
First, we need to make
Ship inherit from Sprite so we can create a group
of ships :
ship.py import pygame
from pygame.sprite import Sprite
u class Ship(Sprite):
def __init__(self, ai_settings, screen):
"""Initialize the ship and set its starting position."""
v super(Ship, self).__init__() --snip--
Here we import Sprite , make sure Ship inherits from Sprite u, and call
super() at the beginning of __init__() v.

314 Chapter 14
Next, we need to modify Scoreboard to create a group of ships we can
display. Here’s the
import statements and __init__() :
scoreboard.py import pygame.font
from pygame.sprite import Group
from ship import Ship
class Scoreboard():
"""A class to report scoring information."""
def __init__(self, ai_settings, screen, stats):
--snip--
self.prep_level()
self.prep_ships()
--snip--
Because we’re making a group of ships, we import the Group and Ship
classes. We call
prep_ships() after the call to prep_level() .
Here’s
prep_ships() :
scoreboard.py def prep_ships(self):
"""Show how many ships are left."""
u self.ships = Group()
v for ship_number in range(self.stats.ships_left): ship = Ship(self.ai_settings, self.screen)
w ship.rect.x = 10 + ship_number * ship.rect.width
x ship.rect.y = 10
The prep_ships() method creates an empty group, self.ships , to
hold the ship instances u . To fill this group, a loop runs once for ever y
ship the player has left v . Inside the loop we create a new ship and set
each ship’s x-coordinate value so the ships appear next to each other
with a 10 -pixel margin on the left side of the group of ships w . We set the
y-coordinate value 10 pixels down from the top of the screen so the ships
line up with the score image x . Finally, we add each new ship to the group
ships y .
Now we need to draw the ships to the screen:
scoreboard.py def show_score(self):
--snip--
self.screen.blit(self.level_image, self.level_rect)
# Draw ships.
self.ships.draw(self.screen)
To display the ships on the screen, we call draw() on the group, and
Pygame draws each ship.

Scoring 315
To show the player how many ships they have to start with, we call
prep_ships() when a new game starts. We do this in check_play_button() in
game_functions.py :
game_ def check_play_button(ai_settings, screen, stats, sb, play_button, ship
,
functions.py aliens, bullets, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:
--snip--
# Reset the scoreboard images.
sb.prep_score()
sb.prep_high_score()
sb.prep_level()
sb.prep_ships()
--snip--
We also call prep_ships() when a ship is hit to update the display of ship
images when the player loses a ship:
game_   u def update_aliens(ai_settings, screen, stats, sb, ship, aliens, bullet
s):
functions.py --snip--
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(ship, aliens):
v ship_hit(ai_settings, screen, stats, sb, ship, aliens, bullets
)
# Look for aliens hitting the bottom of the screen.
w check_aliens_bottom(ai_settings, screen, stats, sb, ship, aliens,
bullets)
x def ship_hit(ai_settings, screen, stats, sb, ship, aliens, bullets): """Respond to ship being hit by alien."""
if stats.ships_left > 0:
# Decrement ships_left.
stats.ships_left -= 1

# Update scoreboard.
y sb.prep_ships()
# Empty the list of aliens and bullets.
--snip--
We first add the parameter sb to the definition of update_aliens() u. We
then pass
the scoreboard object w .
Then we update the definition of
ship_hit() to include sb x . We call
prep_ships() after decreasing the value of ships_left y , so the correct num -
ber of ships is displayed each time a ship is destroyed.

316 Chapter 14
There’s a call to ship_hit() in check_aliens_bottom() , so update that func-
tion as well:
game_ def check_aliens_bottom(ai_settings, screen, stats, sb, ship, aliens,
functions.py bullets):
"""Check if any aliens have reached the bottom of the screen."""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# Treat this the same as if a ship got hit.
ship_hit(ai_settings, screen, stats, sb, ship, aliens, bull
ets)
break
Now check_aliens_bottom() accepts sb as a parameter, and we add an sb
argument in the call to
ship_hit() .
Finally, pass
sb in the call to update_aliens() in alien_invasion.py :
alien_ # Start the main loop for the game.
invasion.py while True:
--snip--
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens,
bullets)
gf.update_aliens(ai_settings, screen, stats, sb, ship, aliens,
bullets)
--snip--
Figure 14 - 6 shows the complete scoring system with the remaining ships
displayed at the top left of the screen.
Figure 14- 6: The complete scoring system for Alien Invasion

Scoring 317
try It y ourself
14-4. All-Time High Score: The high score is reset every time a player closes
and restarts Alien Invasion . Fix this by writing the high score to a file before
calling
sys.exit() and reading the high score in when initializing its value in
GameStats .
14 -5. Refactoring: Look for functions and methods that are doing more
than one task, and refactor them to keep your code organized and efficient .
For example, move some of the code in
check_bullet_alien_collisions() ,
which starts a new level when the fleet of aliens has been destroyed,
to a function called
start_new_level() . Also, move the four separate
method calls in the
__init__() method in Scoreboard to a method called
prep_images() to shorten __init__() . The prep_images() method could also
help
check_play_button() or start_game() if you’ve already refactored
check_play_button() .
n o t e Before attempting to refactor the project, see Appendix D to learn
how to restore the project to a working state if you introduce bugs
while refactoring.
14 - 6 . Expanding Alien Invasion: Think of a way to expand Alien Invasion . For
example, you could program the aliens to shoot bullets down at the ship or
add shields for your ship to hide behind, which can be destroyed by bullets
from either side . Or use something like the
effects like explosions and shooting sounds .
summary
In this chapter you learned to build a Play button to start a new game
and how to detect mouse events and hide the cursor in active games. You
can use what you’ve learned to create other buttons in your games, like a
Help button to display instructions on how to play. You also learned how
to modify the speed of a game as it progresses, how to implement a pro -
gressive scoring system, and how to display information in textual and
nontextual ways.

PrOjEC t 2
Data V Isual Iz at Ion

15
gener at Ing Data
Data visualization involves exploring data
through visual representations. It’s closely
associated with data mining , which uses code
to explore the patterns and connections in a
data set. A data set can be just a small list of numbers
that fits in one line of code or many gigabytes of data.
Making beautiful representations of data is about more than pretty
pictures. When you have a simple, visually appealing representation of a
data set, its meaning becomes clear to viewers. People will see patterns and
significance in your data sets that they never knew existed. Fortunately, you don’t need a supercomputer to visualize complex data.
With Python’s efficiency, you can quickly explore data sets made of millions
of individual data points on just a laptop. The data points don’t have to be
numbers, either. With the basics you learned in the first part of this book,
you can analyze nonnumerical data as well. People use Python for data-intensive work in genetics, climate research,
political and economic analysis, and much more. Data scientists have writ -
ten an impressive array of visualization and analysis tools in Python, many

322 Chapter 15
of which are available to you as well. One of the most popular tools is
matplotlib, a mathematical plotting librar y. We’ll use matplotlib to make
simple plots, such as line graphs and scatter plots. A fter which, we’ll create
a more interesting data set based on the concept of a random walk—a visu-
alization generated from a series of random decisions. We’ll also use a package called Pygal , which focuses on creating visual-
izations that work well on digital devices. You can use Pygal to emphasize
and resize elements as the user interacts with your visualization, and you
can easily resize the entire representation to fit on a tiny smartwatch or
giant monitor. We’ll use Pygal to explore what happens when you roll dice
in various ways.
Installing matplotlib
First, you’ll need to install matplotlib, which we’ll use for our initial set of
visualizations. If you haven’t used pip yet, see “Installing Python Packages
with pip” on page 237.
On Linux
If you’re using the version of Python that came with your system, you can
use your system’s package manager to install matplotlib using just one line:
$sudo apt-get install python3-matplotlib If you’re running Python 2.7, use this line:$ sudo apt-get install python-matplotlib
If you installed a newer version of Python, you’ll have to install a few
libraries that matplotlib depends on:
$sudo apt-get install python3.5-dev python3.5-tk tk-dev$ sudo apt-get install libfreetype6-dev g++
Then use pip to install matplotlib:
$pip install --user matplotlib On OS X Apple includes matplotlib with its standard Python installation. To check whether it’s installed on your system, open a terminal session and tr y import matplotlib . If matplotlib isn’t already on your system and you used Homebrew to install Python, install it like this:$ pip install --user matplotlib

Generating Data 323
note You might need to use pip3 instead of pip when installing packages. Also, if this
command doesn’t work, you might need to leave off the
--user flag.
On Windows
On Windows, you’ll first need to install Visual Studio. Go to h t t p s ://d e v
. windows .com/, click Dow nloads , and look for Visual Studio Community,
which is a free set of developer tools for Windows. Download and run the
i n st a l ler. Next you’ll need an installer for matplotlib. Go to https://pypi.python.org/
pypi/matplotlib/ and look for a wheel file (a file ending in .whl ) that matches
the version of Python you’re using. For example, if you’re using a 32-bit ver -
whl .
note If you don’t see a file matching your installed version of Python, look at what’s avail -
able at http ://w w w.l fd.uci.edu/~gohlke/py t honlibs/# matplot lib . This site
tends to release installers a little earlier than the official matplotlib site.
Copy the . whl file to your project folder, open a command window, and
navigate to the project folder. Then use pip to install matplotlib:
> cd python_work
python_work> python -m pip install --user matplotlib-1.4.3-cp35-none-win32.whl
Testing matplotlib
A fter you’ve installed the necessar y packages, test your installation by start -
ing a terminal session with the
python or python3 command and importing
matplotlib:
$python3 >>> import matplotlib >>> If you don’t see any error messages, then matplotlib is installed on your system, and you can move on to the next section. note If you have trouble with your installation, see Appendix C. If all else fails, ask for help. Your issue will most likely be one that an experienced Python programmer can troubleshoot quickly with a little information from you. The matplotlib Gallery To see the kinds of visualizations you can make with matplotlib, visit the sample galler y at http://matplotlib.org/ . When you click a visualization in the galler y, you can see the code used to generate the plot. 324 Chapter 15 Plotting a simple line graph Let’s plot a simple line graph using matplotlib, and then customize it to create a more informative visualization of our data. We’ll use the square number sequence 1, 4, 9, 16, 25 as the data for the graph. Just provide matplotlib with the numbers as shown here, and matplotlib should do the rest: mpl_squares.py import matplotlib.pyplot as plt squares = [1, 4, 9, 16, 25] plt.plot(squares) plt.show() We first import the pyplot module using the alias plt so we don’t have to type pyplot repeatedly. (You’ll see this convention often in online examples, so we’ll do the same here.) pyplot contains a number of functions that help generate charts and plots. We create a list to hold the squares and then pass it to the plot() func - tion, which will tr y to plot the numbers in a meaningful way. plt.show() opens matplotlib’s viewer and displays the plot, as shown in Figure 15 -1. The viewer allows you to zoom and navigate the plot, and if you click the disk icon, you can save any plot images you like. Figure 15-1: One of the simplest plots you can make in matplotlib Changing the Label Type and Graph Thickness Although the plot shown in Figure 15 -1 shows that the numbers are increas - ing, the label type is too small and the line is too thin. Fortunately, matplotlib allows you to adjust ever y feature of a visualization. Generating Data 325 We’ll use a few of the available customizations to improve the readabil- ity of this plot, as shown here: mpl_squares.py import matplotlib.pyplot as plt squares = [1, 4, 9, 16, 25] u plt.plot(squares, linewidth=5) # Set chart title and label axes. v plt.title("Square Numbers", fontsize=24) w plt.xlabel("Value", fontsize=14) plt.ylabel("Square of Value", fontsize=14) # Set size of tick labels. x plt.tick_params(axis='both', labelsize=14) plt.show() The linewidth parameter at u controls the thickness of the line that plot() generates. The title() function at v sets a title for the chart. The fontsize parameters, which appear repeatedly throughout the code, control the size of the text on the chart. The xlabel() and ylabel() functions allow you to set a title for each of the axes w, and the function tick_params() styles the tick marks x. The arguments shown here affect the tick marks on both the x- and y-axes ( axes='both' ) and set the font size of the tick mark labels to 14 ( labelsize=14 ). As you can see in Figure 15 -2, the resulting chart is much easier to read. The label type is bigger, and the line graph is thicker. Figure 15-2: The chart is much easier to read now. 326 Chapter 15 Correcting the Plot But now that we can read the chart better, we see that the data is not plotted correctly. Notice at the end of the graph that the square of 4.0 is shown as 25! Let’s fix that. W hen you give plot() a sequence of numbers, it assumes the first data point corresponds to an x-coordinate value of 0, but our first point corre - sponds to an x-value of 1. We can override the default behavior by giving plot() both the input and output values used to calculate the squares: mpl_squares.py import matplotlib.pyplot as plt input_values = [1, 2, 3, 4, 5] squares = [1, 4, 9, 16, 25] plt.plot(input_values, squares, linewidth=5) # Set chart title and label axes. --snip-- Now plot() will graph the data correctly because we’ve provided both the input and output values, so it doesn’t have to assume how the output numbers were generated. The resulting plot, shown in Figure 15 -3, is correct. Figure 15-3: The data is now plotted correctly. You can specify numerous arguments when using plot() and use a number of functions to customize your plots. We’ll continue to explore these customization functions as we work with more interesting data sets throughout this chapter. Plotting and Styling Individual Points with scatter() Sometimes it’s useful to be able to plot and style individual points based on certain characteristics. For example, you might plot small values in one Generating Data 327 color and larger values in a different color. You could also plot a large data set with one set of styling options and then emphasize individual points by replotting them with different options.To plot a single point, use the scatter() function. Pass the single ( x, y ) values of the point of interest to scatter() , and it should plot those values: scatter_ import matplotlib.pyplot as plt squares.py plt.scatter(2, 4) plt.show() Let’s style the output to make it more interesting. We’ll add a title, label the axes, and make sure all the text is large enough to read: import matplotlib.pyplot as plt u plt.scatter(2, 4, s=200) # Set chart title and label axes. plt.title("Square Numbers", fontsize=24) plt.xlabel("Value", fontsize=14) plt.ylabel("Square of Value", fontsize=14) # Set size of tick labels. plt.tick_params(axis='both', which='major', labelsize=14) plt.show() At u we call scatter() and use the s argument to set the size of the dots used to draw the graph. When you run scatter_squares.py now, you should see a single point in the middle of the chart, as shown in Figure 15 - 4. Figure 15- 4: Plotting a single point 328 Chapter 15 Plotting a Series of Points with scatter() To plot a series of points, we can pass scatter() separate lists of x- and y-values, like this: scatter_ import matplotlib.pyplot as plt squares.py x_values = [1, 2, 3, 4, 5] y_values = [1, 4, 9, 16, 25] plt.scatter(x_values, y_values, s=100) # Set chart title and label axes. --snip-- The x_values list contains the numbers to be squared, and y_values con- tains the square of each number. When these lists are passed to scatter() , matplotlib reads one value from each list as it plots each point. The points to be plotted are (1, 1), (2, 4), (3, 9), (4, 16), and (5, 25); the result is shown in Figure 15 -5. Figure 15-5: A scatter plot with multiple points Calculating Data Automatically Writing out lists by hand can be inefficient, especially when we have many points. Rather than passing our points in a list, let’s use a loop in Python to do the calculations for us. Here’s how this would look with 1000 points: scatter_ import matplotlib.pyplot as plt squares.py u x_values = list(range(1, 1001))y_values = [x**2 for x in x_values] v plt.scatter(x_values, y_values, s=40) Generating Data 329 # Set chart title and label axes. --snip-- # Set the range for each axis. w plt.axis([0, 1100, 0, 1100000]) plt.show() We start with a list of x-values containing the numbers 1 through 1000 u . Next, a list comprehension generates the y-values by looping through the x-values ( for x in x_values ), squaring each number ( x**2 ), and storing the results in y_values . We then pass the input and output lists to scatter() v . Because this is a large data set, we use a smaller point size and we use the axis() function to specify the range of each axis w . The axis() function requires four values: the minimum and maximum values for the x-axis and the y-axis. Here, we run the x-axis from 0 to 1100 and the y-axis from 0 to 1,100,000. Figure 15 - 6 shows the result. Figure 15- 6: Python can plot 1000 points as easily as it plots 5 points. Removing Outlines from Data Points matplotlib lets you color points individually in a scatter plot. The default— blue dots with a black outline—works well for plots with a few points. But when plotting many points, the black outlines can blend together. To remove the outlines around points, pass the argument edgecolor='none' when you call scatter() : plt.scatter(x_values, y_values, edgecolor='none', s=40) Run scatter_squares.py using this call, and you should see only solid blue points in your plot. 330 Chapter 15 Defining Custom Colors To change the color of the points, pass c to scatter() with the name of a color to use, as shown here: plt.scatter(x_values, y_values, c='red', edgecolor='none', s=40) You can also define custom colors using the RGB color model. To define a color, pass the c argument a tuple with three decimal values (one each for red, green, and blue), using values between 0 and 1. For example, the following line would create a plot with light blue dots: plt.scatter(x_values, y_values, c=(0, 0, 0.8), edgecolor='none', s=40 ) Values closer to 0 produce dark colors, and values closer to 1 produce lighter colors. Using a Colormap A colormap is a series of colors in a gradient that moves from a starting to ending color. Colormaps are used in visualizations to emphasize a pattern in the data. For example, you might make low values a light color and high values a darker color. The pyplot module includes a set of built-in colormaps. To use one of these colormaps, you need to specify how pyplot should assign a color to each point in the data set. Here’s how to assign each point a color based on it s y-value: scatter_ import matplotlib.pyplot as plt squares.py x_values = list(range(1001)) y_values = [x**2 for x in x_values] plt.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues, edgecolor='none', s=40) # Set chart title and label axes. --snip-- We pass the list of y-values to c and then tell pyplot which colormap to use through the cmap argument. This code colors the points with lower y-values light blue and the points with larger y-values dark blue. The result - ing plot is shown in Figure 15 -7. note You can see all the colormaps available in pyplot at http://matplotlib.org/ ; go to Examples , scroll down to Color Examples, and click colormaps_reference . Generating Data 331 Figure 15-7: A plot using the Blues colormap Saving Your Plots Automatically If you want your program to automatically save the plot to a file, you can replace the call to plt.show() with a call to plt.savefig() : plt.savefig('squares_plot.png', bbox_inches='tight') The first argument is a filename for the plot image, which will be saved in the same director y as scatter_squares.py . The second argument trims extra whitespace from the plot. If you want the extra whitespace around the plot, you can omit this argument. t ry It y ourself 15 -1. Cubes: A number raised to the third power is a cube . Plot the first five cubic numbers, and then plot the first 5000 cubic numbers . 15 -2 . Colored Cubes: Apply a colormap to your cubes plot . random w alks In this section we’ll use Python to generate data for a random walk and then use matplotlib to create a visually appealing representation of the generated data. A random walk is a path that has no clear direction but is 332 Chapter 15 determined by a series of random decisions, each of which is left entirely to chance. You might imagine a random walk as the path an ant would take if it had lost its mind and took ever y step in a random direction.Random walks have practical applications in nature, physics, biolog y, chemistr y, and economics. For example, a pollen grain floating on a drop of water moves across the surface of the water because it is constantly being pushed around by water molecules. Molecular motion in a water drop is random, so the path a pollen grain traces out on the surface is a random walk. The code we’re about to write models many real-world situations. Creating the RandomWalk() Class To create a random walk, we’ll create a RandomWalk class, which will make random decisions about which direction the walk should take. The class needs three attributes: one variable to store the number of points in the walk and two lists to store the x- and y-coordinate values of each point in the walk. We’ll use only two methods for the RandomWalk class: the __init__() method and fill_walk() , which will calculate the points in the walk. Let’s start with __init__() as shown here: random_ u from random import choice walk.py class RandomWalk(): """A class to generate random walks.""" v def __init__(self, num_points=5000): """Initialize attributes of a walk.""" self.num_points = num_points # All walks start at (0, 0). w self.x_values = [0] self.y_values = [0] To make random decisions, we’ll store possible choices in a list and use choice() to decide which choice to use each time a decision is made u . We then set the default number of points in a walk to 5000 —large enough to generate some interesting patterns but small enough to generate walks quickly v . Then at w we make two lists to hold the x- and y-values, and we start each walk at point (0, 0). Choosing Directions We’ll use fill_walk() , as shown here, to fill our walk with points and deter - mine the direction of each step. Add this method to random _walk.py : random_ def fill_walk(self): walk.py """Calculate all the points in the walk.""" # Keep taking steps until the walk reaches the desired length. u while len(self.x_values) < self.num_points: Generating Data 333 # Decide which direction to go and how far to go in that direction. v x_direction = choice([1, -1]) x_distance = choice([0, 1, 2, 3, 4]) w x_step = x_direction * x_distance y_direction = choice([1, -1]) y_distance = choice([0, 1, 2, 3, 4]) x y_step = y_direction * y_distance # Reject moves that go nowhere. y if x_step == 0 and y_step == 0: continue # Calculate the next x and y values. z next_x = self.x_values[-1] + x_step next_y = self.y_values[-1] + y_step self.x_values.append(next_x) self.y_values.append(next_y) At u we set up a loop that runs until the walk is filled with the correct number of points. The main part of this method tells Python how to simu - late four random decisions: Will the walk go right or left? How far will it go in that direction? Will it go up or down? How far will it go in that direction? We use choice([1, -1]) to choose a value for x_direction , which returns either 1 for right movement or −1 for left v . Next, choice([0, 1, 2, 3, 4]) tells Python how far to move in that direction ( x_distance ) by randomly selecting an integer between 0 and 4. (The inclusion of a 0 allows us to take steps along the y-axis as well as steps that have movement along both axes.) At w and x we determine the length of each step in the x and y direc - tions by multiplying the direction of movement by the distance chosen. A positive result for x_step moves us right, a negative result moves us left, and 0 moves us vertically. A positive result for y_step means move up, negative means move down, and 0 means move horizontally. If the value of both x_step and y_step are 0, the walk stops, but we continue the loop to prevent this y . To get the next x-value for our walk, we add the value in x_step to the last value stored in x_values z and do the same for the y-values. Once we have these values, we append them to x_values and y_values . Plotting the Random Walk Here’s the code to plot all the points in the walk: rw_visual.py import matplotlib.pyplot as plt from random_walk import RandomWalk # Make a random walk, and plot the points. u rw = RandomWalk() rw.fill_walk() 334 Chapter 15 v plt.scatter(rw.x_values, rw.y_values, s=15)plt.show() We begin by importing pyplot and RandomWalk . We then create a ran - dom walk and store it in rw u , making sure to call fill_walk() . At v we feed the walk’s x- and y-values to scatter() and choose an appropriate dot size. Figure 15 - 8 shows the resulting plot with 5000 points. (The images in this section omit matplotlib’s viewer, but you’ll continue to see it when you run rw_visual.py .) Figure 15-8: A random walk with 5000 points Generating Multiple Random Walks Ever y random walk is different, and it’s fun to explore the various patterns that can be generated. One way to use the preceding code to make multiple walks without having to run the program several times is to wrap it in a while loop, like this: rw_visual.py import matplotlib.pyplot as plt from random_walk import RandomWalk # Keep making new walks, as long as the program is active. while True: # Make a random walk, and plot the points. rw = RandomWalk() rw.fill_walk() plt.scatter(rw.x_values, rw.y_values, s=15) plt.show() u keep_running = input("Make another walk? (y/n): ") if keep_running == 'n': break Generating Data 335 This code will generate a random walk, display it in matplotlib’s viewer, and pause with the viewer open. When you close the viewer, you’ll be asked whether you want to generate another walk. Answer y, and you should be able to generate walks that stay near the starting point, that wander off mostly in one direction, that have thin sections connecting larger groups of points, and so on. When you want to end the program, enter n. note If you’re using Python 2.7, remember to use raw_input() instead of input() at u . Styling the Walk In this section we’ll customize our plots to emphasize the important char - acteristics of each walk and deemphasize distracting elements. To do so, we identify the characteristics we want to emphasize, such as where the walk began, where it ended, and the path taken. Next, we identify the charac - teristics to deemphasize, like tick marks and labels. The result should be a simple visual representation that clearly communicates the path taken in each random walk. Coloring the Points We’ll use a colormap to show the order of the points in the walk and then remove the black outline from each dot so the color of the dots will be clearer. To color the points according to their position in the walk, we pass the c argument a list containing the position of each point. Because the points are plotted in order, the list just contains the numbers from 1 to 5000, as shown here: rw_visual.py --snip-- while True: # Make a random walk, and plot the points. rw = RandomWalk() rw.fill_walk() u point_numbers = list(range(rw.num_points)) plt.scatter(rw.x_values, rw.y_values, c=point_numbers, cmap=plt.cm. Blues, edgecolor='none', s=15) plt.show() keep_running = input("Make another walk? (y/n): ") --snip-- At u we use range() to generate a list of numbers equal to the number of points in the walk. Then we store them in the list point_numbers , which we’ll use to set the color of each point in the walk. We pass point_numbers to the c argument, use the Blues colormap, and then pass edgecolor=none to get rid of the black outline around each point. The result is a plot of the walk that varies from light to dark blue along a gradient, as shown in Figure 15 -9. 336 Chapter 15 Figure 15-9: A random walk colored with the Blues colormap Plotting the Starting and Ending Points In addition to coloring points to show their position along the walk, it would be nice to see where each walk begins and ends. To do so, we can plot the first and last points individually once the main series has been plotted. We’ll make the end points larger and color them differently to make them stand out, as shown here: rw_visual.py --snip-- while True: --snip-- plt.scatter(rw.x_values, rw.y_values, c=point_numbers, cmap=plt.cm. Blues, edgecolor='none', s=15) # Emphasize the first and last points. plt.scatter(0, 0, c='green', edgecolors='none', s=100) plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolors=' none', s=100) plt.show() --snip-- To show the starting point, we plot point (0, 0) in green in a larger size ( s=100 ) than the rest of the points. To mark the end point, we plot the last x- and y-value in the walk in red with a size of 100. Make sure you insert this code just before the call to plt.show() so the starting and ending points are drawn on top of all the other points. When you run this code, you should be able to spot exactly where each walk begins and ends. (If these end points don’t stand out clearly, adjust their color and size until they do.) Generating Data 337 Cleaning Up the Axes Let’s remove the axes in this plot so they don’t distract us from the path of each walk. To turn the axes off, use this code: rw_visual.py --snip-- while True: --snip-- plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolors=' none', s=100) # Remove the axes. u plt.axes().get_xaxis().set_visible(False) plt.axes().get_yaxis().set_visible(False) plt.show() --snip-- To modify the axes, use the plt.axes() function u to set the visibility of each axis to False . As you continue to work with visualizations, you’ll fre - quently see this chaining of methods. Run rw_visual.py now; you should see a series of plots with no axes. Adding Plot Points Let’s increase the number of points to give us more data to work with. To do so, we increase the value of num_points when we make a RandomWalk instance and adjust the size of each dot when drawing the plot, as shown here: rw_visual.py --snip-- while True: # Make a random walk, and plot the points. rw = RandomWalk(50000) rw.fill_walk() # Plot the points, and show the plot. point_numbers = list(range(rw.num_points)) plt.scatter(rw.x_values, rw.y_values, c=point_numbers, cmap=plt.cm. Blues, edgecolor='none', s=1) --snip-- This example creates a random walk with 50,000 points (to mirror real- world data) and plots each point at size s=1 . The resulting walk is wispy and cloud-like, as shown in Figure 15 -10. As you can see, we’ve created a piece of art from a simple scatter plot! Experiment with this code to see how much you can increase the num - ber of points in a walk before your system starts to slow down significantly or the plot loses its visual appeal. 338 Chapter 15 Figure 15-10: A walk with 50,000 points Altering the Size to Fill the Screen A visualization is much more effective at communicating patterns in data if it fits nicely on the screen. To make the plotting window better fit your screen, adjust the size of matplotlib’s output, like this: rw_visual.py --snip-- while True: # Make a random walk, and plot the points. rw = RandomWalk() rw.fill_walk() # Set the size of the plotting window. plt.figure(figsize=(10, 6)) --snip-- The figure() function controls the width, height, resolution, and back - ground color of the plot. The figsize parameter takes a tuple, which tells matplotlib the dimensions of the plotting window in inches. Python assumes that your screen resolution is 80 pixels per inch; if this code doesn’t give you an accurate plot size, adjust the numbers as necessar y. Or, if you know your system’s resolution, pass figure() the resolution using the dpi parameter to set a plot size that makes effective use of the space available on your screen, as shown here: plt.figure(dpi=128, figsize=(10, 6)) Generating Data 339 try It y ourself 15 - 3 . Molecular Motion: Modif y rw_visual.py by replacing plt.scatter() with plt.plot() . To simulate the path of a pollen grain on the surface of a drop of water, pass in the rw.x_values and rw.y_values , and include a linewidth argu - ment . Use 5000 instead of 50,000 points . 15 - 4 . Modified Random Walks: In the class RandomWalk , x_step and y_step are generated from the same set of conditions . The direction is chosen randomly from the list [1, -1] and the distance from the list [0, 1, 2, 3, 4] . Modify the values in these lists to see what happens to the overall shape of your walks . Try a longer list of choices for the distance, such as 0 through 8, or remove the −1 from the x or y direction list . 15 - 5 . Refactoring: The method fill_walk() is lengthy . Create a new method called get_step() to determine the direction and distance for each step, and then calculate the step . You should end up with two calls to get_step() in fill_walk() : x_step = get_step() y_step = get_step() This refactoring should reduce the size of fill_walk() and make the method easier to read and understand . rolling d ice with Pygal In this section we’ll use the Python visualization package Pygal to produce scalable vector graphics files. These are useful in visualizations that are presented on differently sized screens because they scale automatically to fit the viewer’s screen. If you plan to use your visualizations online, consider using Pygal so your work will look good on any device people use to view your v isualizations. In this project we’ll analyze the results of rolling dice. If you roll one regular six-sided die, you have an equal chance of rolling any of the num - bers from 1 through 6. However, when using two dice, you’re more likely to roll certain numbers rather than others. We’ll tr y to determine which num - bers are most likely to occur by generating a data set that represents rolling dice. Then we’ll plot the results of a large number of rolls to determine which results are more likely than others. The study of rolling dice is often used in mathematics to explain various types of data analysis. But it also has real-world applications in casinos and other gambling scenarios, as well as in the way games like Monopoly and many role-playing games are played. 340 Chapter 15 Installing Pygal Install Pygal with pip . (If you haven’t used pip yet, see “Installing Python Packages with pip” on page 237.) On Linux and OS X, this should be something like: pip install --user pygal On Windows, this should be: python -m pip install --user pygal note You may need to use the command pip3 instead of pip , and if the command still doesn’t work you may need to leave off the --user flag. The Pygal Gallery To see what kind of visualizations are possible with Pygal, visit the galler y of chart types: go to http://www.pygal.org/ , click Documentation, and then click Chart ty pes . Each example includes source code, so you can see how the visualizations are generated. Creating the Die Class Here’s a class to simulate the roll of one die: die.py from random import randint class Die(): """A class representing a single die.""" u def __init__(self, num_sides=6): """Assume a six-sided die.""" self.num_sides = num_sides def roll(self): """"Return a random value between 1 and number of sides.""" v return randint(1, self.num_sides) The __init__() method takes one optional argument. With this class, when an instance of our die is created, the number of sides will always be six if no argument is included. If an argument is included, that value is used to set the number of sides on the die u . (Dice are named for their number of sides: a six-sided die is a D6, an eight-sided die is a D8, and so on.) The roll() method uses the randint() function to return a random num - ber between 1 and the number of sides v . This function can return the start- ing value (1), the ending value ( num_sides ), or any integer between the two. Generating Data 3 41 Rolling the Die Before creating a visualization based on this class, let’s roll a D6, print the results, and check that the results look reasonable: die_visual.py from die import Die # Create a D6. u die = Die() # Make some rolls, and store results in a list. results = [] v for roll_num in range(100): result = die.roll() results.append(result) print(results) At u we create an instance of Die with the default six sides. At v we roll the die 100 times and store the results of each roll in the list results . Here’s a sample set of results: [4, 6, 5, 6, 1, 5, 6, 3, 5, 3, 5, 3, 2, 2, 1, 3, 1, 5, 3, 6, 3, 6, 5, 4, 1, 1, 4, 2, 3, 6, 4, 2, 6, 4, 1, 3, 2, 5, 6, 3, 6, 2, 1, 1, 3, 4, 1, 4, 3, 5, 1, 4, 5, 5, 2, 3, 3, 1, 2, 3, 5, 6, 2, 5, 6, 1, 3, 2, 1, 1, 1, 6, 5, 5, 2, 2, 6, 4, 1, 4, 5, 1, 1, 1, 4, 5, 3, 3, 1, 3, 5, 4, 5, 6, 5, 4, 1, 5, 1, 2] A quick scan of these results shows that the Die class seems to be work - ing. We see the values 1 and 6, so we know the smallest and largest possible values are being returned, and because we don’t see 0 or 7, we know all the results are in the appropriate range. We also see each number from 1 through 6, which indicates that all possible outcomes are represented. Analyzing the Results We analyze the results of rolling one D6 by counting how many times we roll each number: die_visual.py --snip-- # Make some rolls, and store results in a list. results = [] u for roll_num in range(1000): result = die.roll() results.append(result) # Analyze the results. frequencies = [] v for value in range(1, die.num_sides+1): w frequency = results.count(value) x frequencies.append(frequency) print(frequencies) 342 Chapter 15 Because we’re using Pygal to analyze instead of print the results, we can increase the number of simulated rolls to 1000 u . To analyze the rolls, we create the empty list frequencies to store the number of times each value is rolled. We loop through the possible values (1 through 6 in this case) at v , count how many times each number appears in results w , and then append this value to the frequencies list x . We then print this list before making a visualization: [155, 167, 168, 170, 159, 181] These results look reasonable: we see six frequencies, one for each pos - sible number when you roll a D6, and we see that no frequency is signifi - cantly higher than any other. Now let’s visualize these results. Making a Histogram With a list of frequencies, we can make a histogram of the results. A histogram is a bar chart showing how often certain results occur. Here’s the code to create the histogram: die_visual.py import pygal --snip-- # Analyze the results. frequencies = [] for value in range(1, die.num_sides+1): frequency = results.count(value) frequencies.append(frequency) # Visualize the results. u hist = pygal.Bar() hist.title = "Results of rolling one D6 1000 times." v hist.x_labels = ['1', '2', '3', '4', '5', '6'] hist.x_title = "Result" hist.y_title = "Frequency of Result" w hist.add('D6', frequencies) hist.render_to_file('die_visual.svg') We make a bar chart by creating an instance of pygal.Bar() , which we store in hist u . We then set the title attribute of hist ( just a string we use to label the histogram), use the possible results of rolling a D6 as the labels for the x-axis v , and add a title for each of the axes. We use add() to add a series of values to the chart at w (passing it a label for the set of values to be added and a list of the values to appear on the chart). Finally, we render the chart to an SVG file, which expects a filename with the .svg extension. The simplest way to look at the resulting histogram is in a web browser. Open a new tab in any web browser and then open the file die_visual.svg (in the folder where you saved die_visual.py ). You should see a chart like the Generating Data 343 one in Figure 15 -11. (I’ve modified this chart slightly for printing; by default Pygal generates charts with a darker background than what you see here.) Figure 15-11: A simple bar chart created with Pygal Notice that Pygal has made the chart interactive: hover your cursor over any bar in the chart and you’ll see the data associated with it. This feature is particularly useful when plotting multiple data sets on the same chart. Rolling Two D i c e Rolling two dice results in larger numbers and a different distribution of results. Let’s modify our code to create two D6 dice to simulate the way we roll a pair of dice. Each time we roll the pair, we’ll add the two numbers (one from each die) and store the sum in results . Save a copy of die_visual.py as dice_visual.py , and make the following changes: dice_visual.py import pygal from die import Die # Create two D6 dice. die_1 = Die() die_2 = Die() # Make some rolls, and store results in a list. results = [] for roll_num in range(1000): u result = die_1.roll() + die_2.roll() results.append(result) # Analyze the results. frequencies = [] v max_result = die_1.num_sides + die_2.num_sides 344 Chapter 15 w for value in range(2, max_result+1): frequency = results.count(value) frequencies.append(frequency) # Visualize the results. hist = pygal.Bar() x hist.title = "Results of rolling two D6 dice 1000 times." hist.x_labels = ['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12 '] hist.x_title = "Result" hist.y_title = "Frequency of Result" hist.add('D6 + D6', frequencies) hist.render_to_file('dice_visual.svg') A fter creating two instances of Die , we roll the dice and calculate the sum of the two dice for each roll u . The largest possible result (12) is the sum of the largest number on both dice, which we store in max_result v . The smallest possible result (2) is the sum of the smallest number on both dice. When we analyze the results, we count the number of results for each value between 2 and max_result w . (We could have used range(2, 13) , but this would work only for two D6 dice. When modeling real-world situations, it’s best to write code that can easily model a variety of situations. This code allows us to simulate rolling a pair of dice with any number of sides.) When creating the chart, we update the title and the labels for the x-axis and data series x . (If the list x_labels were much longer, it would make sense to write a loop to generate this list automatically.) A fter running this code, refresh the tab in your browser showing the chart; you should see a chart like the one in Figure 15 -12. Figure 15-12: Simulated results of rolling two six-sided dice 1000 times Generating Data 345 This graph shows the approximate results you’re likely to get when you roll a pair of D6 dice. As you can see, you’re least likely to roll a 2 or a 12 and most likely to roll a 7 because there are six ways to roll a 7, namely: 1 and 6, 2 and 5, 3 and 4, 4 and 3, 5 and 2, or 6 and 1. Rolling Dice of Different Sizes Let’s create a six-sided die and a ten-sided die, and see what happens when we roll them 50,000 times: different_ from die import Die dice.py import pygal # Create a D6 and a D10. die_1 = Die() u die_2 = Die(10) # Make some rolls, and store results in a list. results = [] for roll_num in range(50000): result = die_1.roll() + die_2.roll() results.append(result) # Analyze the results. --snip-- # Visualize the results. hist = pygal.Bar() v hist.title = "Results of rolling a D6 and a D10 50,000 times." hist.x_labels = ['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12 ', '13', '14', '15', '16'] hist.x_title = "Result" hist.y_title = "Frequency of Result" hist.add('D6 + D10', frequencies) hist.render_to_file('dice_visual.svg') To make a D10, we pass the argument 10 when creating the second Die instance u and change the first loop to simulate 50,000 rolls instead of 1000. The lowest possible result is still 2, but the largest result is now 16 ; so we adjust the title, x-axis labels, and data series labels to reflect that v . Figure 15 -13 shows the resulting chart. Instead of one most likely result, there are five. This happens because there’s still only one way to roll the smallest value (1 and 1) and the largest value (6 and 10), but the smaller die limits the number of ways you can generate the middle numbers: there are six ways to roll a 7, 8, 9, 10, and 11. Therefore, these are the most common results, and you’re equally likely to roll any one of these numbers. 346 Chapter 15 Figure 15-13: The results of rolling a six-sided die and a ten-sided die 50,000 times Our ability to use Pygal to model the rolling of dice gives us consider- able freedom in exploring this phenomenon. In just minutes you can simu - late a tremendous number of rolls using a large variety of dice. t ry It y ourself 15 - 6 . Automatic Labels: Modif y die.py and dice_visual.py by replacing the list we used to set the value of hist.x_labels with a loop to generate this list auto - matically . If you’re comfortable with list comprehensions, try replacing the other for loops in die_visual.py and dice_visual.py with comprehensions as well . 1 5 - 7. Two D8s: Create a simulation showing what happens if you roll two eight- sided dice 1000 times . Increase the number of rolls gradually until you start to see the limits of your system’s capabilities . 15 - 8 . Three Dice: If you roll three D6 dice, the smallest number you can roll is 3 and the largest number is 18 . Create a visualization that shows what happens when you roll three D6 dice . 15 - 9. Multiplication: When you roll two dice, you usually add the two numbers together to get the result . Create a visualization that shows what happens if you multiply these numbers instead . 15 -10 . Practicing with Both Libraries: Try using matplotlib to make a die-rolling visualization, and use Pygal to make the visualization for a random walk . Generating Data 347 summary In this chapter you learned to generate data sets and create visualizations of that data. You learned to create simple plots with matplotlib, and you saw how to use a scatter plot to explore random walks. You learned to create a histogram with Pygal and how to use a histogram to explore the results of rolling dice of different sizes. Generating your own data sets with code is an interesting and power - ful way to model and explore a wide variety of real-world situations. As you continue to work through the data visualization projects that follow, keep an eye out for situations you might be able to model with code. Look at the visualizations you see in news media, and see if you can identify those that were generated using methods similar to the ones you’re learning in these projects. In Chapter 16 we’ll download data from online sources and continue to use matplotlib and Pygal to explore that data. 16 DownloaDIng Data In this chapter you’ll download data sets from online sources and create working visualizations of that data. An incredible variety of data can be found online, much of which hasn’t been examined thoroughly. The ability to analyze this data allows you to discover patterns and connections that no one else has found. We’ll access and visualize data stored in two common data formats, CSV and JSON. We’ll use Python’s csv module to process weather data stored in the CSV (comma-separated values) format and analyze high and low temperatures over time in two different locations. We’ll then use matplotlib to generate a chart based on our downloaded data to display 350 Chapter 16 variations in temperature in two ver y different environments: Sitka, Alaska, and Death Valley, California. Later in the chapter, we’ll use the json module to access population data stored in the JSON format and use Pygal to draw a population map by countr y. By the end of this chapter, you’ll be prepared to work with different types and formats of data sets, and you’ll have a deeper understanding of how to build complex visualizations. The ability to access and visualize online data of different types and formats is essential to working with a wide variety of real-world data sets. t he C sV f ile f ormat One simple way to store data in a text file is to write the data as a series of values separated by commas, called comma-separated values . The resulting files are called CSV files. For example, here’s one line of weather data in CSV format: 2014-1-5,61,44,26,18,7,-1,56,30,9,30.34,30.27,30.15,,,,10,4,,0.00,0,,195 This is weather data for Januar y 5, 2014 in Sitka, Alaska. It includes the day’s high and low temperatures, as well as a number of other measure - ments from that day. CSV files can be tricky for humans to read, but they’re easy for programs to process and extract values from, which speeds up the data analysis process. We’ll begin with a small set of CSV-formatted weather data recorded in Sitka, which is available in the book’s resources through https://www .nostarch.com/ pythoncrashcourse /. Copy the file sitka_weather_07-2014.csv to the folder where you’re writing this chapter’s programs. (Once you download the book’s resources, you’ll have all the files you need for this project.) note The weather data in this project was originally downloaded from http://www .w underground.com/histor y/ . Parsing the CSV File Headers P y t hon’s csv module in the standard librar y parses the lines in a CSV file and allows us to quickly extract the values we’re interested in. Let’s start by examining the first line of the file, which contains a series of headers for the data: highs_lows.py import csv filename = 'sitka_weather_07-2014.csv' u with open(filename) as f: v reader = csv.reader(f) w header_row = next(reader) print(header_row) Downloading Data 351 A fter importing the csv module, we store the name of the file we’re working with in filename . We then open the file and store the resulting file object in f u . Next, we call csv.reader() and pass it the file object as an argument to create a reader object associated with that file v . We store the reader object in reader . The csv module contains a next() function, which returns the next line in the file when passed the reader object. In the preceding listing we call next() only once so we get the first line of the file, which contains the file headers w . We store the data that’s returned in header_row . As you can see, header_row contains meaningful weather-related headers that tell us what information each line of data holds: ['AKDT', 'Max TemperatureF', 'Mean TemperatureF', 'Min TemperatureF', 'Max Dew PointF', 'MeanDew PointF', 'Min DewpointF', 'Max Humidity', ' Mean Humidity', ' Min Humidity', ' Max Sea Level PressureIn', ' Mean Sea Level PressureIn', ' Min Sea Level PressureIn', ' Max VisibilityMiles', ' Mean VisibilityMiles', ' Min VisibilityMiles', ' Max Wind SpeedMPH', ' Mean Wind SpeedMPH', ' Max Gust SpeedMPH', 'PrecipitationIn', ' CloudCover', ' Events', ' WindDirDegrees'] reader processes the first line of comma-separated values in the file and stores each as an item in a list. The header AKDT represents Alaska Daylight Time. The position of this header tells us that the first value in each line will be the date or time. The Max TemperatureF header tells us that the second value in each line is the maximum temperature for that date, in degrees Fahrenheit. You can read through the rest of the headers to determine the kind of information included in the file. note The headers are not always formatted consistently: spaces and units are in odd places. This is common in raw data files but won’t cause a problem. Printing the Headers and Their Positions To make it easier to understand the file header data, print each header and its position in the list: highs_lows.py --snip-- with open(filename) as f: reader = csv.reader(f) header_row = next(reader) u for index, column_header in enumerate(header_row): print(index, column_header) We use enumerate() u on the list to get the index of each item, as well as the value. (Note that we’ve removed the line print(header_row) in favor of this more detailed version.) 352 Chapter 16 Here’s the output showing the index of each header: 0 AKDT 1 Max TemperatureF 2 Mean TemperatureF 3 Min TemperatureF --snip-- 20 CloudCover 21 Events 22 WindDirDegrees Here we see that the dates and their high temperatures are stored in columns 0 and 1. To explore this data, we’ll process each row of data in sitka_weather_07-2014.csv and extract the values with the indices 0 and 1. Extracting and Reading Data Now that we know which columns of data we need, let’s read in some of that data. First, we’ll read in the high temperature for each day: highs_lows.py import csv # Get high temperatures from file. filename = 'sitka_weather_07-2014.csv' with open(filename) as f: reader = csv.reader(f) header_row = next(reader) u highs = [] v for row in reader: w highs.append(row[1]) print(highs) We make an empty list called highs u and then loop through the remaining rows in the file v . The reader object continues from where it left off in the CSV file and automatically returns each line following its current position. Because we’ve already read the header row, the loop will begin at the second line where the actual data begins. On each pass through the loop, we append the data from index 1, the second column, to highs w . The following listing shows the data now stored in highs : ['64', '71', '64', '59', '69', '62', '61', '55', '57', '61', '57', '59', '57', '61', '64', '61', '59', '63', '60', '57', '69', '63', '62', '59', '57', '57', '61', '59', '61', '61', '66'] We’ve extracted the high temperature for each date and stored them neatly in a list as strings. Downloading Data 353 Next, convert these strings to numbers with int() so they can be read by matplotlib: highs_lows.py --snip-- highs = [] for row in reader: u high = int(row[1]) highs.append(high) print(highs) We convert the strings to integers at u before appending the tempera - tures to the list. The result is a list of daily highs in numerical format: [64, 71, 64, 59, 69, 62, 61, 55, 57, 61, 57, 59, 57, 61, 64, 61, 59, 63, 60, 57, 69, 63, 62, 59, 57, 57, 61, 59, 61, 61, 66] Now let’s create a visualization of this data. Plotting Data in a Temperature Chart To visualize the temperature data we have, we’ll first create a simple plot of the daily highs using matplotlib, as shown here: highs_lows.py import csv from matplotlib import pyplot as plt # Get high temperatures from file. --snip-- # Plot data. fig = plt.figure(dpi=128, figsize=(10, 6)) u plt.plot(highs, c='red') # Format plot. v plt.title("Daily high temperatures, July 2014", fontsize=24) w plt.xlabel('', fontsize=16) plt.ylabel("Temperature (F)", fontsize=16) plt.tick_params(axis='both', which='major', labelsize=16) plt.show() We pass the list of highs to plot() u and pass c='red' to plot the points in red. (We’ll plot the highs in red and the lows in blue.) We then specify a few other formatting details, such as font size and labels v , which you should recognize from Chapter 15. Because we have yet to add the dates, we won’t label the x-axis, but plt.xlabel() does modify the font size to make the default labels more readable w . Figure 16 -1 shows the resulting plot: a simple line graph of the high temperatures for July 2014, in Sitka, Alaska. 354 Chapter 16 Figure 16 -1: A line graph showing daily high temperatures for July 2014 in Sitka, Alaska The datetime Module Let’s add dates to our graph to make it more useful. The first date from the weather data file is in the second row of the file: 2014-7-1,64,56,50,53,51,48,96,83,58,30.19,--snip-- The data will be read in as a string, so we need a way to convert the string '2014-7-1' to an object representing this date. We can construct an object representing July 1, 2014, using the strptime() method from the datetime module. Let’s see how strptime() works in a terminal session: >>> from datetime import datetime >>> first_date = datetime.strptime('2014-7-1', '%Y-%m-%d') >>> print(first_date) 2014-07-01 00:00:00 We first import the datetime class from the datetime module. Then we call the method strptime() with the string containing the date we want to work with as the first argument. The second argument tells Python how the date is formatted. In this example, '%Y-' tells Python to interpret the part of the string before the first dash as a four-digit year; '%m-' tells Python to interpret the part of the string before the second dash as a number repre - senting the month; and '%d' tells Python to interpret the last part of the string as the day of the month, from 1 to 31. The strptime() method can take a variety of arguments to determine how to interpret the date. Table 16 -1 shows some of these arguments. Downloading Data 355 Table 16-1: Date and Time Formatting Arguments from the datetime Module ArgumentMeaning %A Weekday name, such as Monday %B Month name, such as January %m Month, as a number (01 to 12) %d Day of the month, as a number (01 to 31) %Y Four-digit year, such as 2015 %y Two-digit year, such as 15 %H Hour, in 24-hour format (00 to 23) %I Hour, in 12-hour format (01 to 12) %p am or pm %M Minutes (00 to 59) %S Seconds (00 to 61) Plotting Dates K nowing how to process the dates in our CSV file, we can now improve our plot of the temperature data by extracting dates for the daily highs and passing the dates and the highs to plot() , as shown here: highs_lows.py import csv from datetime import datetime from matplotlib import pyplot as plt # Get dates and high temperatures from file. filename = 'sitka_weather_07-2014.csv' with open(filename) as f: reader = csv.reader(f) header_row = next(reader) u dates, highs = [], [] for row in reader: v current_date = datetime.strptime(row[0], "%Y-%m-%d") dates.append(current_date) high = int(row[1]) highs.append(high) # Plot data. fig = plt.figure(dpi=128, figsize=(10, 6)) w plt.plot(dates, highs, c='red') # Format plot. plt.title("Daily high temperatures, July 2014", fontsize=24) plt.xlabel('', fontsize=16) 356 Chapter 16 x fig.autofmt_xdate()plt.ylabel("Temperature (F)", fontsize=16) plt.tick_params(axis='both', which='major', labelsize=16) plt.show() We create two empty lists to store the dates and high temperatures from the file u . We then convert the data containing the date information ( row[0] ) to a datetime object v and append it to dates . We pass the dates and the high temperature values to plot() at w . The call to fig.autofmt_xdate() at x draws the date labels diagonally to prevent them from overlapping. Figure 16 -2 shows the improved graph. Figure 16 -2: The graph is more meaningful now that it has dates on the x-axis. Plotting a Longer Timeframe With our graph set up, let’s add more data to get a more complete picture of the weather in Sitka. Copy the file sitka_weather_2014.csv , which contains a full year’s worth of Weather Underground data for Sitka, to the folder where you’re storing this chapter’s programs. Now we can generate a graph for the entire year’s weather: highs_lows.py --snip-- # Get dates and high temperatures from file. u filename = 'sitka_weather_2014.csv' with open(filename) as f: --snip-- # Format plot. v plt.title("Daily high temperatures - 2014", fontsize=24) plt.xlabel('', fontsize=16) --snip-- Downloading Data 357 We modify the filename to use the new data file sitka_weather_2014.csv u , and we update the title of our plot to reflect the change in its content v . Figure 16 -3 shows the resulting plot. Figure 16 -3: A year’s worth of data Plotting a Second Data Series The reworked graph in Figure 16 -3 displays a substantial amount of mean - ingful data, but we can make it even more useful by including the low tem - peratures. We need to extract the low temperatures from the data file and then add them to our graph, as shown here: highs_lows.py --snip-- # Get dates, high, and low temperatures from file. filename = 'sitka_weather_2014.csv' with open(filename) as f: reader = csv.reader(f) header_row = next(reader) u dates, highs, lows = [], [], [] for row in reader: current_date = datetime.strptime(row[0], "%Y-%m-%d") dates.append(current_date) high = int(row[1]) highs.append(high) v low = int(row[3]) lows.append(low) 358 Chapter 16 # Plot data. fig = plt.figure(dpi=128, figsize=(10, 6)) plt.plot(dates, highs, c='red') w plt.plot(dates, lows, c='blue') # Format plot. x plt.title("Daily high and low temperatures - 2014", fontsize=24) --snip-- At u we add the empty list lows to hold low temperatures, and then we extract and store the low temperature for each date, from the fourth position in each row ( row[3] ) v . At w we add a call to plot() for the low temperatures and color these values blue. Finally, we update the title x . Figure 16 - 4 shows the resulting chart. Figure 16 - 4: Two data series on the same plot Shading an Area in the Chart Having added two data series, we can now examine the range of tempera - tures for each day. Let’s add a finishing touch to the graph by using shading to show the range between each day’s high and low temperatures. To do so, we’ll use the fill_between() method, which takes a series of x-values and two series of y-values, and fills the space between the two y-value series: highs_lows.py --snip-- # Plot data. fig = plt.figure(dpi=128, figsize=(10, 6)) u plt.plot(dates, highs, c='red', alpha=0.5) plt.plot(dates, lows, c='blue', alpha=0.5) v plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1) --snip-- Downloading Data 359 The alpha argument at u controls a color’s transparency. An alpha value of 0 is completely transparent, and 1 (the default) is completely opaque. By setting alpha to 0.5 we make the red and blue plot lines appear lighter. At v we pass fill_between() the list dates for the x-values and then the two y-value series highs and lows . The facecolor argument determines the color of the shaded region, and we give it a low alpha value of 0.1 so the filled region connects the two data series without distracting from the information they represent. Figure 16 -5 shows the plot with the shaded region between the highs and lows. Figure 16 -5: The region between the two data sets is shaded. The shading helps make the range between the two data sets immedi - ately apparent. Error-Checking We should be able to run the code from highs_lows.py using data for any location. But some weather stations occasionally malfunction and fail to collect some or all of the data they’re supposed to. Missing data can result in exceptions that crash our programs if we don’t handle them properly. For example, let’s see what happens when we attempt to generate a tem - perature plot for Death Valley, California. Copy the file death_valley_2014.csv to the folder where you’re storing this chapter’s programs, and then change highs_lows.py to generate a graph for Death Valley: highs_lows.py --snip-- # Get dates, high, and low temperatures from file. filename = 'death_valley_2014.csv' with open(filename) as f: --snip-- 360 Chapter 16 When we run the program we get an error, as shown in the last line in the follow ing output: Traceback (most recent call last): File "highs_lows.py", line 17, in high = int(row[1]) ValueError: invalid literal for int() with base 10: '' The traceback tells us that Python can’t process the high temperature for one of the dates because it can’t turn an empty string ( '') into an inte - ger. A look through death_valley_2014.csv shows the problem: 2014-2-16,,,,,,,,,,,,,,,,,,,0.00,,,-1 It seems that on Februar y 16, 2014, no data was recorded; the string for the high temperature is empty. To address this issue, we’ll run error- checking code when the values are being read from the CSV file to handle exceptions that might arise when we parse our data sets. Here’s how that works: highs_lows.py --snip-- # Get dates, high and low temperatures from file. filename = 'death_valley_2014.csv' with open(filename) as f: reader = csv.reader(f) header_row = next(reader) dates, highs, lows = [], [], [] for row in reader: u try: current_date = datetime.strptime(row[0], "%Y-%m-%d") high = int(row[1]) low = int(row[3]) except ValueError: v print(current_date, 'missing data') else: w dates.append(current_date) highs.append(high) lows.append(low) # Plot data. --snip-- # Format plot. x title = "Daily high and low temperatures - 2014\nDeath Valley, CA" plt.title(title, fontsize=20) --snip-- Downloading Data 3 61 Each time we examine a row, we tr y to extract the date and the high and low temperature u . If any data is missing, Python will raise a ValueError and we handle it by printing an error message that includes the date of the missing data v . A fter printing the error, the loop will continue processing the next row. If all data for a date is retrieved without error, the else block will run and the data will be appended to the appropriate lists w . Because we’re plotting information for a new location, we update the title to include the location on the plot x . W hen you run highs_lows.py now, you’ll see that only one date had miss - ing data: 2014-02-16 missing data Figure 16 - 6 shows the resulting plot. Figure 16 - 6: Daily high and low temperatures for Death Valley Comparing this graph to the Sitka graph, we can see that Death Valley is warmer overall than southeast Alaska, as might be expected, but also that the range of temperatures each day is actually greater in the desert. The height of the shaded region makes this clear. Many data sets you work with will have missing data, improperly format - ted data, or incorrect data. Use the tools you learned in the first half of this book to deal with these situations. Here we used a try -except -else block to handle missing data. Sometimes you’ll use continue to skip over some data or use remove() or del to eliminate some data after it’s been extracted. You can use any approach that works, as long as the result is a meaningful, accu - rate visualization. 362 Chapter 16 try It y ourself 16 -1. San Francisco: Are temperatures in San Francisco more like temperatures in Sitka or temperatures in Death Valley? Generate a high-low temperature plot for San Francisco and make a comparison . (You can download weather data for almost any location from http://www.wunderground.com/history/ . Enter a location and date range, scroll to the bottom of the page, and find a link labeled Comma- Delimited File . Right- click this link, and save the data as a CSV file .) 16 -2 . Sitka-Death Valley Comparison: The temperature scales on the Sitka and Death Valley graphs reflect the different ranges of the data . To accu- rately compare the temperature range in Sitka to that of Death Valley, you need identical scales on the y-axis . Change the settings for the y-axis on one or both of the charts in Figures 16 -5 and 16 - 6, and make a direct com - parison between temperature ranges in Sitka and Death Valley (or any two places you want to compare) . You can also try plotting the two data sets on the same chart . 16 -3. Rainfall: Choose any location you’re interested in, and make a visualiza - tion that plots its rainfall . Start by focusing on one month’s data, and then once your code is working, run it for a full year’s data . 16 - 4 . Explore: Generate a few more visualizations that examine any other weather aspect you’re interested in for any locations you’re curious about . mapping global d ata sets: json f ormat In this section, you’ll download location-based countr y data in the JSON format and work with it using the json module. Using Pygal’s beginner- friendly mapping tool for countr y-based data, you’ll create visualizations of this data that explore global patterns concerning the world’s population distribution over different countries. Downloading World Population Data Copy the file population_data.json , which contains population data from 1960 through 2010 for most of the world’s countries, to the folder where you’re storing this chapter’s programs. This data comes from one of the many data sets that the Open K nowledge Foundation ( http://data.okfn.org/) makes freely available. Downloading Data 363 Extracting Relevant Data Let’s look at population_data.json to see how we might begin to process the data in the file: population_ [ data.json { "Country Name": "Arab World", "Country Code": "ARB", "Year": "1960", "Value": "96388069" }, { "Country Name": "Arab World", "Country Code": "ARB", "Year": "1961", "Value": "98882541.4" }, --snip-- ] The file is basically one long Python list. Each item is a dictionar y with four keys: a countr y name, a countr y code, a year, and a value representing the population. We want to examine each countr y’s name and population only in 2010, so start by writing a program to print just that information: world_ import json population.py # Load the data into a list. filename = 'population_data.json' with open(filename) as f: u pop_data = json.load(f) # Print the 2010 population for each country. v for pop_dict in pop_data: w if pop_dict['Year'] == '2010': x country_name = pop_dict['Country Name'] population = pop_dict['Value'] print(country_name + ": " + population) We first import the json module to be able to load the data properly from the file, and then we store the data in pop_data at u . The json.load() function converts the data into a format Python can work with: in this case, a list. At v we loop through each item in pop_data. Each item is a dictionar y with four key-value pairs, and we store each dictionar y in pop_dict . At w we look for 2010 in the 'Year' key of each dictionar y. (Because the values in population_data.json are all in quotes, we do a string compari - son.) If the year is 2010, we store the value associated with 'Country Name' in country_name and the value associated with 'Value' in population at x . We then print the name of each countr y and its population. 364 Chapter 16 The output is a series of countr y names and population values: Arab World: 357868000 Caribbean small states: 6880000 East Asia & Pacific (all income levels): 2201536674 --snip-- Zimbabwe: 12571000 Not all of the data we captured includes exact countr y names, but this is a good start. Now we need to convert the data into a format Pygal can work with. Converting Strings into Numerical Values Ever y key and value in population_data.json is stored as a string. To work with the population data, we need to convert the population strings to numeri - cal values. We do this using the int() function: world_ --snip-- population.py for pop_dict in pop_data: if pop_dict['Year'] == '2010': country_name = pop_dict['Country Name'] u population = int(pop_dict['Value']) v print(country_name + ": " + str(population)) Now we’ve stored each population value in numerical format at u . When we print the population value, we need to convert it to a string at v . However, this change results in an error for some values, as shown here: Arab World: 357868000 Caribbean small states: 6880000 East Asia & Pacific (all income levels): 2201536674 --snip-- Traceback (most recent call last): File "print_populations.py", line 12, in population = int(pop_dict['Value']) u ValueError: invalid literal for int() with base 10: '1127437398.85751 ' It’s often the case that raw data isn’t formatted consistently, so we come across errors a lot. Here the error occurs because Python can’t directly turn a string that contains a decimal, '1127437398.85751' , into an integer u . (This decimal value is probably the result of interpolation for years when a spe - cific population count was not made.) We address this error by converting the string to a float and then converting that float to an integer: world_ --snip-- population.py for pop_dict in pop_data: if pop_dict['Year'] == '2010': country = pop_dict['Country Name'] population = int(float(pop_dict['Value'])) print(country + ": " + str(population)) Downloading Data 365 The float() function turns the string into a decimal, and the int() func- tion drops the decimal part of the number and returns an integer. Now we can print a full set of population values for the year 2010 with no errors: Arab World: 357868000 Caribbean small states: 6880000 East Asia & Pacific (all income levels): 2201536674 --snip-- Zimbabwe: 12571000 Each string was successfully converted to a float and then to an integer. Now that these population values are stored in a numerical format, we can use them to make a world population map. Obtaining Two-Digit Country Codes Before we can focus on mapping, we need to address one last aspect of the data. The mapping tool in Pygal expects data in a particular format: countries need to be provided as countr y codes and populations as values. Several standardized sets of countr y codes are frequently used when work - ing with geopolitical data; the codes included in population_data.json are three-letter codes, but Pygal uses two-letter codes. We need a way to find the two-digit code from the countr y name. Pygal’s countr y codes are stored in a module called i18n , short for internationalization . The dictionar y COUNTRIES contains the two-letter countr y codes as keys and the countr y names as values. To see these codes, import the dictionar y from the i18n module and print its keys and values: countries.py from pygal.i18n import COUNTRIES u for country_code in sorted(COUNTRIES.keys()): print(country_code, COUNTRIES[country_code]) In the for loop we tell Python to sort the keys in alphabetical order u . Then we print each countr y code and the countr y it’s associated with: ad Andorra ae United Arab Emirates af Afghanistan --snip-- zw Zimbabwe To extract the countr y code data, we write a function that searches through COUNTRIES and returns the countr y code. We’ll write this in a sepa - rate module called country_codes so we can later import it into a visualiza - tion program: country_ from pygal.i18n import COUNTRIES codes.py u def get_country_code(country_name): """Return the Pygal 2-digit country code for the given country.""" 366 Chapter 16 v for code, name in COUNTRIES.items(): w if name == country_name: return code # If the country wasn't found, return None. x return None print(get_country_code('Andorra')) print(get_country_code('United Arab Emirates')) print(get_country_code('Afghanistan')) We pass get_country_code() the name of the countr y and store it in the parameter country_name u . We then loop through the code-name pairs in COUNTRIES v . If the name of the countr y is found, the countr y code is returned w. We add a line after the loop to return None if the countr y name was not found x . Finally, we pass three countr y names to check that the function works. As expected, the program outputs three two-letter countr y codes: ad ae af Before using this function, remove the print statements from country_codes.py . Next we import get_country_code() into world_population.py : world_ import json population.py from country_codes import get_country_code --snip-- # Print the 2010 population for each country. for pop_dict in pop_data: if pop_dict['Year'] == '2010': country_name = pop_dict['Country Name'] population = int(float(pop_dict['Value'])) u code = get_country_code(country_name) if code: v print(code + ": "+ str(population)) w else: print('ERROR - ' + country_name) A fter extracting the countr y name and population, we store the coun - tr y code in code or None if no code is available u . If a code is returned, the code and countr y’s population are printed v . If the code is not available, we display an error message with the name of the countr y we can’t find a code for w . Run this program, and you’ll see some countr y codes with their populations and some error lines: ERROR - Arab World ERROR - Caribbean small states ERROR - East Asia & Pacific (all income levels) Downloading Data 367 --snip-- af: 34385000 al: 3205000 dz: 35468000 --snip-- ERROR - Yemen, Rep. zm: 12927000 zw: 12571000 The errors come from two sources. First, not all the classifications in the data set are by countr y; some population statistics are for regions ( Arab World ) and economic groups ( all income levels). Second, some of the statistics use a different system for full names of countries ( Yemen, Rep. instead of Ye m e n ). For now, we’ll omit countr y data that cause errors and see what our map looks like for the data that we recovered successfully. Building a World Map With the countr y codes we have, it’s quick and simple to make a world map. Pygal includes a Worldmap chart type to help map global data sets. As an example of how to use Worldmap , we’ll create a simple map that high - lights North America, Central America, and South America: americas.py import pygal u wm = pygal.Worldmap() wm.title = 'North, Central, and South America' v wm.add('North America', ['ca', 'mx', 'us']) wm.add('Central America', ['bz', 'cr', 'gt', 'hn', 'ni', 'pa', 'sv']) wm.add('South America', ['ar', 'bo', 'br', 'cl', 'co', 'ec', 'gf', 'gy', 'pe', 'py', 'sr', 'uy', 've']) w wm.render_to_file('americas.svg') At u we make an instance of the Worldmap class and set the map’s title attribute. At v we use the add() method, which takes in a label and a list of countr y codes for the countries we want to focus on. Each call to add() sets up a new color for the set of countries and adds that color to a key on the left of the graph with the label specified here. We want the entire region of North America represented in one color, so we place 'ca' , 'mx' , and 'us' in the list we pass to the first add() call to highlight Canada, Mexico, and the United States together. We then do the same for the countries in Central A merica and South A merica. The method render_to_file() at w creates an .svg file containing the chart, which you can open in your browser. The output is a map highlight - ing North, Central, and South America in different colors, as shown in Figure 16 -7. 368 Chapter 16 Figure 16 -7: A simple instance of the Worldmap chart type We now know how to make a map with colored areas, a key, and neat labels. Let’s add data to our map to show information about a countr y. Plotting Numerical Data on a World Map To practice plotting numerical data on a map, create a map showing the populations of the three countries in North America: na_ import pygal populations.py wm = pygal.Worldmap() wm.title = 'Populations of Countries in North America' u wm.add('North America', {'ca': 34126000, 'us': 309349000, 'mx': 113423 000}) wm.render_to_file('na_populations.svg') First create a Worldmap instance and set a title. Then use the add() method, but this time pass a dictionar y as the second argument instead of a list u . The dictionar y has Pygal two-letter countr y codes as its keys and population numbers as its values. Pygal automatically uses these numbers to shade the countries from light (least populated) to dark (most populated). Figure 16 - 8 shows the resulting map. Downloading Data 369 Figure 16 -8: Population sizes of countries in North America This map is interactive: if you hover over each countr y, you’ll see its population. Let’s add more data to our map. Plotting a Complete Population Map To plot population numbers for the rest of the countries, we have to con - vert the countr y data we processed earlier into the dictionar y format Pygal expects, which is two-letter countr y codes as keys and population numbers as values. Add the following code to world_population.py : world_ import json population.py import pygal from country_codes import get_country_code # Load the data into a list. --snip-- # Build a dictionary of population data. u cc_populations = {} for pop_dict in pop_data: if pop_dict['Year'] == '2010': country = pop_dict['Country Name'] population = int(float(pop_dict['Value'])) code = get_country_code(country) 370 Chapter 16 if code: v cc_populations[code] = population w wm = pygal.Worldmap() wm.title = 'World Population in 2010, by Country' x wm.add('2010', cc_populations) wm.render_to_file('world_population.svg') We first import pygal . At u we create an empty dictionar y to store countr y codes and populations in the format Pygal expects. At v we build the cc_populations dictionar y using the countr y code as a key and the popu - lation as the value whenever a code is returned. We also remove all the print statements. We make a Worldmap instance and set its title attribute w . When we call add() , we pass it the dictionar y of countr y codes and population values x . Figure 16 -9 shows the map that’s generated. Figure 16 -9: The world’s population in 2010 We don’t have data for a few countries, which are colored in black, but most countries are colored according to their population size. You’ll deal with the missing data later in this chapter, but first we’ll alter the shading to more accurately reflect the population of the countries. Currently, our map shows many lightly shaded countries and two darkly shaded countries. The contrast between most of the countries isn’t enough to indicate how popu - lated they are relative to each other. We’ll fix this by grouping countries into population levels and shading each group. Downloading Data 371 Grouping Countries by Population Because China and India are more heavily populated than other countries, the map shows little contrast. China and India are each home to over a bil- lion people, whereas the next most populous countr y is the United States with approximately 300 million people. Instead of plotting all countries as one group, let’s separate the countries into three population levels: less than 10 million, between 10 million and 1 billion, and more than 1 billion: world_ --snip-- population.py # Build a dictionary of population data. cc_populations = {} for pop_dict in pop_data: if pop_dict['Year'] == '2010': --snip-- if code: cc_populations[code] = population # Group the countries into 3 population levels. u cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {} v for cc, pop in cc_populations.items(): if pop < 10000000: cc_pops_1[cc] = pop elif pop < 1000000000: cc_pops_2[cc] = pop else: cc_pops_3[cc] = pop # See how many countries are in each level. w print(len(cc_pops_1), len(cc_pops_2), len(cc_pops_3)) wm = pygal.Worldmap() wm.title = 'World Population in 2010, by Country' x wm.add('0-10m', cc_pops_1) wm.add('10m-1bn', cc_pops_2) wm.add('>1bn', cc_pops_3) wm.render_to_file('world_population.svg') To group the countries, we create an empty dictionar y for each cat - egor y u. We then loop through cc_populations to check the population of each countr y v . The if-elif -else block adds an entr y to the appropri - ate dictionar y ( cc_pops_1 , cc_pops_2 , or cc_pops_3 ) for each countr y code– p opu l a t ion p a i r. At w we print the length of each of these dictionaries to find out the sizes of the groups. When we plot the data x , we make sure to add all three groups to the Worldmap . When you run this program, you’ll first see the size of each group: 85 69 2 372 Chapter 16 This output indicates that there are 85 countries with fewer than 10 million people, 69 countries with between 10 million and 1 billion people, and two outlier countries with over 1 billion. This seems like an even enough split for an informative map. Figure 16 -10 shows the resulting map. Figure 16 -10: The world’s population shown in three different groups Now three different colors help us see the distinctions between popula - tion levels. Within each of the three levels, countries are shaded from light to dark for smallest to largest population. Styling World Maps in Pygal Although the population groups in our map are effective, the default color settings are pretty ugly: for example, here Pygal has chosen a garish pink and green motif. We’ll use Pygal’s styling directives to rectify the colors. Let’s direct Pygal to use one base color again, but this time we’ll choose the color and apply more distinct shading for the three population groups: world_ import json population.py import pygal u from pygal.style import RotateStyle --snip-- # Group the countries into 3 population levels. cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {} Downloading Data 373 for cc, pop in cc_populations.items(): if pop < 10000000: --snip-- v wm_style = RotateStyle('#336699') w wm = pygal.Worldmap(style=wm_style) wm.title = 'World Population in 2010, by Country' --snip-- Pygal styles are stored in the style module from which we import the style RotateStyle u . This class takes one argument, an RGB color in hex format v . Pygal then chooses colors for each of the groups based on the color provided. The hex format is a string with a hash mark ( #) followed by six characters: the first two represent the red component of the color, the next two represent the green component, and the last two represent the blue component. The component values can range from 00 (none of that color) to FF (maximum amount of that color). If you search online for hex color chooser , you should find a tool that will let you experiment with colors and give you the RGB values. The color used here (# 336699) mixes a bit of red (33) , a little more green (66), and even more blue (99) to give RotateStyle a light blue base color to work from. RotateStyle returns a style object, which we store in wm_style . To use this style object, pass it as a key word argument when you make an instance of Worldmap w . Figure 16 -11 shows the updated chart. Figure 16 -11: The three population groups in a unified color theme 374 Chapter 16 This styling gives the map a unified look, and it results in groups that are easy to distinguish. Lightening the Color Theme Pygal tends to use dark themes by default. For the purposes of printing, I’ve lightened the style of my charts using LightColorizedStyle . This class changes the overall theme of the chart, including the background and labels as well as the individual countr y colors. To use it, first import the style: from pygal.style import LightColorizedStyle You can then use LightColorizedStyle on its own, as such: wm_style = LightColorizedStyle But this class gives you no direct control over the color used, so Pygal will choose a default base color. To set a color, use LightColorizedStyle as a base for RotateStyle . Import both LightColorizedStyle and RotateStyle : from pygal.style import LightColorizedStyle, RotateStyle Then create a style using RotateStyle , but pass it an additional base_style argument: wm_style = RotateStyle('#336699', base_style=LightColorizedStyle) This gives you a light overall theme but bases the countr y colors on the color you pass as an argument. With this style you’ll see that your charts match the screenshots here a bit more closely. While you’re experimenting to find styling directives that work well for different visualizations, it can help to use aliases in your import statements: from pygal.style import LightColorizedStyle as LCS, RotateStyle as RS This will result in shorter style definitions: wm_style = RS('#336699', base_style=LCS) Using just this small set of styling directives gives you significant control over the appearance of charts and maps in Pygal. Downloading Data 375 try It y ourself 16 -5. All Countries: On the population maps we made in this section, our pro - gram couldn’t automatically find two-letter codes for about 12 countries . Work out which countries are missing codes, and look through the COUNTRIES diction - ary for the codes . Add an if-elif block to get_country_code() so it returns the correct country code values for these specific countries: if country_name == 'Yemen, Rep.' return 'ye' elif --snip-- Place this code after the COUNTRIES loop but before the return None state - ment . When you’re finished, you should see a more complete map . 16 - 6 . Gross Domestic Product: The Open Knowledge Foundation maintains a data set containing the gross domestic product (GDP) for each country in the world, which you can find at http://data.okfn.org/data/core/gdp/ . Download the JSON version of this data set, and plot the GDP of each country in the world for the most recent year in the data set . 1 6 - 7. Choose Your Own Data: The World Bank maintains many data sets that are broken down for information on each country worldwide . Go to h t t p : // data.worldbank.org/indicator/ and find a data set that looks interesting . Click the data set, click the Download Data link, and choose CSV . You’ll receive three CSV files, two of which are labeled Metadata ; use the third CSV file . Write a program that generates a dictionary with Pygal’s two-letter country codes as its keys and your chosen data from the file as its values . Plot the data on a Worldmap and style the map as you like . 16 - 8 . Testing the country_codes Module: When we wrote the country_codes module, we used print statements to check whether the get_country_code() function worked . Write a proper test for this function using what you learned in Chapter 11 . summary In this chapter you learned to work with online data sets. You learned how to process CSV and JSON files, and extract the data you want to focus on. Using historical weather data, you learned more about working with matplotlib, including how to use the datetime module and how to plot mul - tiple data series on one chart. You learned to plot countr y data on a world map in Pygal and to style Pygal maps and charts. 376 Chapter 16 As you gain experience with CSV and JSON files, you’ll be able to process almost any data you want to analyze. Most online data sets can be downloaded in either or both of these formats. From working with these formats, you’ll be able to learn other data formats as well. In the next chapter, you’ll write programs that automatically gather their own data from online sources, and then you’ll create visualizations of that data. These are fun skills to have if you want to program as a hobby and critical skills if you’re interested in programming professionally. 17 work Ing w Ith a PI s In this chapter you’ll learn how to write a self-contained program to generate a visualization based on data that it retrieves. Your program will use a web application pro - gramming interface (A PI) to automatically request spe - cific information from a website rather than entire pages. It will then use that information to generate a visualization. Because programs written like this will always use current data to generate a visualization, even when that data might be rapidly changing, it will always be up to date. 378 Chapter 17 using a w eb aPI A web A PI is a part of a website designed to interact with programs that use ver y specific UR Ls to request certain information. This kind of request is called an API call . The requested data will be returned in an easily pro - cessed format, such as JSON or CSV. Most apps that rely on external data sources, such as apps that integrate with social media sites, rely on A PI calls. Git and GitHub We’ll base our visualization on information from GitHub, a site that allows programmers to collaborate on projects. We’ll use GitHub’s A PI to request information about Python projects on the site and then generate an interactive visualization of the relative popularity of these projects in P ygal. Git Hub ( h t t p s ://g i t h u b.c o m / ) takes its name from Git, a distributed ver - sion control system that allows teams of programmers to collaborate on projects. Git helps people manage their individual work on a project, so changes made by one person won’t interfere with changes other people are making. When you’re implementing a new feature in a project, Git tracks the changes you make to each file. When your new code works, you commit the changes you’ve made, and Git records the new state of your project. If you make a mistake and want to revert your changes, you can easily return to any previously working state. (To learn more about ver - sion control using Git, see Appendix D.) Projects on GitHub are stored in repositories , which contain ever ything associated with the project: its code, information on its collaborators, any issues or bug reports, and so on. When users on GitHub like a project, they can “star” it to show their support and keep track of projects they might want to use. In this chapter we’ll write a program to automatically download information about the most-starred Python projects on GitHub, and then we’ll create an informa - tive visualization of these projects. Requesting Data Using an API Call GitHub’s A PI lets you request a wide range of information through A PI calls. To see what an A PI call looks like, enter the following into your browser’s address bar and press enter : https://api.github.com/search/repositories?q=language:python&sort=stars This call returns the number of Python projects currently hosted on GitHub, as well as information about the most popular Python repositories. Let’s examine the call. The first part, https://api.github.com/ , directs the request to the part of GitHub’s website that responds to A PI calls. The next part, search/repositories , tells the A PI to conduct a search through all repos - itories on GitHub. The question mark after repositories signals that we’re about to pass an argument. The q stands for quer y, and the equal sign lets us begin Working with APIs 379 specifying a quer y ( q=). By using language:python , we indicate that we want information only on repositories that have Python as the primar y language. The final part, &sort=stars , sorts the projects by the number of stars they’ve been given. The following snippet shows the first few lines of the response. You can see from the response that this UR L is not intended to be entered by humans. { "total_count": 713062, "incomplete_results": false, "items": [ { "id": 3544424, "name": "httpie", "full_name": "jkbrzt/httpie", --snip-- As you can see in the second line of output, GitHub found a total of 713,062 Python projects as of this writing. Because the value for " incomplete_results" is false , we know that the request was successful (it’s not incomplete). If GitHub had been unable to fully process the A PI request, it would have returned true here. The "items" returned are displayed in the list that follows, which contains details about the most popular Python projects on GitHub. Installing Requests The requests package allows a Python program to easily request informa - tion from a website and examine the response that’s returned. To install requests, issue a command like the following:$ pip install --user requests
If you haven’t used pip yet, see “Installing Python Packages with pip”
on page 237. (You may need to use a slightly different version of this com -
mand, depending on your system’s setup.)
Processing an API Response
Now we’ll begin to write a program to issue an A PI call and process the
results by identifying the most starred Python projects on GitHub:
python_ u import requests
repos.py # Make an API call and store the response.
v url = 'https://api.github.com/search/repositories?q=language:python&sort
=stars'
w r = requests.get(url)
x print("Status code:", r.status_code)

380 Chapter 17
# Store API response in a variable.
y response_dict = r.json()
# Process results.
print(response_dict.keys())
At u we import the requests module. At v we store the UR L of the
A PI call, and then we use
requests to make the call w . We call get() and
pass it the UR L, and we store the response object in the variable
r. The
response object has an attribute called
status_code , which tells us whether
the request was successful. (A status code of 200 indicates a successful
response.) At x we print the value of
status_code to make sure the call
went through successfully. The A PI returns the information in JSON format, so we use the
json()
method y to convert the information to a Python dictionar y. We store the
resulting dictionar y in
response_dict .
Finally, we print the keys from
response_dict and see this:
Status code: 200
dict_keys(['items', 'total_count', 'incomplete_results'])
Because the status code is 200, we know that the request was successful.
The response dictionar y contains only three keys:
'items' , 'total_count' , and
'incomplete_results' .
note Simple calls like this should return a complete set of results, so it’s pretty safe to ignore
the value associated with
'incomplete_results' . But when you’re making more com -
plex API calls, your program should check this value.
Working with the Response Dictionary
Now that we have the information from the A PI call stored as a dictionar y,
we can work with the data stored there. Let’s generate some output that
summarizes the information. This is a good way to make sure we received
the information we expected and to start examining the information we’re
interested in:
python_ import requests
repos.py # Make an API call and store the response.
url = 'https://api.github.com/search/repositories?q=language:python&sort
=stars'
r = requests.get(url)
print("Status code:", r.status_code)
# Store API response in a variable.
response_dict = r.json()
u print("Total repositories:", response_dict['total_count'])
# Explore information about the repositories.
v repo_dicts = response_dict['items'] print("Repositories returned:", len(repo_dicts))

Working with APIs 381
# Examine the first repository.
w repo_dict = repo_dicts[0]
x print("\nKeys:", len(repo_dict))
y for key in sorted(repo_dict.keys()): print(key)
At u we print the value associated with 'total_count' , which represents
the total number of Python repositories on GitHub. The value associated with
'items' is a list containing a number of dic -
tionaries, each of which contains data about an individual Python reposi -
tor y. At v we store this list of dictionaries in
repo_dicts . We then print the
length of
repo_dicts to see how many repositories we have information for.
To take a closer look at the information returned about each reposi -
tor y, we pull out the first item from
repo_dicts and store it in repo_dict w . We
then print the number of keys in the dictionar y to see how much informa -
tion we have x . At y we print all of the dictionar y’s
keys to see what kind of
information is included. The results start to give us a clearer picture of the actual data:
Status code: 200
Total repositories: 713062
Repositories returned: 30
u Keys: 68 archive_url
assignees_url
blobs_url
--snip--
url
watchers
watchers_count
GitHub’s A PI returns a lot of information about each repositor y: there
are 68 keys in
repo_dict u . When you look through these keys, you’ll get
a sense of the kind of information you can extract about a project. (The
only way to know what information is available through an A PI is to read
the documentation or to examine the information through code, as we’re
doing here.) Let’s pull out the values for some of the keys in
repo_dict :
python_ --snip--
repos.py # Explore information about the repositories.
repo_dicts = response_dict['items']
print("Repositories returned:", len(repo_dicts))
# Examine the first repository.
repo_dict = repo_dicts[0]
u print('Name:', repo_dict['name'])

382 Chapter 17
w print('Stars:', repo_dict['stargazers_count'])print('Repository:', repo_dict['html_url'])
x print('Created:', repo_dict['created_at'])
y print('Updated:', repo_dict['updated_at']) print('Description:', repo_dict['description'])
Here we print out the values for a number of keys from the first reposi -
tor y’s dictionar y. At u we print the name of the project. An entire diction -
ar y represents the project’s owner, so at v we use the key
owner to access
the dictionar y representing the owner and then use the key
owner’s login name. At w we print how many stars the project has earned
and the UR L for the project’s GitHub repositor y. We then show when it was
created x and when it was last updated y . Finally, we print the repositor y’s
description, and the output should look something like this:
Status code: 200
Total repositories: 713065
Repositories returned: 30
Name: httpie
Owner: jkbrzt
Stars: 16101
Repository: https://github.com/jkbrzt/httpie
Created: 2012-02-25T12:39:13Z
Updated: 2015-07-13T14:56:41Z
Description: CLI HTTP client; user-friendly cURL replacement featuring
extensions, etc.
We can see that the most-starred Python project on GitHub as of this
writing is HTTPie, its owner is user jkbrzt , and it has been starred by more
than 16,000 GitHub users. We can see the UR L for the project’s reposi -
tor y, its creation date of Februar y 2012, and that it was updated recently.
Finally, the description tells us that HTTPie helps make HTTP calls from
a terminal ( CLI is short for command line interface ).
Summarizing the Top Repositories
When we make a visualization for this data, we’ll want to include more than
one repositor y. Let’s write a loop to print selected information about each
of the repositories returned by the A PI call so we can include them all in
the visualization:
python_ --snip--
repos.py # Explore information about the repositories.
repo_dicts = response_dict['items']
print("Repositories returned:", len(repo_dicts))

Working with APIs 383
u print("\nSelected information about each repository:")
v for repo_dict in repo_dicts: print('\nName:', repo_dict['name'])
print('Stars:', repo_dict['stargazers_count'])
print('Repository:', repo_dict['html_url'])
print('Description:', repo_dict['description'])
We print an introductor y message at u . At v we loop through all the
dictionaries in
repo_dicts . Inside the loop we print the name of each proj -
ect, its owner, how many stars it has, its UR L on GitHub, and the project’s
description:
Status code: 200
Total repositories: 713067
Repositories returned: 30

Name: httpie
Owner: jkbrzt
Stars: 16101
Repository: https://github.com/jkbrzt/httpie
Description: CLI HTTP client; user-friendly cURL replacement featuring
extensions, etc.
Name: django
Owner: django
Stars: 15028
Repository: https://github.com/django/django
Description: The Web framework for perfectionists with deadlines.
--snip--
Name: powerline
Owner: powerline
Stars: 4315
Repository: https://github.com/powerline/powerline
Description: Powerline is a statusline plugin for vim, and provides
statuslines and prompts for several other applications, including zsh, b
ash,
tmux, IPython, Awesome and Qtile.
Some interesting projects appear in these results, and it might be worth
taking a look at a few. But don’t spend too long on it, because we’re about
to create a visualization that will make it much easier to read through the
result s.
Monitoring API Rate Limits
Most A PIs are rate-limited, which means there’s a limit to how many
requests you can make in a certain amount of time. To see if you’re

384 Chapter 17
approaching GitHub’s limits, enter https://api.github.com/rate_limit into a
web browser. You should see a response like this:
{
"resources": {
"core": {
"limit": 60,
"remaining": 58,
"reset": 1426082320
},
u "search": {
v "limit": 10,
w "remaining": 8,
x "reset": 1426078803 }
},
"rate": {
"limit": 60,
"remaining": 58,
"reset": 1426082320
}
}
The information we’re interested in is the rate limit for the search
A PI u. We see at v that the limit is 10 requests per minute and that we have
8 requests remaining for the current minute w . The reset value represents
the time in Unix or epoch time (the number of seconds since midnight on
Januar y 1, 1970) when our quota will reset x . If you reach your quota, you’ll
get a short response that lets you know you’ve reached the A PI limit. If you
reach the limit, just wait until your quota resets.
note Many APIs require you to register and obtain an API key in order to make API calls.
As of this writing GitHub has no such requirement, but if you obtain an API key,
your limits will be much higher.
Visualizing r epositories u sing Pygal
Now that we have some interesting data, let’s make a visualization showing
the relative popularity of Python projects on GitHub. We’ll make an inter -
active bar chart: the height of each bar will represent the number of stars
the project has acquired. Clicking a bar will take you to that project’s home
on GitHub. Here’s an initial attempt:
python_ import requests
repos.py import pygal
from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS

Working with APIs 385
# Make an API call and store the response.
URL = 'https://api.github.com/search/repositories?q=language:python&sort
=star'
r = requests.get(URL)
print("Status code:", r.status_code)
# Store API response in a variable.
response_dict = r.json()
print("Total repositories:", response_dict['total_count'])
# Explore information about the repositories.
repo_dicts = response_dict['items']
u names, stars = [], [] for repo_dict in repo_dicts:
v names.append(repo_dict['name']) stars.append(repo_dict['stargazers_count'])
# Make visualization.
w my_style = LS('#333366', base_style=LCS)
x chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=Fal
se) chart.title = 'Most-Starred Python Projects on GitHub'
chart.x_labels = names
We start by importing pygal and the Pygal styles we’ll need for the
chart. We continue to print the status of the A PI call response and the total
number of repositories found, so we’ll know if there was a problem with the
A PI call. We no longer print information about the specific projects that are
returned, because that information will be included in the visualization. At u we create two empty lists to store the data we’ll include in the
chart. We’ll need the name of each project in order to label the bars, and
we’ll need the number of stars to determine the height of the bars. In the
loop, we append the name of each project and number of stars it has to
these lists v .
Next we define a style using the
LightenStyle class (alias LS) and base it
on a dark shade of blue w . We also pass the
base_style argument to use the
LightColorizedStyle class (alias LCS ). We then use Bar() to make a simple bar
chart and pass it
my_style x . We pass two more style arguments: we set the
rotation of the labels along the x-axis to 45 degrees (
x_label_rotation=45 ),
and we hide the legend, because we’re plotting only one series on the chart
(
show_legend=False ). We then give the chart a title and set the x_labels attri -
bute to the list
names .
Because we don’t need this data series to be labeled, we pass an empty
string for the label when we add the data at y . The resulting chart is shown
in Figure 17-1. We can see that the first few projects are significantly more
popular than the rest, but all of them are important projects in the Python
ecosystem.

386 Chapter 17
Figure 17-1: The most-starred Python projects on GitHub
Refining Pygal Charts
Let’s refine the styling of our chart. We’ll be making a few different custom-
izations, so first restructure the code slightly by creating a configuration
object that contains all of our customizations to pass to
Bar() :
python_ --snip--
repos.py # Make visualization.
my_style = LS('#333366', base_style=LCS)
u my_config = pygal.Config()
v my_config.x_label_rotation = 45 my_config.show_legend = False
w my_config.title_font_size = 24 my_config.label_font_size = 14
my_config.major_label_font_size = 18
x my_config.truncate_label = 15
y my_config.show_y_guides = False
z my_config.width = 1000
{ chart = pygal.Bar(my_config, style=my_style) chart.title = 'Most-Starred Python Projects on GitHub'
chart.x_labels = names
chart.render_to_file('python_repos.svg')
At u we make an instance of Pygal’s Config class, called my_config ;
modifying the attributes of
my_config will customize the appearance of the
chart. We set the two attributes
x_label_rotation and show_legend v , origi -
nally passed as key word arguments when we made an instance of
Bar . At w

Working with APIs 387
we set the font size for the chart’s title, minor labels, and major labels. The
minor labels in this chart are the project names along the x-axis and most
of the numbers along the y-axis. The major labels are just the labels on the
y-axis that mark off increments of 5000 stars. These labels will be larger,
which is why we differentiate between the two. At x we use
truncate_label to
shorten the longer project names to 15 characters. (When you hover over a
truncated project name on your screen, the full name will pop up.) Next,
we hide the horizontal lines on the graph by setting
show_y_guides to False y .
Finally, at z we set a custom width so the chart will use more of the avail -
able space in the browser. Now when we make an instance of
Bar at { , we pass my_config as the first
argument, and it sends all of our configuration settings in one argument.
We can make as many style and configuration changes as we want through
my_config , and the line at { won’t change. Figure 17-2 shows the restyled
chart.
Figure 17-2: The styling for the chart has been refined.
In Pygal, hovering the cursor over an individual bar shows the information
that the bar represents. This is commonly called a tooltip , and in this case
it currently shows the number of stars a project has. Let’s create a custom
tooltip to show each project’s description as well. Let’s look at a short example using the first three projects plotted indi -
vidually with custom labels passed for each bar. To do this, we’ll pass a list
of dictionaries to
bar_ import pygal
descriptions.py from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS
my_style = LS('#333366', base_style=LCS)
chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=Fals
e)

388 Chapter 17
chart.title = 'Python Projects'
u plot_dicts = [
v {'value': 16101, 'label': 'Description of httpie.'}, {'value': 15028, 'label': 'Description of django.'},
{'value': 14798, 'label': 'Description of flask.'},
]
At u we define a list called plot_dicts that contains three dictionaries:
one for the HTTPie project, one for the Django project, and one for Flask.
Each dictionar y has two keys:
'value' and 'label' . Pygal uses the number
associated with
'value' to figure out how tall each bar should be, and it
uses the string associated with
'label' to create the tooltip for each bar. For
example, the first dictionar y at v will create a bar representing a project
with 16,101 stars, and its tooltip will say Description of httpie .
The
add() method needs a string and a list. When we call add() , we pass
in the list of dictionaries representing the bars (
plot_dicts ) w . Figure 17-3
shows one of the tooltips. Pygal includes the number of stars as a default
tooltip in addition to the custom tooltip we passed it.
Figure 17-3: Each bar has a customized tooltip label.
Plotting the Data
To plot our data, we’ll generate plot_dicts automatically for the 30 projects
returned by the A PI call.

Working with APIs 389
Here’s the code to do this:
python_ --snip--
repos.py # Explore information about the repositories.
repo_dicts = response_dict['items']
print("Number of items:", len(repo_dicts))
u names, plot_dicts = [], [] for repo_dict in repo_dicts:
names.append(repo_dict['name'])

v plot_dict = { 'value': repo_dict['stargazers_count'],
'label': repo_dict['description'],
}
w plot_dicts.append(plot_dict)
# Make visualization.
my_style = LS('#333366', base_style=LCS)
--snip--
At u we make an empty list for names and an empty list for plot_dicts .
We still need the
names list in order to generate the labels for the x-axis.
Inside the loop we create the dictionar y
plot_dict for each project v .
We store the number of stars with the key
'value' and the project descrip -
tion with the key
'label' in each plot_dict . We then append each project’s
plot_dict to plot_dicts w . At x we pass the list plot_dicts to add() . Figure 17- 4
shows the resulting chart.
Figure 17- 4: Hovering over a bar shows the project’s description.

390 Chapter 17
Pygal also allows you to use each bar in the chart as a link to a website. To
add this capability, we just add one line to our code, leveraging the diction-
ar y we’ve set up for each project. We add a new key-value pair to each proj -
e c t’s
plot_dict using the key 'xlink' :
python_ --snip--
repos.py names, plot_dicts = [], []
for repo_dict in repo_dicts:
names.append(repo_dict['name'])
plot_dict = {
'value': repo_dict['stargazers_count'],
'label': repo_dict['description'],
}
plot_dicts.append(plot_dict)
--snip--
Pygal uses the UR L associated with 'xlink' to turn each bar into an
active link. You can click any of the bars in the chart, and the GitHub page
for that project will automatically open in a new tab in your browser. Now
you have an interactive, informative visualization of data retrieved through
an A PI!
t he h acker n ews aPI
To explore how you would use A PI calls on other sites, we’ll look at Hacker
News (http://news.ycombinator.com/ ). On Hacker News people share articles
sions and comments on the site, which is available without having to regis -
ter for a key. The following call returns information about the current top article as
of this writing:
https://hacker-news.firebaseio.com/v0/item/9884165.json
The response is a dictionar y of information about the article with the
I D 9 8 8 416 5 :
{
u 'url': 'http://www.bbc.co.uk/news/science-environment-33524589', 'type': 'story',
v 'title': 'New Horizons: Nasa spacecraft speeds past Pluto',
w 'descendants': 141, 'score': 230,
'time': 1436875181,
'text': '',

Working with APIs 391
'by': 'nns',
'id': 9884165,
x 'kids': [9884723, 9885099, 9884789, 9885604, 9885844] }
The dictionar y contains a number of keys we can work with, such
as
'url' u and 'title' v . The key 'descendants' contains the number of
'kids' provides the IDs of
all comments made directly in response to this submission x . Each of these
comments may have kids of their own as well, so the number of descendants
a submission has can be greater than its number of kids. Let’s make an A PI call that returns the IDs of the current top articles
on Hacker News, and then examine each of the top articles:
hn_ import requests
submissions.py from operator import itemgetter
# Make an API call and store the response.
u url = 'https://hacker-news.firebaseio.com/v0/topstories.json' r = requests.get(url)
print("Status code:", r.status_code)
# Process information about each submission.
v submission_ids = r.json()
w submission_dicts = [] for submission_id in submission_ids[:30]:
# Make a separate API call for each submission.
x url = ('https://hacker-news.firebaseio.com/v0/item/' + str(submission_id) + '.json')
submission_r = requests.get(url)
print(submission_r.status_code)
response_dict = submission_r.json()

y submission_dict = { 'title': response_dict['title'],
_id),
submission_dicts.append(submission_dict)

), reverse=True)
| for submission_dict in submission_dicts: print("\nTitle:", submission_dict['title'])
First, we make the A PI call and print the status of the response u . This
A PI call returns a list containing the IDs of the 500 most popular articles
on Hacker News at the time the call is issued. We then convert the response

392 Chapter 17
text to a Python list at v, which we store in submission_ids . We’ll use these
IDs to build a set of dictionaries that each store information about one of
the current submissions. We set up an empty list called
submission_dicts at w to store these
dictionaries. We then loop through the IDs of the top 30 submissions.
We make a new A PI call for each submission by generating a UR L that
includes the current value of
submission_id x . We print the status of each
request so we can see whether it is successful. At y we create a dictionar y for the submission currently being processed,
where we store the title of the submission and a link to the discussion page
for that item. At z we store the number of comments in the dictionar y. If an
article has no comments yet, the key
'descendants' will not be present. When
you’re not sure if a key exists in a dictionar y, use the
dict.get() method,
which returns the value associated with the given key if it exists or the value
you provide if the key doesn’t exist (0 in this example). Finally, we append
each
submission_dict to the list submission_dicts .
Submissions on Hacker News are ranked according to an overall score,
based on a number of factors including how many times it’s been voted
up, how many comments it’s received, and how recent the submission is.
We want to sort the list of dictionaries by the number of comments. To do
this, we use a function called
itemgetter() { , which comes from the operator
module. We pass this function the key
'comments' , and it pulls the value asso -
ciated with that key from each dictionar y in the list. The
sorted() function
then uses this value as its basis for sorting the list. We sort the list in reverse
order to place the most-commented stories first. Once the list is sorted, we loop through the list at | and print out
three pieces of information about each of the top submissions: the title,
a link to the discussion page, and the number of comments the submission
currently has:
Status code: 200
200
200
200
--snip--
Title: Firefox deactivates Flash by default
Title: New Horizons: Nasa spacecraft speeds past Pluto
Title: Iran Nuclear Deal Is Reached With World Powers

Working with APIs 393
Title: Match Group Buys PlentyOfFish for $575M Discussion link: http://news.ycombinator.com/item?id=9884417 Comments: 75 Title: Our Nexus 4 devices are about to explode Discussion link: http://news.ycombinator.com/item?id=9885625 Comments: 14 --snip-- You would use a similar process to access and analyze information with any A PI. With this data, you could make a visualization showing which sub - missions have inspired the most active recent discussions. t ry It y ourself 17-1. Other Languages: Modify the API call in python_repos.py so it generates a chart showing the most popular projects in other languages . Try languages such as JavaScript , Ruby, C, Java , Perl, Haskell , and Go . 17-2 . Active Discussions : Using the data from hn_submissions.py , make a bar chart showing the most active discussions currently happening on Hacker News . The height of each bar should correspond to the number of comments each submission has . The label for each bar should include the submission’s title, and each bar should act as a link to the discussion page for that submission . 17- 3 . Testing python_repos.py : In python_repos.py , we printed the value of status_code to make sure the API call was successful . Write a program called test_ python_repos.py , which uses unittest to assert that the value of status_code is 200 . Figure out some other assertions you can make—for example, that the number of items returned is expected and that the total number of repositories is greater than a certain amount . summary In this chapter you learned how to use A PIs to write self-contained pro - grams that automatically gather the data they need and use that data to create a visualization. We used the GitHub A PI to explore the most-starred Python projects on GitHub, and we looked briefly at the Hacker News A PI as well. You learned how to use the requests package to automatically issue an A PI call to GitHub and how to process the results of that call. We also introduced some Pygal settings that further customize the appearance of the charts you generate. In the final project we’ll use Django to build a web application. PrOjEC t 3 w eB aPPl ICat Ions 18 get t Ing s tarte D w Ith Django Behind the scenes, today’s websites are actually rich applications that act like fully developed desktop applications. Python has a great set of tools for building web applica - tions. In this chapter you’ll learn how to use Django ( http://djangoproject.com/ ) to build a project called Learning Log—an online journal system that lets you keep track of information you’ve learned about particular topics. We’ll write a specification for this project, and then we’ll define models for the data the app will work with. We’ll use Django’s admin system to enter some initial data and then learn to write views and templates so Django can build the pages of our site. Django is a web framework —a set of tools designed to help you build interactive websites. Django can respond to page requests and make it 398 Chapter 18 easier to read and write to a database, manage users, and much more. In Chapters 19 and 20 we’ll refine the Learning Log project and then deploy it to a live ser ver so you (and your friends) can use it. setting u p a Project When beginning a project, you first need to describe the project in a speci - fication, or spec . Then you’ll set up a virtual environment to build the proj - ect in. Writing a Spec A full spec details the project goals, describes the project’s functionality, and discusses its appearance and user interface. Like any good project or business plan, a spec should keep you focused and help keep your project on track. We won’t write a full project spec here, but we’ll lay out a few clear goals to keep our development process focused. Here’s the spec we’ll use: We’ll write a web app called Learning Log that allows users to log the topics they’re interested in and to make journal entries as they learn about each topic. The Learning Log home page should describe the site and invite users to either register or log in. Once logged in, a user should be able to create new topics, add new entries, and read and edit existing entries. When you learn about a new topic, keeping a journal of what you’ve learned can be helpful in tracking and revisiting information. A good app makes this process efficient. Creating a Virtual Environment To work with Django, we’ll first set up a virtual environment to work in. A virtual environment is a place on your system where you can install packages and isolate them from all other Python packages. Separating one project’s libraries from other projects is beneficial and will be necessar y when we deploy Learning Log to a ser ver in Chapter 20. Create a new director y for your project called learning_log , switch to that director y in a terminal, and create a virtual environment. If you’re using Python 3, you should be able to create a virtual environment with the follow ing command: learning_log$ python -m venv ll_env
learning_log$Here we’re running the venv module and using it to create a virtual environment named ll _env . If this works, move on to “Activating the Virtual Environment” on page 399. If it doesn’t work, read the next section, “Installing virtualenv.” Getting Started with Django 399 Installing virtualenv If you’re using an earlier version of Python or if your system isn’t set up to use the venv module correctly, you can install the virtualenv package. To install virtualenv, enter the following:$ pip install --user virtualenv
Keep in mind that you might need to use a slightly different version of
this command. (If you haven’t used pip yet, see “Installing Python Packages
with pip” on page 237.)
note If you’re using Linux and this still doesn’t work, you can install virtualenv through
your system’s package manager. On Ubuntu, for example, the command
sudo apt-get
install python-virtualenv
will install virtualenv.
Change to the learning_log director y in a terminal, and create a virtual
environment like this:
learning_log$virtualenv ll_env New python executable in ll_env/bin/python Installing setuptools, pip...done. learning_log$
note If you have more than one version of Python installed on your system, you should
specify the version for virtualenv to use. For example, the command
virtualenv
ll_env --python=python3
will create a virtual environment that uses Python 3.
Activating the Virtual Environment
Now that we have a virtual environment set up, we need to activate it with
learning_log$source ll_env/bin/activate u (ll_env)learning_log$
This command runs the script activate in ll _env/bin. W hen the env i -
ronment is active, you’ll see the name of the environment in parentheses,
as shown at u ; then you can install packages to the environment and use
packages that have already been installed. Packages you install in ll _env will
be available only while the environment is active.
note If you’re using Windows, use the command ll_env\Scripts\activate (without the
word
source ) to activate the virtual environment.
To stop using a virtual environment, enter
deactivate :
(ll_env)learning_log$deactivate learning_log$

400 Chapter 18
The environment will also become inactive if you close the terminal it’s
running in.
Installing Django
Once you’ve created your virtual environment and activated it, install Django:
(ll_env)learning_log$pip install Django Installing collected packages: Django Successfully installed Django Cleaning up... (ll_env)learning_log$
Because we’re working in a virtual environment, this command is the
same on all systems. There’s no need to use the
--user flag, and there’s no
need to use longer commands like
python -m pip install package_name .
Keep in mind that Django will be available only when the environment
is active.
Creating a Project in Django
Without leaving the active virtual environment (remember to look for ll _env
in parentheses), enter the following commands to create a new project:
u (ll_env)learning_log$django-admin.py startproject learning_log . v (ll_env)learning_log$ ls
learning_log ll_env manage.py
w (ll_env)learning_log$ls learning_log __init__.py settings.py urls.py wsgi.py The command at u tells Django to set up a new project called learning_log . The dot at the end of the command creates the new project with a director y structure that will make it easy to deploy the app to a ser ver when we’re finished developing it. note Don’t forget this dot, or you may run into some configuration issues when we deploy the app. If you forget the dot, delete the files and folders that were created (except ll_env ), and run the command again. Running the ls command ( dir on Windows) v shows that Django has created a new director y called learning_log . It also created a file called manage.py , which is a short program that takes in commands and feeds them to the relevant part of Django to run them. We’ll use these com - mands to manage tasks like working with databases and running ser vers. The learning_log director y contains four files w , the most important of which are settings.py , url s.py, and wsg i.py . The settings.py file controls how Django interacts with your system and manages your project. We’ll modify a few of these settings and add some settings of our own as the project Getting Started with Django 401 evolves. The url s.py file tells Django which pages to build in response to browser requests. The wsg i.py file helps Django ser ve the files it creates. The filename is an acronym for web server gateway interface . Creating the Database Because Django stores most of the information related to a project in a database, we need to create a database that Django can work with. To create the database for the Learning Log project, enter the following command (still in an active environment): (ll_env)learning_log$ python manage.py migrate
u Operations to perform: Synchronize unmigrated apps: messages, staticfiles
Apply all migrations: contenttypes, sessions, auth, admin
--snip--
Applying sessions.0001_initial... OK
v (ll_env)learning_log$ls db.sqlite3 learning_log ll_env manage.py Any time we modify a database, we say we’re migrating the database. Issuing the migrate command for the first time tells Django to make sure the database matches the current state of the project. The first time we run this command in a new project using SQLite (more about SQLite in a moment), Django will create a new database for us. At u Django reports that it will make the database tables needed to store the information we’ll use in this project ( Synchronize unmigrated apps ), and then make sure the database structure matches the current code ( Apply all migrations). Running the ls command shows that Django created another file called db.sqlite3 v. SQLite is a database that runs off a single file; it’s ideal for writ - ing simple apps because you won’t have to pay much attention to managing the database. Viewing the Project Let’s make sure that Django has set up the project properly. Enter the runserver command as follows: (ll_env)learning_log$ python manage.py runserver
Performing system checks...
u System check identified no issues (0 silenced). July 15, 2015 - 06:23:51
v Django version 1.8.4, using settings 'learning_log.settings'
w Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Django starts a ser ver so you can view the project on your system to see
how well it works. When you request a page by entering a UR L in a browser,
the Django ser ver responds to that request by building the appropriate
page and sending that page to the browser.

402 Chapter 18
At u Django checks to make sure the project is set up properly; at v
it reports the version of Django in use and the name of the settings file
being used; and at w it reports the UR L where the project is being ser ved.
The UR L http://127.0.0.1:8000/ indicates that the project is listening for
requests on port 8000 on your computer—called a localhost. The term
localhost refers to a ser ver that only processes requests on your system; it
doesn’t allow anyone else to see the pages you’re developing. Now open a web browser and enter the UR L http://localhost:8000/ , or
http://127.0.0.1:8000/ if the first one doesn’t work. You should see something
like Figure 18 -1, a page that Django creates to let you know all is working
properly so far. Keep the ser ver running for now, but when you want to stop
the ser ver you can do so by pressing
ctr l -C.
Figure 18-1: Everything is working so far.
note If you receive the error message That port is already in use , tell Django to use a
different port by entering
python manage.py runserver 8001 and cycle through higher
numbers until you find an open port.
t ry It y ourself
18-1. New Projects: To get a better idea of what Django does, build a couple
of empty projects and look at what it creates . Make a new folder with a simple
name, like InstaBook or FaceGram (outside of your learning _log directory),
navigate to that folder in a terminal, and create a virtual environment . Install
Django, and run the command
(make sure you include the dot at the end of the command) .
Look at the files and folders this command creates, and compare them to
Learning Log . Do this a few times until you’re familiar with what Django creates
when starting a new project . Then delete the project directories if you wish .

Getting Started with Django 403
starting an a pp
A Django project is organized as a group of individual apps that work together
to make the project work as a whole. For now, we’ll create just one app to
do most of the work for our project. We’ll add another app to manage user
accounts in Chapter 19. You should still be running
runserver in the terminal window you
opened earlier. Open a new terminal window (or tab) and navigate to the
director y that contains manage.py . Activate the virtual environment, and
then run the
startapp command:
learning_log$source ll_env/bin/activate (ll_env)learning_log$ python manage.py startapp learning_logs
u (ll_env)learning_log$ls db.sqlite3 learning_log learning_logs ll_env manage.py v (ll_env)learning_log$ ls learning_logs/ admin.py __init__.py migrations models.py tests.py views.py
The command startapp appname tells Django to create the infrastructure
needed to build an app. If you look in the project director y now, you’ll see
a new folder called learning_logs u. Open that folder to see what Django has
created v. The most important files are model s.py , admin.py , and views.py .
We’ll use model s.py to define the data we want to manage in our app . We’ l l
get to admin.py and views.py a little later.
Defining Models
Let’s think about our data for a moment. Each user will need to create a
number of topics in their learning log. Each entr y they make will be tied to
a topic, and these entries will be displayed as text. We’ll also need to store
the timestamp of each entr y so we can show users when they made each
ent r y.
Open the file model s.py , and look at its existing content:
models.py from django.db import models
A module called models is being imported for us, and we’re being invited
to create models of our own. A model tells Django how to work with the data
that will be stored in the app. Code-wise, a model is just a class; it has attri -
butes and methods, just like ever y class we’ve discussed. Here’s the model
for the topics users will store:
from django.db import models
class Topic(models.Model):
"""A topic the user is learning about"""
u text = models.CharField(max_length=200)

404 Chapter 18
w def __str__(self): """Return a string representation of the model."""
return self.text
We’ve created a class called Topic , which inherits from Model —a parent
class included in Django that defines the basic functionality of a model.
Only two attributes are in the
Topic class: text and date_added .
The
text attribute is a CharField —a piece of data that’s made up of
characters, or text u . You use
CharField when you want to store a small
amount of text, such as a name, a title, or a city. When we define a
CharField
attribute, we have to tell Django how much space it should reser ve in the
database. Here we give it a
max_length of 200 characters, which should be
enough to hold most topic names. The
date_added attribute is a DateTimeField —a piece of data that will
record a date and time v . We pass the argument
tells Django to automatically set this attribute to the current date and time
whenever the user creates a new topic.
note To see the different kinds of fields you can use in a model, see the Django Model
Field Reference at https://docs.djangoproject.com/en/1.8/ref/models/
fields/ . You won’t need all the information right now, but it will be extremely useful
when you’re developing your own apps.
We need to tell Django which attribute to use by default when it dis -
plays information about a topic. Django calls a
__str__() method to display
a simple representation of a model. Here we’ve written a
__str__() method
that returns the string stored in the
text attribute w .
note If you’re using Python 2.7, you should call the __str__() method __unicode__()
instead. The body of the method is identical.
Activating Models
To use our models, we have to tell Django to include our app in the overall
project. Open settings.py (in the learning_log/learning_log director y), and
you’ll see a section that tells Django which apps are installed in the project:
settings.py --snip--
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
)
--snip--

Getting Started with Django 405
This is just a tuple, telling Django which apps work together to make up
the project. Add our app to this tuple by modifying
INSTALLED_APPS so it looks
like this:
--snip--
INSTALLED_APPS = (
--snip--
'django.contrib.staticfiles',

# My apps
'learning_logs',
)
--snip--
Grouping apps together in a project helps to keep track of them as the
project grows to include more apps. Here we start a section caled My apps ,
which includes only
learning_logs for now.
Next, we need to tell Django to modify the database so it can store
information related to the model
Topic . From the terminal, run the follow -
ing command:
(ll_env)learning_log$python manage.py makemigrations learning_logs Migrations for 'learning_logs': 0001_initial.py: - Create model Topic (ll_env)learning_log$
The command makemigrations tells Django to figure out how to modify
the database so it can store the data associated with any new models we’ve
defined. The output here shows that Django has created a migration file
called 0001_initial.py . This migration will create a table for the model
Topic
in the database. Now we’ll apply this migration and have Django modify the database
for us:
(ll_env)learning_log$python manage.py migrate --snip-- Running migrations: Rendering model states... DONE u Applying learning_logs.0001_initial... OK Most of the output from this command is identical to the output from the first time we issued the migrate command. The line we need to check appears at u , where Django confirms that ever ything worked OK when it applied the migration for learning_logs . Whenever we want to modify the data that Learning Log manages, we’ll follow these three steps: modify model s.py , call makemigrations on learning_logs , and tell Django to migrate the project. 406 Chapter 18 The Django Admin Site When you define models for an app, Django makes it easy for you to work with your models through the admin site . A site’s administrators use the admin site, not a site’s general users. In this section, we’ll set up the admin site and use it to add some topics through the Topic model. Setting Up a Superuser Django allows you to create a user who has all privileges available on the site, called a superuser . A privilege controls the actions a user can take. The most restrictive privilege settings allow a user to only read public informa - tion on the site. Registered users typically have the privilege of reading their own private data and some selected information available only to members. To effectively administer a web application, the site owner usu - ally needs access to all information stored on the site. A good administrator is careful with their users’ sensitive information, because users put a lot of trust into the apps they access. To create a superuser in Django, enter the following command and respond to the prompts: (ll_env)learning_log$ python manage.py createsuperuser
Superuser created successfully.
(ll_env)learning_log$When you issue the command createsuperuser , Django prompts you to enter a username for the superuser u . Here we’re using ll_admin, but you can enter any username you want. You can enter an email address if you want or just leave this field blank v . You’ll need to enter your password twice w . note Some sensitive information can be hidden from a site’s administrators. For example, Django doesn’t actually store the password you enter; instead, it stores a string derived from the password, called a hash . Each time you enter your password, Django hashes your entry and compares it to the stored hash. If the two hashes match, you’re authenticated. By requiring hashes to match, if an attacker gains access to a site’s database, they’ll be able to read its stored hashes but not the passwords. When a site is set up properly, it’s almost impossible to get the original passwords from the hashes. r egistering a Model with the a dmin Site Django includes some models in the admin site automatically, such as User and Group , but the models we create need to be registered manually. Getting Started with Django 407 When we started the learning_logs app, Django created a file called admin.py in the same director y as model s.py : admin.py from django.contrib import admin # Register your models here. To register Topic with the admin site, enter: from django.contrib import admin u from learning_logs.models import Topic v admin.site.register(Topic) This code imports the model we want to register, Topic u , and then uses admin.site.register() v to tell Django to manage our model through the admin site. Now use the superuser account to access the admin site. Go to h t t p :// localhost :8000/admin/ , enter the username and password for the superuser you just created, and you should see a screen like the one in Figure 18 -2. This page allows you to add new users and groups and change existing ones. We can also work with data related to the Topic model that we just defined. Figure 18-2: The admin site with Topic included note If you see a message in your browser that the web page is not available, make sure you still have the Django server running in a terminal window. If you don’t, activate a virtual environment and reissue the command python manage.py runserver . a dding t opics Now that Topic has been registered with the admin site, let’s add our first topic. Click To p i c s to go to the Topics page, which is mostly empty, because we have no topics to manage yet. Click Add , and you’ll see a form for adding 408 Chapter 18 a new topic. Enter Chess in the first box and click Save. You’ll be sent back to the Topics admin page, and you’ll see the topic you just created. Let’s create a second topic so we’ll have more data to work with. Click Add again, and create a second topic, Rock Climbing . When you click Save , you’ll be sent back to the main Topics page again, and you’ll see both Chess and Rock Climbing listed. Defining the Entry Model To record what we’ve been learning about chess and rock climbing, we need to define a model for the kinds of entries users can make in their learning logs. Each entr y needs to be associated with a particular topic. This rela - tionship is called a many-to-one relationship , meaning many entries can be associated with one topic. Here’s the code for the Entry model: models.py from django.db import models class Topic(models.Model): --snip-- u class Entry(models.Model): """Something specific learned about a topic""" v topic = models.ForeignKey(Topic) w text = models.TextField() date_added = models.DateTimeField(auto_now_add=True) x class Meta: verbose_name_plural = 'entries' def __str__(self): """Return a string representation of the model.""" y return self.text[:50] + "..." The Entry class inherits from Django’s base Model class, just as Topic did u. The first attribute, topic , is a ForeignKey instance v . A foreign key is a database term; it’s a reference to another record in the database. This is the code that connects each entr y to a specific topic. Each topic is assigned a key, or ID, when it’s created. When Django needs to establish a connection between two pieces of data, it uses the key associated with each piece of information. We’ll use these connections shortly to retrieve all the entries associated with a certain topic. Next is an attribute called text , which is an instance of TextField w. This kind of field doesn’t need a size limit, because we don’t want to limit the size of individual entries. The date_added attribute allows us to present entries in the order they were created and to place a timestamp next to each entr y. At x we nest the Meta class inside our Entry class. Meta holds extra infor - mation for managing a model; here it allows us to set a special attribute telling Django to use Entries when it needs to refer to more than one entr y. Getting Started with Django 409 (Without this, Django would refer to multiple entries as Entrys.) Finally, the __str__() method tells Django which information to show when it refers to individual entries. Because an entr y can be a long body of text, we tell Django to show just the first 50 characters of text y . We also add an ellipsis to clarify that we’re not always displaying the entire entr y. Migrating the Entry Model Because we’ve added a new model, we need to migrate the database again. This process will become quite familiar: you modify model s.py , run the com- mand python manage.py makemigrations app_name , and then run the command python manage.py migrate . Migrate the database and check the output: (ll_env)learning_log$ python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
u 0002_entry.py: - Create model Entry
(ll_env)learning_log$python manage.py migrate Operations to perform: --snip-- v Applying learning_logs.0002_entry... OK A new migration called 0002_entry.py is generated, which tells Django how to modify the database to store information related to the model Entry u . When we issue the migrate command, we see that Django applied this migration, and ever ything was okay v . Registering Entry with the Admin Site We also need to register the Entry model. Here’s what admin.py should look like now: admin.py from django.contrib import admin from learning_logs.models import Topic, Entry admin.site.register(Topic) admin.site.register(Entry) Go back to http://localhost/admin/ , and you should see Entries listed under learning_logs . Click the Add link for Entries, or click Entries , and then choose Add entr y . You should see a drop-down list to select the topic you’re creating an entr y for and a text box for adding an entr y. Select Chess from the drop-down list, and add an entr y. Here’s the first entr y I made: The opening is the first part of the game, roughly the first ten moves or so. In the opening, it’s a good idea to do three things— bring out your bishops and knights, tr y to control the center of the board, and castle your king. 410 Chapter 18 Of course, these are just guidelines. It will be important to learn when to follow these guidelines and when to disregard these suggestions. When you click Save, you’ll be brought back to the main admin page for entries. Here you’ll see the benefit of using text[:50] as the string rep - resentation for each entr y; it’s much easier to work with multiple entries in the admin interface if you see only the first part of an entr y rather than the entire text of each entr y. Make a second entr y for Chess and one entr y for Rock Climbing so we have some initial data. Here’s a second entr y for Chess: In the opening phase of the game, it’s important to bring out your bishops and knights. These pieces are powerful and maneu - verable enough to play a significant role in the beginning moves of a game. And here’s a first entr y for Rock Climbing: One of the most important concepts in climbing is to keep your weight on your feet as much as possible. There’s a myth that climbers can hang all day on their arms. In reality, good climbers have practiced specific ways of keeping their weight over their feet whenever possible. These three entries will give us something to work with as we continue to develop Learning Log. The Django Shell Now that we’ve entered some data, we can examine that data programmati - cally through an interactive terminal session. This interactive environment is called the Django shell , and it’s a great environment for testing and trouble - shooting your project. Here’s an example of an interactive shell session: (ll_env)learning_log$ python manage.py shell
u >>> from learning_logs.models import Topic >>> Topic.objects.all()
[, ]
The command python manage.py shell (run in an active virtual environ -
ment) launches a Python interpreter that you can use to explore the data
stored in your project’s database. Here we import the model
Topic from the
learning_logs.models module u . We then use the method Topic.objects.all()
to get all of the instances of the model
Topic ; the list that’s returned is called
a queryset .
We can loop over a quer yset just as we’d loop over a list. Here’s how you
can see the ID that’s been assigned to each topic object:
>>> topics = Topic.objects.all()
>>> for topic in topics:

Getting Started with Django 411
... print(topic.id, topic)
...
1 Chess
2 Rock Climbing
We store the quer yset in topics , and then print each topic’s id attribute
and the string representation of each topic. We can see that Chess has an
ID of 1, and Rock Climbing has an ID of 2. If you know the ID of a particular object, you can get that object and
examine any attribute the object has. Let’s look at the
values for Chess:
>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
datetime.datetime(2015, 5, 28, 4, 39, 11, 989446, tzinfo=)
We can also look at the entries related to a certain topic. Earlier we
defined the
topic attribute for the Entry model. This was a ForeignKey , a con -
nection between each entr y and a topic. Django can use this connection to
get ever y entr y related to a certain topic, like this:
u >>> t.entry_set.all()
[, In
the opening phase of the game, it's important t...>]
To get data through a foreign key relationship, you use the lowercase
name of the related model followed by an underscore and the word
set u .
For example, say you have the models
Pizza and Topping , and Topping is
related to
Pizza through a foreign key. If your object is called my_pizza ,
representing a single pizza, you can get all of the pizza’s toppings using
the code
my_pizza.topping_set.all() .
We’ll use this kind of syntax when we begin to code the pages users
can request. The shell is ver y useful for making sure your code retrieves
the data you want it to. If your code works as you expect it to in the shell,
you can expect it to work properly in the files you write within your project.
If your code generates errors or doesn’t retrieve the data you expect it to,
it’s much easier to troubleshoot your code in the simple shell environment
than it is within the files that generate web pages. We won’t refer to the shell
much, but you should continue using it to practice working with Django’s
syntax for accessing the data stored in the project.
note Each time you modify your models, you’ll need to restart the shell to see the effects of
those changes. To exit a shell session, enter
ctr l -D; on Windows enter ctr l -Z and
then press
enter .

412 Chapter 18
try It y ourself
18-2. Short Entries: The __str__() method in the Entry model currently appends
an ellipsis to every instance of
Entry when Django shows it in the admin site
or the shell . Add an
if statement to the __str__() method that adds an ellipsis
only if the entry is more than 50 characters long . Use the admin site to add an
entry that’s fewer than 50 characters in length, and check that it doesn’t have
an ellipsis when viewed .
18-3. The Django API: When you write code to access the data in your project,
you’re writing a query . Skim through the documentation for querying your data
at https://docs.djangoproject.com/en/1.8/topics/db/queries/. Much of what
you see will look new to you, but it will be quite useful as you start to work on
18-4. Pizzeria: Start a new project called
pizzeria with an app called pizzas .
Define a model
Pizza with a field called name , which will hold name values
such as
Hawaiian and Meat Lovers . Define a model called Topping with fields
called
pizza and name . The pizza field should be a foreign key to Pizza , and
name should be able to hold values such as pineapple , Canadian bacon , and
sausage .
Register both models with the admin site, and use the site to enter some
pizza names and toppings . Use the shell to explore the data you entered .
Usually, making web pages with Django consists of three stages: defining
UR Ls, writing views, and writing templates. First, you must define patterns
for UR Ls. A UR L pattern describes the way the UR L is laid out and tells
Django what to look for when matching a browser request with a site UR L
so it knows which page to return.
Each UR L then maps to a particular view —the view function retrieves
and processes the data needed for that page. The view function often calls a
template , which builds a page that a browser can read. To see how this works,
let’s make the home page for Learning Log. We’ll define the UR L for the
home page, write its view function, and create a simple template. Because all we’re doing is making sure Learning Log works as it’s sup -
posed to, we’ll keep the page simple for now. A functioning web app is fun
to style when it’s complete; an app that looks good but doesn’t work well
is pointless. For now, the home page will display only a title and a brief
description.

Getting Started with Django 413
Mapping a URL
Users request pages by entering UR Ls into a browser and clicking links,
so we’ll need to decide what UR Ls are needed in our project. The home
page UR L is first: it’s the base UR L people use to access the project. At the
moment, the base UR L, http://localhost:8000/, returns the default Django
site that lets us know the project was set up correctly. We’ll change this by
mapping the base UR L to Learning Log’s home page. In the main learning_log project folder, open the file url s.py . Here’s the
code you’ll see:
urls.py u from django.conf.urls import include, url
v urlpatterns = [
The first two lines import the functions and modules that manage
UR Ls for the project and admin site u . The body of the file defines the
urlpatterns variable v. In this url s.py file, which represents the project as a
whole, the
urlpatterns variable includes sets of UR Ls from the apps in the
project. The code at w includes the module
all the UR Ls that can be requested from the admin site. We need to include the UR Ls for
learning_logs :
from django.conf.urls import include, url
urlpatterns = [
u url(r'', include('learning_logs.urls', namespace='learning_logs')), ]
We’ve added a line to include the module learning_logs.urls at u .
This line includes a
namespace argument, which allows us to distinguish
learning_logs ’s UR Ls from other UR Ls that might appear in the project,
which can be ver y helpful as your project starts to grow. The default url s.py is in the learning_log folder; now we need to make a
second url s.py file in the learning_logs folder:
urls.py u """Defines URL patterns for learning_logs."""
v from django.conf.urls import url
w from . import views
y url(r'^$', views.index, name='index'), ] 414 Chapter 18 To make it clear which url s.py we’re working in, we add a docstring at the beginning of the file u . We then import the url function, which is needed when mapping UR Ls to views v . We also import the views mod- ule w; the dot tells Python to import views from the same director y as the current url s.py module. The variable urlpatterns in this module is a list of individual pages that can be requested from the learning_logs app x . The actual UR L pattern is a call to the url() function, which takes three arguments y . The first is a regular expression . Django will look for a regular expression in urlpatterns that matches the requested UR L string. Therefore, a regular expression will define the pattern that Django can lo ok for. Let’s look at the regular expression r'^$' . The r tells Python to inter -
pret the following string as a raw string, and the quotes tell Python where
the regular expression begins and ends. The caret (
^) tells Python to
find the beginning of the string, and the dollar sign tells Python to look
for the end of the string. In its entirety, this expression tells Python to
look for a UR L with nothing between the beginning and end of the UR L.
Python ignores the base UR L for the project ( http://localhost:8000/), so an
empty regular expression matches the base UR L. Any other UR L will not
match this expression, and Django will return an error page if the UR L
requested doesn’t match any existing UR L patterns. The second argument in
url() at y specifies which view function
to call. When a requested UR L matches the regular expression, Django
will call
views.index (we’ll write this view function in the next section).
The third argument provides the name
index for this UR L pattern so we
can refer to it in other sections of the code. Whenever we want to provide
note Regular expressions, often called regexes , are used in almost every programming
language. They’re incredibly useful, but they take some practice to get used to. If you
didn’t follow all of this, don’t worry; you’ll see plenty of examples as you work through
this project.
Writing a View
A view function takes in information from a request, prepares the data
needed to generate a page, and then sends the data back to the browser,
often by using a template that defines what the page will look like. The file views.py in learning_logs was generated automatically when
we ran the command
python manage.py startapp . Here’s what’s in views.py
r ight now:
views.py from django.shortcuts import render

Getting Started with Django 415
Currently, this file just imports the render() function, which renders the
response based on the data provided by views. The following code is how
from django.shortcuts import render
def index(request):
return render(request, 'learning_logs/index.html')
When a UR L request matches the pattern we just defined, Django will
look for a function called
index() in the views.py file. Django then passes the
request object to this view function. In this case, we don’t need to process
any data for the page, so the only code in the function is a call to
render() .
The
render() function here uses two arguments—the original request object
and a template it can use to build the page. Let’s write this template.
Writing a Template
A template sets up the structure for a web page. The template defines
what the page should look like, and Django fills in the relevant data each
time the page is requested. A template allows you to access any data pro -
vided by the view. Because our view for the home page provided no data,
this template is fairly simple. Inside the learning_logs folder, make a new folder called templates .
Inside the templates folder, make another folder called learning_logs . This
might seem a little redundant (we have a folder named learning_logs inside
a folder named templates inside a folder named learning_logs ), but it sets
up a structure that Django can interpret unambiguously, even in the con -
text of a large project containing many individual apps. Inside the inner
learning_logs folder, make a new file called index.html . Write the following
into that file:
index.html

Learning Log

Learning Log helps you keep track of your learning, for any topic you
're

This is a ver y simple file. If you’re not familiar with HTML, the

tags signify paragraphs. The

tag opens a paragraph, and the

tag
closes a paragraph. We have two paragraphs: the first acts as a title, and the
second describes what users can do with Learning Log. Now when we request the project’s base UR L, http://localhost:8000/ ,
we’ll see the page we just built instead of the default Django page. Django
will take the requested UR L, and that UR L will match the pattern
r'^$' ; then Django will call the function views.index() , and this will render the page using the template contained in index.html . The resulting page is shown in Figure 18 -3. 416 Chapter 18 Figure 18-3: The home page for Learning Log Although it may seem a complicated process for creating one page, this separation between UR Ls, views, and templates actually works well. It allows you to think about each aspect of a project separately, and in larger projects it allows individuals to focus on the areas in which they’re strongest. For example, a database specialist can focus on the models, a programmer can focus on the view code, and a web designer can focus on the templates. t ry It y ourself 18-5. Meal Planner: Consider an app that helps people plan their meals throughout the week . Make a new folder called meal_planner , and start a new Django project inside this folder . Then make a new app called meal_plans . Make a simple home page for this project . 18-6. Pizzeria Home Page: Add a home page to the Pizzeria project you started in Exercise 18-4 (page 412) . Building additional Pages Now that we’ve established a routine for building a page, we can start to build out the Learning Log project. We’ll build two pages that display data: a page that lists all topics and a page that shows all the entries for a particu - lar topic. For each of these pages, we’ll specify a UR L pattern, write a view function, and write a template. But before we do this, we’ll create a base template that all templates in the project can inherit from. Template Inheritance When building a website, you’ll almost always require some elements to be repeated on each page. Rather than writing these elements directly into each page, you can write a base template containing the repeated elements Getting Started with Django 417 and then have each page inherit from the template. This approach lets you focus on developing the unique aspects of each page and makes it much easier to change the overall look and feel of the project. the Parent t emplate We’ll start by creating a template called base.html in the same director y as index.html . This file will contain elements common to all pages; ever y other template will inherit from base.html . The only element we want to repeat on each page right now is the title at the top. Because we’ll include this tem - plate on ever y page, let’s make the title a link to the home page : base.html v {% block content %}{% endblock content %} The first part of this file creates a paragraph containing the name of the project, which also acts as a link to the home page. To generate a link, we use a template tag , indicated by braces and percent signs {% %} . A template tag is a bit of code that generates information to be displayed on a page. In this example, the template tag {% url 'learning_logs:index' %} generates a UR L matching the UR L pattern defined in learning_logs/urls.py with the name 'index' u . In this example, learning_logs is the namespace and index is a uniquely named UR L pattern in that namespace. In a simple HTML page, a link is surrounded by the anchor tag: link text Having the template tag generate the UR L for us makes it much eas - ier to keep our links up to date. To change a UR L in our project, we only need to change the UR L pattern in url s.py , and Django will automatically insert the updated UR L the next time the page is requested. Ever y page in our project will inherit from base.html , so from now on ever y page will have a link back to the home page. At v we insert a pair of block tags. This block, named content , is a place - holder; the child template will define the kind of information that goes in the content block. A child template doesn’t have to define ever y block from its parent, so you can reser ve space in parent templates for as many blocks as you like, and the child template uses only as many as it requires. note In Python code, we almost always indent four spaces. Template files tend to have more levels of nesting than Python files, so it’s common to use only two spaces for each indentation level. 418 Chapter 18 the Child t emplate Now we need to rewrite index.html to inherit from base.html. Here’s index.html: index.html u {% extends "learning_logs/base.html" %} v {% block content %} Learning Log helps you keep track of your learning, for any topic y ou're learning about. w {% endblock content %} If you compare this to the original index.html , you can see that we’ve replaced the Learning Log title with the code for inheriting from a parent template u . A child template must have an {% extends %} tag on the first line to tell Django which parent template to inherit from. The file base.html is part of learning_logs , so we include learning_logs in the path to the par- ent template. This line pulls in ever ything contained in the base.html tem- plate and allows index.html to define what goes in the space reser ved by the content block. We define the content block at v by inserting a {% block %} t ag w ith the name content . Ever ything that we aren’t inheriting from the parent template goes inside a content block. Here, that’s the paragraph describing the Learning Log project. At w we indicate that we’re finished defining the content by using an {% endblock content %} t ag. You can start to see the benefit of template inheritance: in a child tem - plate we only need to include content that’s unique to that page. This not only simplifies each template, but also makes it much easier to modify the site. To modify an element common to many pages, you only need to mod - ify the element in the parent template. Your changes are then carried over to ever y page that inherits from that template. In a project that includes tens or hundreds of pages, this structure can make it much easier and faster to improve your site. note In a large project, it’s common to have one parent template called ba se.ht ml for the entire site and parent templates for each major section of the site. All the section templates inherit from ba se.ht ml , and each page in the site inherits from a section template. This way you can easily modify the look and feel of the site as a whole, any section in the site, or any individual page. This configuration provides a very effi - cient way to work, and it encourages you to steadily update your site over time. The Topics Page Now that we have an efficient approach to building pages, we can focus on our next two pages: the general topics page and the page to display entries for a single topic. The topics page will show all topics that users have created, and it’s the first page that will involve working with data. Getting Started with Django 419 the t opics U rL Pattern First, we define the UR L for the topics page. It’s common to choose a simple UR L fragment that reflects the kind of information presented on the page. We’ll use the word topics , so the UR L http://localhost:8000/topics/ will return this page. Here’s how we modify learning_logs/ url s.py: urls.py """Defines URL patterns for learning_logs.""" --snip-- urlpatterns = [ # Home page url(r'^$', views.index, name='index'),
# Show all topics.
u url(r'^topics/$', views.topics, name='topics'), ] We’ve simply added topics/ into the regular expression argument used for the home page UR L u . When Django examines a requested UR L, this pattern will match any UR L that has the base UR L followed by topics . You can include or omit a for ward slash at the end, but there can’t be anything else after the word topics , or the pattern won’t match. Any request with a UR L that matches this pattern will then be passed to the function topics() in views.py . the t opics View The topics() function needs to get some data from the database and send it to the template. Here’s what we need to add to views.py : views.py from django.shortcuts import render u from .models import Topic def index(request): --snip-- v def topics(request): """Show all topics.""" w topics = Topic.objects.order_by('date_added') x context = {'topics': topics} y return render(request, 'learning_logs/topics.html', context) We first import the model associated with the data we need u . The topics() function needs one parameter: the request object Django received from the ser ver v . At w we quer y the database by asking for the Topic objects, sorted by the date_added attribute. We store the resulting quer yset in topics . 420 Chapter 18 At x we define a context that we’ll send to the template. A context is a dictionar y in which the keys are names we’ll use in the template to access the data and the values are the data we need to send to the template. In this case, there’s one key-value pair, which contains the set of topics we’ll display on the page. When building a page that uses data, we pass the context vari - able to render() as well as the request object and the path to the template y . t he top ics tem p l ate The template for the topics page receives the context dictionar y so the tem - plate can use the data that topics() provides. Make a file called topics.html in the same director y as index.html . Here’s how we can display the topics in the template: topics.html {% extends "learning_logs/base.html" %} {% block content %} Topics u v {% for topic in topics %} w • {{ topic }} • x {% empty %} • No topics have been added yet. • y {% endfor %} z {% endblock content %} We start by using the {% extends %} tag to inherit from base.html , just as the index template does, and then open a content block. The body of this page contains a bulleted list of the topics that have been entered. In stan - dard HTML, a bulleted list is called an unordered list , indicated by the tags . We begin the bulleted list of topics at u. At v we have another template tag equivalent to a for loop, which loops through the list topics from the context dictionar y. The code used in tem - plates differs from Python in some important ways. Python uses indentation to indicate which lines of a for statement are part of a loop. In a template, ever y for loop needs an explicit {% endfor %} tag indicating where the end of the loop occurs. So in a template, you’ll see loops written like this: {% for item in list %} do something with each item {% endfor %} Inside the loop, we want to turn each topic into an item in the bulleted list. To print a variable in a template, wrap the variable name in double Getting Started with Django 421 braces. The code {{ topic }} at w will be replaced by the value of topic on each pass through the loop. The braces won’t appear on the page; they just indicate to Django that we’re using a template variable. The HTML tag • indicates a list item. Anything between these tags, inside a pair of tags, will appear as a bulleted item in the list. At x we use the {% empty %} template tag, which tells Django what to do if there are no items in the list. In this case, we print a message informing the user that no topics have been added yet. The last two lines close out the for loop y and then close out the bulleted list z . Now we need to modify the base template to include a link to the topics page: base.html {% block content %}{% endblock content %} We add a dash after the link to the home page u , and then we add a link to the topics page, using the UR L template tag again v . This line tells Django to generate a link matching the UR L pattern with the name 'topics' in learning_logs/urls.py . Now when you refresh the home page in your browser, you’ll see a Topics link. When you click the link, you’ll see a page that looks similar to Figure 18 - 4. Figure 18- 4: The topics page Individual Topic Pages Next, we need to create a page that can focus on a single topic, showing the topic name and all the entries for that topic. We’ll again define a new UR L pattern, write a view, and create a template. We’ll also modify the topics page so each item in the bulleted list links to its corresponding topic page. 422 Chapter 18 the t opic U rL Pattern The UR L pattern for the topic page is a little different than the other UR L patterns we’ve seen so far because it will use the topic’s id attribute to indi - cate which topic was requested. For example, if the user wants to see the detail page for the topic Chess, where the id is 1, the UR L will be h t t p :// localhost:8000/topics/1/ . Here’s a pattern to match this UR L, which goes in learning_logs/urls.py : urls.py --snip-- urlpatterns = [ --snip-- # Detail page for a single topic url(r'^topics/(?P\d+)/$', views.topic, name='topic'),
]
Let’s examine the regular expression in this UR L pattern, r'^topics/
(?P\d+)/$' . The r tells Django to interpret the string as a raw string, and the expression is contained in quotes. The second part of the expression, /(?P\d+)/ , matches an integer between two for ward slashes and stores the integer value in an argument called topic_id . The parentheses surrounding this part of the expression captures the value stored in the UR L; the ?P part stores the matched value in topic_id ; and the expression \d+ matches any number of digits that appear between the for ward slashes. When Django finds a UR L that matches this pattern, it calls the view function topic() with the value stored in topic_id as an argument. We’ll use the value of topic_id to get the correct topic inside the function. the t opic View The topic() function needs to get the topic and all associated entries from the database, as shown here: views.py --snip-- u def topic(request, topic_id): """Show a single topic and all its entries.""" v topic = Topic.objects.get(id=topic_id) w entries = topic.entry_set.order_by('-date_added') x context = {'topic': topic, 'entries': entries} y return render(request, 'learning_logs/topic.html', context) This is the first view function that requires a parameter other than the request object. The function accepts the value captured by the expression (?P\d+) and stores it in topic_id u . At v we use get() to retrieve the topic, just as we did in the Django shell. At w we get the entries associated with this topic, and we order them according to date_added : the minus sign in front of date_added sorts the results in reverse order, which will display the most recent entries first. We store the topic and entries in the context dic - tionar y x and send context to the template topic.html y. Getting Started with Django 423 note The code phrases at v and w are called queries , because they query the database for specific information. When you’re writing queries like these in your own projects, it’s very helpful to try them out in the Django shell first. You’ll get much quicker feed - back in the shell than you will by writing a view and template and then checking the results in a browser. t he top ic tem p l ate The template needs to display the name of the topic and the entries. We also need to inform the user if no entries have been made yet for this topic: topic.html {% extends 'learning_logs/base.html' %} {% block content %} u Topic: {{ topic }} Entries: v w {% for entry in entries %} • x {{ entry.date_added|date:'M d, Y H:i' }} y {{ entry.text|linebreaks }} • z {% empty %} • There are no entries for this topic yet. • {% endfor %} {% endblock content %} We extend base.html , as we do for all pages in the project. Next, we show the topic that’s currently being displayed u , which is stored in the template variable {{ topic }} . The variable topic is available because it’s included in the context dictionar y. We then start a bulleted list to show each of the ent r ies v and loop through them as we did the topics earlier w . Each bullet will list two pieces of information: the timestamp and the full text of each entr y. For the timestamp x , we display the value of the attribute date_added . In Django templates, a vertical line ( |) represents a template filter —a function that modifies the value in a template variable. The filter date:'M d, Y H:i' displays timestamps in the format January 1, 2 015 23 : 0 0 . The next line displays the full value of text rather than just the first 50 characters from entry . The filter linebreaks y ensures that long text entries include line breaks in a format understood by browsers rather than showing a block of uninterrupted text. At z we use the {% empty %} template tag to print a message informing the user that no entries have been made. 424 Chapter 18 Links from the topics Page Before we look at the topic page in a browser, we need to modify the topics template so each topic links to the appropriate page. Here’s the change to topics.html : topics.html --snip-- {% for topic in topics %} • {{ topic }} a> • {% empty %} --snip-- We use the UR L template tag to generate the proper link, based on the UR L pattern in learning_logs with the name 'topic' . This UR L pattern requires a topic_id argument, so we add the attribute topic.id to the UR L template tag. Now each topic in the list of topics is a link to a topic page, such as http://localhost:8000/topics/1/ . If you refresh the topics page and click a topic, you should see a page that looks like Figure 18 -5. Figure 18-5: The detail page for a single topic, showing all entries for a topic t ry It y ourself 18-7. Template Documentation: Skim the Django template documentation at https://docs.djangoproject.com/en/1.8/ref/templates/ . You can refer back to it when you’re working on your own projects . 18-8. Pizzeria Pages: Add a page to the Pizzeria project from Exercise 18- 6 (page 416) that shows the names of available pizzas . Then link each pizza name to a page displaying the pizza’s toppings . Make sure you use template inheritance to build your pages efficiently . Getting Started with Django 425 summary In this chapter you started learning how to build web applications using the Django framework. You wrote a brief project spec, installed Django to a virtual environment, learned to set up a project, and checked that the proj - ect was set up correctly. You learned to set up an app and defined models to represent the data for your app. You learned about databases and how Django helps you migrate your database after you make a change to your models. You learned how to create a superuser for the admin site, and you used the admin site to enter some initial data. You also explored the Django shell, which allows you to work with your project’s data in a terminal session. You learned to define UR Ls, create view functions, and write templates to make pages for your site. Finally, you used template inheritance to simplify the structure of individual templates and to make it easier to modify the site as the project evolves. In Chapter 19 we’ll make intuitive, user-friendly pages that allow users to add new topics and entries and edit existing entries without going through the admin site. We’ll also add a user registration system, allowing users to create an account and to make their own learning log. This is the heart of a web app—the ability to create something that any number of users can interact w it h. 19 user aCCounts At the heart of a web application is the ability for any user, any where in the world, to register an account with your app and start using it. In this chapter you’ll build forms so users can add their own topics and entries, and edit existing entries. You’ll also learn how Django guards against common attacks to form-based pages so you don’t have to spend too much time thinking about securing your apps. We’ll then implement a user authentication system. You’ll build a regis - tration page for users to create accounts, and then restrict access to certain pages to logged-in users only. We’ll then modify some of the view functions so users can only see their own data. You’ll learn to keep your users’ data safe and secure. 428 Chapter 19 allowing u sers to enter data Before we build an authentication system for creating accounts, we’ll first add some pages that allow users to enter their own data. We’ll give users the ability to add a new topic, add a new entr y, and edit their previous ent r ies. Currently, only a superuser can enter data through the admin site. We don’t want users to interact with the admin site, so we’ll use Django’s form- building tools to build pages that allow users to enter data. Adding New Topics Let’s start by giving users the ability to add a new topic. Adding a form- based page works in much the same way as the pages we’ve already built: we define a UR L, write a view function, and write a template. The one major difference is the addition of a new module called for ms.py , which will con- tain the forms. t he t opic ModelForm Any page that lets a user enter and submit information on a web page is a form , even if it doesn’t look like one. When users enter information, we need to validate that the information provided is the right kind of data and not anything malicious, such as code to interrupt our ser ver. We then need to process and save valid information to the appropriate place in the database. Django automates much of this work. The simplest way to build a form in Django is to use a ModelForm , which uses the information from the models we defined in Chapter 18 to auto - matically build a form. Write your first form in the file for ms.py , which you should create in the same director y as model s.py : forms.py from django import forms from .models import Topic u class TopicForm(forms.ModelForm): class Meta: v model = Topic w fields = ['text'] x labels = {'text': ''} We first import the forms module and the model we’ll work with, Topic . At u we define a class called TopicForm , which inherits from forms.ModelForm . The simplest version of a ModelForm consists of a nested Meta class tell - ing Django which model to base the form on and which fields to include in the form. At v we build a form from the Topic model and include only the text field w . The code at x tells Django not to generate a label for the text field. User Accounts 429 the new_topic U rL The UR L for a new page should be short and descriptive, so when the user wants to add a new topic, we’ll send them to http://localhost:8000/new_topic/ . Here’s the UR L pattern for the new_topic page, which we add to learning_logs/ url s.py : urls.py --snip-- urlpatterns = [ --snip-- # Page for adding a new topic url(r'^new_topic/$', views.new_topic, name='new_topic'),
]
This UR L pattern will send requests to the view function new_topic() ,
which we’ll write next.
t he new_topic() View Function
The
new_topic() function needs to handle two different situations: initial
requests for the
new_topic page (in which case it should show a blank form)
and the processing of any data submitted in the form. It then needs to
redirect the user back to the
topics page:
views.py from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from .models import Topic
from .forms import TopicForm
--snip--
def new_topic(request):
u if request.method != 'POST': # No data submitted; create a blank form.
v form = TopicForm() else:
# POST data submitted; process data.
w form = TopicForm(request.POST)
x if form.is_valid():
y form.save()
z return HttpResponseRedirect(reverse('learning_logs:topics
'))
{ context = {'form': form} return render(request, 'learning_logs/new_topic.html', context)
We import the class HttpResponseRedirect , which we’ll use to redirect the
topics page after they submit their topic. The reverse()
function determines the UR L from a named UR L pattern, meaning that
Django will generate the UR L when the page is requested. We also import
the form we just wrote,
TopicForm .

430 Chapter 19
GEt and POS t r equests
The two main types of request you’ll use when building web apps are GET
requests and POST requests. You use GET requests for pages that only read
data from the ser ver. You usually use POST requests when the user needs to
submit information through a form. We’ll be specifying the POST method
for processing all of our forms. (A few other kinds of requests exist, but we
won’t be using them in this project.) The function
new_topic() takes in the request object as a parameter.
When the user initially requests this page, their browser will send a GET
request. When the user has filled out and submitted the form, their browser
will submit a POST request. Depending on the request, we’ll know whether
the user is requesting a blank form (a GET request) or asking us to process
a completed form (a POST request). The test at u determines whether the request method is GET or POST.
If the request method is not POST, the request is probably GET, so we need
to return a blank form (if it’s another kind of request, it’s still safe to return
a blank form). We make an instance of
TopicForm v , store it in the vari -
able
form , and send the form to the template in the context dictionar y { .
Because we included no arguments when instantiating
TopicForm , Django
creates a blank form that the user can fill out. If the request method is POST, the
else block runs and processes the
data submitted in the form. We make an instance of
TopicForm w and pass
it the data entered by the user, stored in
request.POST . The form object that’s
returned contains the information submitted by the user. We can’t save the submitted information in the database until we’ve
checked that it’s valid x . The
is_valid() function checks that all required
fields have been filled in (all fields in a form are required by default) and
that the data entered matches the field types expected—for example, that
the length of
text is less than 200 characters, as we specified in model s.py
in Chapter 18. This automatic validation saves us a lot of work. If ever y -
thing is valid, we can call
save() y , which writes the data from the form
to the database. Once we’ve saved the data, we can leave this page. We
use
reverse() to get the UR L for the topics page and pass the UR L to
HttpResponseRedirect() z , which redirects the user’s browser to the topics
page. On the
topics page, the user should see the topic they just entered
in the list of topics.
t he new_topic t emplate
Now we make a new template called new_topic.html to display the form we
just created:
new_topic.html {% extends "learning_logs/base.html" %}
{% block content %}

User Accounts 431
u

v {% csrf_token %}
w {{ form.as_p }}
x

{% endblock content %}
This template extends base.html , so it has the same base structure as
the rest of the pages in Learning Log. At u we define an HTML form.
The
action argument tells the ser ver where to send the data submitted in
the form; in this case, we send it back to the view function
new_topic() .
The
method argument tells the browser to submit the data as a POST
request. Django uses the template tag
{% csrf_token %} v to prevent attackers
from using the form to gain unauthorized access to the ser ver (this kind of
attack is called a cross-site request forgery ). At w we display the form; here you
see how simple Django can make tasks such as displaying a form. We only
need to include the template variable
{{ form.as_p }} for Django to create
all the fields necessar y to display the form automatically. The
as_p modifier
tells Django to render all the form elements in paragraph format, which is
a simple way to display the form neatly. Django doesn’t create a submit button for forms, so we define one
at x .
Next, we include a link to the
new_topic page on the topics page:
topics.html {% extends "learning_logs/base.html" %}
{% block content %}

Topics

--snip--

{% endblock content %}
Place the link after the list of existing topics. Figure 19 -1 shows the
resulting form. Go ahead and use the form to add a few new topics of

432 Chapter 19
Figure 19-1: The page for adding a new topic
Now that the user can add a new topic, they’ll want to add new entries too.
We’ll again define a UR L, write a view function and a template, and link to
the page. But first we’ll add another class to for ms.py.
t he Entry ModelForm
We need to create a form associated with the
Entry model, but this time with
a little more customization than
TopicForm :
forms.py from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
--snip--
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
u labels = {'text': ''}
v widgets = {'text': forms.Textarea(attrs={'cols': 80})}
We first update the import statement to include Entry as well as Topic .
The new class
EntryForm inherits from forms.ModelForm and has a nested Meta
class listing the model it’s based on and the field to include in the form. We
again give the field
'text' a blank label u .
At v we include the
widgets attribute. A widget is an HTML form ele -
ment, such as a single-line text box, multi-line text area, or drop-down list.
By including the
widgets attribute you can override Django’s default widget
choices. By telling Django to use a
forms.Textarea element, we’re customizing
the input widget for the field
'text' so the text area will be 80 columns wide
instead of the default 40. This will give users enough room to write a mean -
i ng f u l ent r y.

User Accounts 433
the new_entry U rL
We need to include a
topic_id argument in the UR L for adding a new entr y,
because the entr y must be associated with a particular topic. Here’s the UR L,
which we add to learning_logs/urls.py :
urls.py --snip--
urlpatterns = [
--snip--
# Page for adding a new entry
url(r'^new_entry/(?P\d+)/\$', views.new_entry, name='ne
w_entry'),
]
This UR L pattern matches any UR L with the form http://localhost
:8000/new_entry/id/
, where id is a number matching the topic ID. The code
(?P\d+) captures a numerical value and stores it in the variable
topic_id . When a UR L matching this pattern is requested, Django sends the
request and the ID of the topic to the
new_entry() view function.
t he new_entry() View Function
The view function for
new_entry is much like the function for adding a new
topic:
views.py from django.shortcuts import render
--snip--
from .models import Topic
from .forms import TopicForm, EntryForm
--snip--
def new_entry(request, topic_id):
"""Add a new entry for a particular topic."""
u topic = Topic.objects.get(id=topic_id)
v if request.method != 'POST': # No data submitted; create a blank form.
w form = EntryForm() else:
# POST data submitted; process data.
x form = EntryForm(data=request.POST) if form.is_valid():
y new_entry = form.save(commit=False)
z new_entry.topic = topic new_entry.save()
{ return HttpResponseRedirect