0% found this document useful (0 votes)
3 views

Programming in Python IAP

The document is the 1st Edition of 'Programming in Python' by Frank Neven and others, aimed at beginners with no prior programming experience. It covers fundamental concepts of Python, including variables, expressions, conditionals, functions, and data types, along with practical programming techniques. The book is based on 'Learning with Python 3' and is available under the GNU Free Documentation License.

Uploaded by

sofie.joosten
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Programming in Python IAP

The document is the 1st Edition of 'Programming in Python' by Frank Neven and others, aimed at beginners with no prior programming experience. It covers fundamental concepts of Python, including variables, expressions, conditionals, functions, and data types, along with practical programming techniques. The book is based on 'Learning with Python 3' and is available under the GNU Free Documentation License.

Uploaded by

sofie.joosten
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 182

Programming in Python

Release 1st Edition

Frank Neven, Peter Wentworth, Jeffrey Elkner,


Allen B. Downey and Chris Meyers

Sep 14, 2018


CONTENTS

1 Preface (UHLE) 3

2 Getting started with Python 5

3 Variables, expressions and statements 11

4 Looping & the way of the turtle 27

5 Conditionals 39

6 Iteration 49

7 Functions 61

8 Strings 73

9 Files 85

10 Lists 91

11 Recursion 109

12 Syntax diagrams 115

13 Tuples 119

14 Dictionaries 123

15 Modules 131

16 Debugging 137

17 Some Tips, Tricks, and Common Errors 145

18 GNU Free Documentation License 151

19 Copyright Notice 159

20 Foreword 161

21 Preface 163

22 The Rhodes Local Edition (RLE) (Version of August, 2012) 167

i
23 Contributor List 171

Index 175

ii
Programming in Python, Release 1st Edition

Version date: January 2018


by Frank Neven ([email protected])
• search
• Chapter 1 Preface: This edition of the book (UHLE, UHasselt Local Edition)
• Chapter 2 Getting started
• Chapter 3 Variables, expressions, and statements
• Chapter 4 Looping & the way of the turtle
• Chapter 5 Conditionals
• Chapter 6 Iteration
• Chapter 7 Functions
• Chapter 8 Strings
• Chapter 9 Files
• Chapter 10 Lists
• Chapter 11 Recursion
• Chapter 12 Syntax diagrams
• Chapter 13 Tuples
• Chapter 14 Dictionaries
• Chapter 15 Modules
• Appendix A Debugging
• Appendix B Some tips, tricks, and common errors
• Copyright Notice
• GNU Free Document License
• Foreword (original)
• Preface (original)
• Preface-3 Rhodes Local Edition of the book
• Contributor List

CONTENTS 1
Programming in Python, Release 1st Edition

2 CONTENTS
CHAPTER

ONE

PREFACE (UHLE)

By Frank Neven
The present book is based on Learning with Python 3 by Peter Wentworth, Jeffrey Elkner, Allen B. Downey, and Chris
Meyers which is freely available under the terms of the GNU Free Documentation License. As required by the license
and out of respect for the original authors, the foreword, preface and contributor list are retained. I did move these
sections to the end of the document.
This edition is entitled Programming in Python and also referred to as the UHLE (UHasselt Local Edition). The book
is targeted at students without any prior programming experience.

3
Programming in Python, Release 1st Edition

4 Chapter 1. Preface (UHLE)


CHAPTER

TWO

GETTING STARTED WITH PYTHON

2.1 The Python programming language

The programming language you will be learning is Python. Python is an example of a high-level language; other
high-level languages you might have heard of are C++, PHP, Pascal, C#, and Java.
As you might infer from the name high-level language, there are also low-level languages, sometimes referred to as
machine languages or assembly languages. Loosely speaking, computers can only execute programs written in low-
level languages. Thus, programs written in a high-level language have to be translated into something more suitable
before they can run.
Almost all programs are written in high-level languages because of their advantages. It is much easier to program in a
high-level language so programs take less time to write, they are shorter and easier to read, and they are more likely
to be correct. Second, high-level languages are portable, meaning that they can run on different kinds of computers
with few or no modifications.
The engine that translates and runs Python is called the Python Interpreter: There are two ways to use it: immediate
mode and script mode. In immediate mode, you type Python expressions into the Python Interpreter window, and the
interpreter immediately shows the result:

The >>> is called the Python prompt. The interpreter uses the prompt to indicate that it is ready for instructions. We
typed 2 + 2, and the interpreter evaluated our expression, and replied 4, and on the next line it gave a new prompt,
indicating that it is ready for more input.
Alternatively, you can write a program in a file and use the interpreter to execute the contents of the file. Such a file is
called a script. Scripts have the advantage that they can be saved to disk, printed, and so on.
For example, we created a file named firstprogram.py. By convention, files that contain Python programs have
names that end with .py To execute the program, we can click the Run button:

5
Programming in Python, Release 1st Edition

There are various development environments available for programming in Python. Most programs are more inter-
esting than this one. Working directly in the interpreter is convenient for testing short bits of code because you get
immediate feedback. Think of it as scratch paper used to help you work out problems. Anything longer than a few
lines should be put into a script.

2.2 What is a program?

A program is a sequence of instructions that specifies how to perform a computation. The computation might be
something mathematical, such as solving a system of equations or finding the roots of a polynomial, but it can also
be a symbolic computation, such as searching and replacing text in a document or (strangely enough) compiling a
program.
The details look different in different languages, but a few basic instructions appear in just about every language:
input Get data from the keyboard, a file, or some other device.
output Display data on the screen or send data to a file or other device.
math Perform basic mathematical operations like addition and multiplication.
conditional execution Check for certain conditions and execute the appropriate sequence of statements.
repetition Perform some action repeatedly, usually with some variation.
Believe it or not, that’s pretty much all there is to it! Every program you’ve ever used, no matter how complicated,
is made up of instructions that look more or less like these. Thus, we can describe programming as the process of
breaking a large, complex task into smaller and smaller subtasks until the subtasks are simple enough to be performed
with sequences of these basic instructions.
That may be a little vague, but we will come back to this topic later when we talk about algorithms.

6 Chapter 2. Getting started with Python


Programming in Python, Release 1st Edition

2.3 The first program

Traditionally, the first program written in a new language is called Hello, World! because all it does is display the
words, Hello, World! In Python, the script looks like this: (For scripts, we’ll show line numbers to the left of the
Python statements.)

1 print("Hello, World!")

This is an example of using the print function, which doesn’t actually print anything on paper. It displays a value on
the screen. In this case, the result shown is

1 Hello, World!

The quotation marks in the program mark the beginning and end of the value; they don’t appear in the result.
Some people judge the quality of a programming language by the simplicity of the Hello, World! program. By this
standard, Python does about as well as possible.

2.4 Comments

As programs get bigger and more complicated, they get more difficult to read. Formal languages are dense, and it is
often difficult to look at a piece of code and figure out what it is doing, or why.
For this reason, it is a good idea to add notes to your programs to explain in natural language what the program is
doing.
A comment in a computer program is text that is intended only for the human reader — it is completely ignored by
the interpreter.
In Python, the # token starts a comment. The rest of the line is ignored. Here is a new version of Hello, World!.

1 #---------------------------------------------------
2 # This demo program shows off how elegant Python is!
3 # Written by Joe Soap, December 2010.
4 # Anyone may freely copy or modify this program.
5 #---------------------------------------------------
6

7 print("Hello, World!") # Isn't this easy!

You’ll also notice that we’ve left a blank line in the program. Blank lines are also ignored by the interpreter, but
comments and blank lines can make your programs much easier for humans to parse. Use them liberally!

2.5 Formal and natural languages

Natural languages are the languages that people speak, such as English, Spanish, and French. They were not designed
by people (although people try to impose some order on them); they evolved naturally.
Formal languages are languages that are designed by people for specific applications. For example, the notation
that mathematicians use is a formal language that is particularly good at denoting relationships among numbers and
symbols. Chemists use a formal language to represent the chemical structure of molecules. And most importantly:
Programming languages are formal languages that have been designed to express computations.

2.3. The first program 7


Programming in Python, Release 1st Edition

Formal languages tend to have strict rules about syntax. For example, 3+3=6 is a syntactically correct mathematical
statement, but 3=+6$ is not. H2 O is a syntactically correct chemical name, but 2 Zz is not.
Syntax rules come in two flavors, pertaining to tokens and structure. Tokens are the basic elements of the language,
such as words, numbers, parentheses, commas, and so on. In Python, a statement like print("Happy New Year
for ",2013) has 6 tokens: a function name, an open parenthesis (round bracket), a string, a comma, a number, and
a close parenthesis.
It is possible to make errors in the way one constructs tokens. One of the problems with 3=+6$ is that $ is not a legal
token in mathematics (at least as far as we know). Similarly, 2 Zz is not a legal token in chemistry notation because
there is no element with the abbreviation Zz.
The second type of syntax rule pertains to the structure of a statement— that is, the way the tokens are arranged.
The statement 3=+6$ is structurally illegal because you can’t place a plus sign immediately after an equal sign.
Similarly, molecular formulas have to have subscripts after the element name, not before. And in our Python example,
if we omitted the comma, or if we changed the two parentheses around to say print)"Happy New Year for
",2013( our statement would still have six legal and valid tokens, but the structure is illegal.
When you read a sentence in English or a statement in a formal language, you have to figure out what the structure of
the sentence is (although in a natural language you do this subconsciously). This process is called parsing.
For example, when you hear the sentence, “The other shoe fell”, you understand that the other shoe is the subject and
fell is the verb. Once you have parsed a sentence, you can figure out what it means, or the semantics of the sentence.
Assuming that you know what a shoe is and what it means to fall, you will understand the general implication of this
sentence.
Although formal and natural languages have many features in common — tokens, structure, syntax, and semantics —
there are many differences:
ambiguity Natural languages are full of ambiguity, which people deal with by using contextual clues and other
information. Formal languages are designed to be nearly or completely unambiguous, which means that any
statement has exactly one meaning, regardless of context.
redundancy In order to make up for ambiguity and reduce misunderstandings, natural languages employ lots of
redundancy. As a result, they are often verbose. Formal languages are less redundant and more concise.
literalness Formal languages mean exactly what they say. On the other hand, natural languages are full of idiom and
metaphor. If someone says, “The other shoe fell”, there is probably no shoe and nothing falling. You’ll need to
find the original joke to understand the idiomatic meaning of the other shoe falling. Yahoo! Answers thinks it
knows!
People who grow up speaking a natural language—everyone—often have a hard time adjusting to formal languages.
In some ways, the difference between formal and natural language is like the difference between poetry and prose, but
more so:
poetry Words are used for their sounds as well as for their meaning, and the whole poem together creates an effect or
emotional response. Ambiguity is not only common but often deliberate.
prose The literal meaning of words is more important, and the structure contributes more meaning. Prose is more
amenable to analysis than poetry but still often ambiguous.
program The meaning of a computer program is unambiguous and literal, and can be understood entirely by analysis
of the tokens and structure.
Here are some suggestions for reading programs (and other formal languages). First, remember that formal languages
are much more dense than natural languages, so it takes longer to read them. Also, the structure is very important, so
it is usually not a good idea to read from top to bottom, left to right. Instead, learn to parse the program in your head,
identifying the tokens and interpreting the structure. Finally, the details matter. Little things like spelling errors and
bad punctuation, which you can get away with in natural languages, can make a big difference in a formal language.

8 Chapter 2. Getting started with Python


Programming in Python, Release 1st Edition

2.6 Syntax and Semantics

We try to be explicit about syntax and semantics. Usually we employ boxes like the one below:

Syntax & semantics


Syntax

print( STRING1, STRING2)

Semantics Prints the strings STRING1 and STRING2 separated by a space to the output and moves the
output to the next line.

2.6. Syntax and Semantics 9


Programming in Python, Release 1st Edition

10 Chapter 2. Getting started with Python


CHAPTER

THREE

VARIABLES, EXPRESSIONS AND STATEMENTS

3.1 Values and data types

A value is one of the fundamental things — like a letter or a number — that a program manipulates. The values we
have seen so far are 4 (the result when we added 2 + 2), and "Hello, World!".
These values are classified into different data types: 4 is an integer, and "Hello, World!" is a string, so-called
because it contains a string of letters. You (and the interpreter) can identify strings because they are enclosed in
quotation marks.
If you are not sure what class a value falls into, Python has a function called type which can tell you.

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


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

Notice that the interpreter answers with <class 'int'> rather than <type 'int'>. In this course, you can treat
the words class and type interchangeably. We’ll come back to a deeper understanding of what a class is in later courses.
Not surprisingly, strings belong to the class str and integers belong to the class int. Less obviously, numbers with a
decimal point belong to a class called float, because these numbers are represented in a format called floating-point.

>>> type(3.2)
<class 'float'>

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

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

They’re strings!
Strings in Python can be enclosed in either single quotes (') or double quotes ("), or three of each (''' or """)

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


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

11
Programming in Python, Release 1st Edition

Double quoted strings can contain single quotes inside them, as in "Bruce's beard", and single quoted strings
can have double quotes inside them, as in 'The knights who say "Ni!"'.
Strings enclosed with three occurrences of either quote symbol are called triple quoted strings. They can contain either
single or double quotes:

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


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

Triple quoted strings can even span multiple lines:

>>> message = """This message will


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

Python doesn’t care whether you use single or double quotes or the three-of-a-kind quotes to surround your strings:
once it has parsed the text of your program or command, the way it stores the value is identical in all cases, and the
surrounding quotes are not part of the value. But when the interpreter wants to display a string, it has to decide which
quotes to use to make it look like a string.

>>> 'This is a string.'


'This is a string.'
>>> """And so is this."""
'And so is this.'

So the Python language designers usually chose to surround their strings by single quotes. What do you think would
happen if the string already contained single quotes? Try it in your Python interpreter.

3.2 Variables

One of the most powerful features of a programming language is the ability to manipulate variables. A variable is a
name that refers to a value.
The assignment statement gives a value to a variable:

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


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

This example makes three assignments. The first assigns the string value "What's up, Doc?" to a variable
named message. The second gives the integer 17 to n, and the third assigns the floating-point number 3.14159 to
a variable called pi.
The assignment token, =, should not be confused with equals, which uses the token ==. The assignment statement
binds a name, on the left-hand side of the operator, to a value, on the right-hand side. This is why you will get an error
if you enter:

>>> 17 = n
File "<interactive input>", line 1
SyntaxError: can't assign to literal

12 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

Tip: When reading or writing code, say to yourself “n is assigned 17” or “n gets the value 17”. Don’t
say “n equals 17”.

A common way to represent variables on paper is to write the name with an arrow pointing to the variable’s value.
This kind of figure is called a state snapshot or state frame because it shows what state each of the variables is in at a
particular instant in time. (Think of it as the variable’s state of mind). This diagram shows the result of executing the
assignment statements:

If you ask the interpreter to evaluate a variable, it will produce the value that is currently linked to the variable:

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

We use variables in a program to “remember” things, perhaps the current score at the football game. But variables are
variable. This means they can change over time, just like the scoreboard at a football game. You can assign a value to
a variable, and later assign a different value to the same variable. (This is different from maths. In maths, if you give
‘x‘ the value 3, it cannot change to link to a different value half-way through your calculations!)

>>> day = "Thursday"


>>> day
'Thursday'
>>> day = "Friday"
>>> day
'Friday'
>>> day = 21
>>> day
21

You’ll notice we changed the value of day three times, and on the third assignment we even made it refer to a value
that was of a different type.
A great deal of programming is about having the computer remember things, e.g. The number of missed calls on your
phone, and then arranging to update or change the variable when you miss another call.
You can step through the following program in Python Tutor to see what happens:

message = "What's up, Doc?"


n = 17
pi = 3.14159
day = "Thursday"
day = "Friday"
day = 21

We discuss composition of statements as depicted in the above program below. In essence, Python executes the above
assignments sequentially starting with

3.2. Variables 13
Programming in Python, Release 1st Edition

message = "What's up, Doc?"

and finishing with

day = 21

The end result looks as follows:

Python Tutor visualizes the global frame (that is, the variables together with their content as seen by the Python
execution) by enumerating variables by their name and their values in a box. As becomes apparent through the
execution with Python Tutor, Python creates a variable the first time it is encountered (and not before) and assigns a
value to the variable. Subsequent assignments to this variable will change its value.
When a variable is used in an expression at the right hand side of an assignment, the variable is replaced by its value.
For instance, running the code

day = 21
day = day + 1

results in the situation where variable day is assigned the value 22. You can check this out yourself.
If you try to get the value of a variable that has never been assigned to, you’ll get an error:

>>> day = x + 1
Traceback (most recent call last):
File "<interactive input>", line 1, in
NameError: name 'x' is not defined

Before you can update a variable, you have to initialize it to some starting value, usually with a simple assignment:

x = 1
day = x + 1

Python is a so-called dynamically typed language. This means that a variable inherits the type of the object it is bound
to. Consequently, its type can dynamically change. For instance, in the following code

1 x = 1
2 x = "Monday"

the variable x is bound to the integer 1 after execution of line 1 and therefore is of type integer, while after execution
of line 2 x is bound to the string Monday and therefore now is of type string. In a statically typed language, like for
instance Java or C, variables are associated to types at compile time and this type can not change. In such languages

14 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

variables together with their type have to be declared. For instance, the following Java code declares two variables x
and y as an integer and a string, respectively:

1 // Java code
2 int x;
3 String y;

3.3 Variable names and keywords

Variable names can be arbitrarily long. They can contain both letters and digits, but they have to begin with a letter
or an underscore. Although it is legal to use uppercase letters, by convention we don’t. If you do, remember that case
matters. Bruce and bruce are different variables.
The underscore character ( _) can appear in a name. It is often used in names with multiple words, such as my_name
or price_of_tea_in_china.
There are some situations in which names beginning with an underscore have special meaning, so a safe rule for
beginners is to start all names with a letter.
If you give a variable an illegal name, you get a syntax error:

>>> 76trombones = "big parade"


SyntaxError: invalid syntax
>>> more$ = 1000000
SyntaxError: invalid syntax
>>> class = "Computer Science 101"
SyntaxError: invalid syntax

76trombones is illegal because it does not begin with a letter. more$ is illegal because it contains an illegal
character, the dollar sign. But what’s wrong with class?
It turns out that class is one of the Python keywords. Keywords define the language’s syntax rules and structure,
and they cannot be used as variable names.
Python has thirty-something keywords (and every now and again improvements to Python introduce or eliminate one
or two):

and as assert break class continue


def del elif else except exec
finally for from global if import
in is lambda nonlocal not or
pass raise return try while with
yield True False None

You might want to keep this list handy. If the interpreter complains about one of your variable names and you don’t
know why, see if it is on this list.
Programmers generally choose names for their variables that are meaningful to the human readers of the program —
they help the programmer document, or remember, what the variable is used for.

Caution: Beginners sometimes confuse “meaningful to the human readers” with “meaningful to the computer”.
So they’ll wrongly think that because they’ve called some variable average or pi, it will somehow magically
calculate an average, or magically know that the variable pi should have a value like 3.14159. No! The computer
doesn’t understand what you intend the variable to mean.

3.3. Variable names and keywords 15


Programming in Python, Release 1st Edition

So you’ll find some instructors who deliberately don’t choose meaningful names when they teach beginners —
not because we don’t think it is a good habit, but because we’re trying to reinforce the message that you — the
programmer — must write the program code to calculate the average, and you must write an assignment statement
to give the variable pi the value you want it to have.

3.4 Statements

A statement is an instruction that the Python interpreter can execute. We have only seen the assignment statement so
far. Some other kinds of statements that we’ll see shortly are while statements, for statements, if statements, and
import statements. (There are other kinds too!)
When you type a statement on the command line, Python executes it. Statements don’t produce any result. That is, in
contrast to expressions (which are discussed below), statements do not return a value.

3.5 Evaluating expressions

An expression is a combination of values, variables, operators, and calls to functions. If you type an expression at the
Python prompt, the interpreter evaluates it and displays the result:

>>> 1 + 1
2
>>> len("hello")
5

In this example len is a built-in Python function that returns the number of characters in a string. We’ve previously
seen the print and the type functions, so this is our third example of a function!
The evaluation of an expression produces a value, which is why expressions can appear on the right hand side of
assignment statements. A value all by itself is a simple expression, and so is a variable.

>>> 17
17
>>> y = 3.14
>>> x = len("hello")
>>> x
5
>>> y
3.14

3.6 Operators and operands

Operators are special tokens that represent computations like addition, multiplication and division. The values the
operator uses are called operands.
The following are all legal Python expressions whose meaning is more or less clear:

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

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

16 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

>>> 2 ** 3
8
>>> 3 ** 2
9

When a variable name appears in the place of an operand, it is replaced with its value before the operation is performed.
Addition, subtraction, multiplication, and exponentiation all do what you expect.
Example: so let us convert 645 minutes into hours:

>>> minutes = 645


>>> hours = minutes / 60
>>> hours
10.75

Oops! In Python 3, the division operator / always yields a floating point result. What we might have wanted to know
was how many whole hours there are, and how many minutes remain. Python gives us two different flavors of the
division operator. The second, called floor division uses the token //. Its result is always a whole number — and if it
has to adjust the number it always moves it to the left on the number line. So 6 // 4 yields 1, but -6 // 4 might surprise
you!

>>> 7 / 4
1.75
>>> 7 // 4
1
>>> minutes = 645
>>> hours = minutes // 60
>>> hours
10

Take care that you choose the correct flavor of the division operator. If you’re working with expressions where you
need floating point values, use the division operator that does the division accurately.

3.7 Type converter functions

Here we’ll look at three more Python functions, int, float and str, which will (attempt to) convert their arguments
into types int, float and str respectively. We call these type converter functions.
The int function can take a floating point number or a string, and turn it into an int. For floating point numbers, it
discards the decimal portion of the number — a process we call truncation towards zero on the number line. Let us
see this in action:

>>> int(3.14)
3
>>> int(3.9999) # This doesn't round to the closest int!
3
>>> int(3.0)
3
>>> int(-3.999) # Note that the result is closer to zero
-3
>>> int(minutes / 60)
10
>>> int("2345") # Parse a string to produce an int
2345
(continues on next page)

3.7. Type converter functions 17


Programming in Python, Release 1st Edition

(continued from previous page)


>>> int(17) # It even works if arg is already an int
17
>>> int("23 bottles")

This last case doesn’t look like a number — what do we expect?

Traceback (most recent call last):


File "<interactive input>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '23 bottles'

The type converter float can turn an integer, a float, or a syntactically legal string into a float:

>>> float(17)
17.0
>>> float("123.45")
123.45

The type converter str turns its argument into a string:

>>> str(17)
'17'
>>> str(123.45)
'123.45'

3.8 Order of operations

When more than one operator appears in an expression, the order of evaluation depends on the rules of precedence.
Python follows the same precedence rules for its mathematical operators that mathematics does. The acronym PEM-
DAS is a useful way to remember the order of operations:
1. Parentheses have the highest precedence and can be used to force an expression to evaluate in the order you
want. Since expressions in parentheses are evaluated first, 2 * (3-1) is 4, and (1+1)**(5-2) is 8. You
can also use parentheses to make an expression easier to read, as in (minute * 100) / 60, even though it
doesn’t change the result.
2. Exponentiation has the next highest precedence, so 2**1+1 is 3 and not 4, and 3*1**3 is 3 and not 27.
3. Multiplication and both Division operators have the same precedence, which is higher than Addition and
Subtraction, which also have the same precedence. So 2*3-1 yields 5 rather than 4, and 5-2*2 is 1, not
6.
4. Operators with the same precedence are evaluated from left-to-right. In algebra we say they are left-associative.
So in the expression 6-3+2, the subtraction happens first, yielding 3. We then add 2 to get the result 5. If
the operations had been evaluated from right to left, the result would have been 6-(3+2), which is 1. (The
acronym PEDMAS could mislead you to thinking that division has higher precedence than multiplication, and
addition is done ahead of subtraction - don’t be misled. Subtraction and addition are at the same precedence,
and the left-to-right rule applies.)
• Due to some historical quirk, an exception to the left-to-right left-associative rule is the exponentiation
operator **, so a useful hint is to always use parentheses to force exactly the order you want when expo-
nentiation is involved:

>>> 2 ** 3 ** 2 # The right-most ** operator gets done first!


512
(continues on next page)

18 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

(continued from previous page)


>>> (2 ** 3) ** 2 # Use parentheses to force the order you want!
64

The immediate mode command prompt of Python is great for exploring and experimenting with expressions like this.

3.9 Operations on strings

In general, you cannot perform mathematical operations on strings, even if the strings look like numbers. The following
are illegal (assuming that message has type string):

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


>>> message - 1 # Error
>>> "Hello" / 123 # Error
>>> message * "Hello" # Error
>>> "15" + 2 # Error

Interestingly, the + operator does work with strings, but for strings, the + operator represents concatenation, not
addition. Concatenation means joining the two operands by linking them end-to-end. For example:

1 fruit = "banana"
2 baked_good = " nut bread"
3 print(fruit + baked_good)

The output of this program is banana nut bread. The space before the word nut is part of the string, and is
necessary to produce the space between the concatenated strings.
The * operator also works on strings; it performs repetition. For example, 'Fun'*3 is 'FunFunFun'. One of the
operands has to be a string; the other has to be an integer.
On one hand, this interpretation of + and * makes sense by analogy with addition and multiplication. Just as 4*3 is
equivalent to 4+4+4, we expect "Fun"*3 to be the same as "Fun"+"Fun"+"Fun", and it is. On the other hand,
there is a significant way in which string concatenation and repetition are different from integer addition and multipli-
cation. Can you think of a property that addition and multiplication have that string concatenation and repetition do
not?
Next, we briefly look at indexing operator and the length function. These will be discussed in more detail in Chapter
8.
The indexing operator (Python uses square brackets to enclose the index) selects a single character substring from a
string:

>>> fruit = "banana"


>>> m = fruit[1]
>>> print(m)

The expression fruit[1] selects character number 1 from fruit, and creates a new string containing just this one
character. The variable m refers to the result. When we display m, we could get a surprise:

Computer scientists always start counting from zero! The letter at subscript position zero of "banana" is b. So at
position [1] we have the letter a.
If we want to access the zero-eth letter of a string, we just place 0, or any expression that evaluates to 0, in between
the brackets:

3.9. Operations on strings 19


Programming in Python, Release 1st Edition

>>> m = fruit[0]
>>> print(m)
b

The expression in brackets is called an index. An index specifies a member of an ordered collection, in this case the
collection of characters in the string. The index indicates which one you want, hence the name. It can be any integer
expression.
The len function, when applied to a string, returns the number of characters in a string:

>>> fruit = "banana"


>>> len(fruit)
6

To get the last letter of a string, you might be tempted to try something like this:

1 sz = len(fruit)
2 last = fruit[sz] # ERROR!

That won’t work. It causes the runtime error IndexError: string index out of range. The reason is
that there is no character at index position 6 in "banana". Because we start counting at zero, the six indexes are
numbered 0 to 5. To get the last character, we have to subtract 1 from the length of fruit:

1 sz = len(fruit)
2 last = fruit[sz-1]

To conclude our initial discussion on strings, we mention that strings are immutable. That is, they cannot be changed.
Running the code

s = "duck"
s[0] = "a"

displays the error

TypeError: 'str' object does not support item assignment

3.10 Input

There is a built-in function in Python for getting input from the user:

1 n = input("Please enter your name: ")

Depending on the IDE, a run of this script opens up a dialog window to enter input or expects you to enter the input
in the console window. In both cases, the input text that has been entered is returned from the input function, and in
this case assigned to the variable n.
Even if you asked the user to enter their age, you would get back a string like "17". It would be your job, as the
programmer, to convert that string into a int or a float, using the int or float converter functions we saw earlier.

3.11 Composition

So far, we have looked at the elements of a program — variables, expressions, statements, and function calls — in
isolation, without talking about how to combine them.

20 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

One of the most useful features of programming languages is their ability to take small building blocks and compose
them into larger chunks.
For example, we know how to get the user to enter some input, we know how to convert the string we get into a float,
we know how to write a complex expression, and we know how to print values. Let’s put these together in a small
four-step program that asks the user to input a value for the radius of a circle, and then computes the area of the circle
from the formula

Firstly, we’ll do the four steps one at a time:

1 response = input("What is your radius? ")


2 r = float(response)
3 area = 3.14159 * r**2
4 print("The area is ", area)

You can also run this code in Python Tutor. Now let’s compose the first two lines into a single line of code, and
compose the second two lines into another line of code.

1 r = float( input("What is your radius? ") )


2 print("The area is ", 3.14159 * r**2)

If we really wanted to be tricky, we could write it all in one statement:

1 print("The area is ", 3.14159*float(input("What is your radius?"))**2)

Such compact code may not be most understandable for humans, but it does illustrate how we can compose bigger
chunks from our building blocks.
If you’re ever in doubt about whether to compose code or fragment it into smaller steps, try to make it as simple as
you can for the human to follow. My choice would be the first case above, with four separate steps.

3.12 The modulus operator

The modulus operator works on integers (and integer expressions) and gives the remainder when the first number
is divided by the second. In Python, the modulus operator is a percent sign (%). The syntax is the same as for other
operators. It has the same precedence as the multiplication operator.

>>> q = 7 // 3 # This is integer division operator


>>> print(q)
2
>>> r = 7 % 3
>>> print(r)
1

So 7 divided by 3 is 2 with a remainder of 1.


The modulus operator turns out to be surprisingly useful. For example, you can check whether one number is divisible
by another—if x % y is zero, then x is divisible by y.
Also, you can extract the right-most digit or digits from a number. For example, x % 10 yields the right-most digit
of x (in base 10). Similarly x % 100 yields the last two digits.
It is also extremely useful for doing conversions, say from seconds, to hours, minutes and seconds. So let’s write a
program to ask the user to enter some seconds, and we’ll convert them into hours, minutes, and remaining seconds.

3.12. The modulus operator 21


Programming in Python, Release 1st Edition

1 total_secs = int(input("How many seconds, in total?"))


2 hours = total_secs // 3600
3 secs_still_remaining = total_secs % 3600
4 minutes = secs_still_remaining // 60
5 secs_finally_remaining = secs_still_remaining % 60
6

7 print("Hrs=", hours, " mins=", minutes,


8 "secs=", secs_finally_remaining)

You can also run this code in Python Tutor.

3.13 Whitespace

Typed words are usually separated by whitespace. In Python, whitespace consists of space, tab, and return (well, there
is also linefeed, formfeed and vertical tab, but let’s not get into those). Generally, whitespace is ignored by Python. In
particular, this means that

n = n + 1

has exactly the same meaning as

n=n+1

or even

n= n + 1

Blank lines are also considered to be whitespace and they are ignored as well. Whitespace at the beginning of a line,
also known as leading whitespace, defines indentation. We cover indentation in the next chapter.

3.14 Assignment recap

Syntax & semantics: assignment statement


Syntax

variable_name = expression

Semantics The expression expression is evaluated to a value. If the variable with name
variable_name does not exist it is created. The variable is then linked to the value.

Variables in Python can be considered as labels. Every occurrence of variable_name in an expression is evaluated
to the linked/labeled value. Let us have a look at the following code:

1 a = 5
2 b = a
3 b = 6
4 print(a)

Line 2 results in the following state frame:

22 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

Whereas line 3 results in the following state frame:

The reason that the value of a is not changed to 6 as well is because all data types we have seen up to now are
immutable. This means that they can not be changed. The assignment b=6 creates a novel value 6 and links b to it
while leaving the value of a alone. Because of this immutability, Python Tutor models the assignment

a = 5
b = a

as follows

For now, it is safe to consider every assignment as creating a separate value. This will change when we discuss mutable

3.14. Assignment recap 23


Programming in Python, Release 1st Edition

types like lists.


By the way, the output of the above program is 5 and not 6.

3.15 Print

By default a print statement adds a newline \n to the printed message. That is,

1 print("Hello ")
2 print("Mr. Robot")

produces

Hello
Mr. Robot

rather than

Hello Mr. Robot

You can change this behavior by specifying another end symbol:

1 print("Hello ", end="")


2 print("Mr. Robot")

The output now is

Hello Mr. Robot

Any text can be used:

1 print("Hello ", end="hi")


2 print("Mr. Robot")

The output now is

Hello hiMr. Robot

Similarly, the default separator symbol is a white space:

1 print("Hello", "Mr. Robot")

produces as output

Hello Mr. Robot

You can change this as follows:

1 print("Hello", "Mr. Robot", sep="hi")

which produces as output

HellohiMr. Robot

24 Chapter 3. Variables, expressions and statements


Programming in Python, Release 1st Edition

3.16 Glossary

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

n = n + 1

n plays a very different role on each side of the =. On the right it is a value and makes up part of the expression
which will be evaluated by the Python interpreter before assigning it to the name on the left. Python creates a
variable the first time it is encountered (and not before) and subsequent assignments to this variable will change
its value.
assignment token = is Python’s assignment token. Do not confuse it with equals, which is an operator for comparing
values.
composition The ability to combine simple expressions and statements into compound statements and expressions in
order to represent complex computations concisely.
concatenate To join two strings end-to-end.
data type A set of values. The type of a value determines how it can be used in expressions. So far, the types you
have seen are integers (int), floating-point numbers (float), and strings (str).
evaluate To simplify an expression by performing the operations in order to yield a single value.
expression A combination of variables, operators, and values that represents a single result value.
float A Python data type which stores floating-point numbers. Floating-point numbers are stored internally in two
parts: a base and an exponent. When printed in the standard format, they look like decimal numbers. Beware of
rounding errors when you use floats, and remember that they are only approximate values.
floor division An operator (denoted by the token //) that divides one number by another and yields an integer, or, if
the result is not already an integer, it yields the next smallest integer.
int A Python data type that holds positive and negative whole numbers.
keyword A reserved word that is used by the compiler to parse program; you cannot use keywords like if, def, and
while as variable names.
modulus operator An operator, denoted with a percent sign ( %), that works on integers and yields the remainder
when one number is divided by another.
operand One of the values on which an operator operates.
operator A special symbol that represents a simple computation like addition, multiplication, or string concatenation.
rules of precedence The set of rules governing the order in which expressions involving multiple operators and
operands are evaluated.
state snapshot or state frame A graphical representation of a set of variables and the values to which they refer,
taken at a particular instant during the program’s execution.
statement An instruction that the Python interpreter can execute. So far we have only seen the assignment statement,
but we will soon meet the import statement and the for statement.
str A Python data type that holds a string of characters.
value A number or string (or other things to be named later) that can be stored in a variable or computed in an
expression.
variable A name that refers to a value.

3.16. Glossary 25
Programming in Python, Release 1st Edition

variable name A name given to a variable. Variable names in Python consist of a sequence of letters (a..z, A..Z, and
_) and digits (0..9) that begins with a letter. In best programming practice, variable names should be chosen so
that they describe their use in the program, making the program self documenting.

26 Chapter 3. Variables, expressions and statements


CHAPTER

FOUR

LOOPING & THE WAY OF THE TURTLE

There are many modules in Python that provide very powerful features that we can use in our own programs. Some of
these can send email, or fetch web pages. The one we’ll look at in this chapter allows us to create turtles and get them
to draw shapes and patterns. The turtles are fun, but the real purpose of the chapter is to teach ourselves a little more
Python, and to develop our theme of computational thinking or thinking like a computer scientist. Most of the Python
covered here will be explored in more depth later.

4.1 Our first turtle program

Let’s write a couple of lines of Python program to create a new turtle and start drawing a rectangle. (We’ll call the
variable that refers to our first turtle alex, but we can choose another name if we follow the naming rules from the
previous chapter).

1 import turtle # Allows us to use turtles


2 wn = turtle.Screen() # Creates a playground for turtles
3 alex = turtle.Turtle() # Create a turtle, assign to alex
4

5 alex.forward(50) # Tell alex to move forward by 50 units


6 alex.left(90) # Tell alex to turn by 90 degrees
7 alex.forward(30) # Complete the second side of a rectangle
8

9 wn.mainloop() # Wait for user to close window

When we run this program, a new window pops up:

Here are a couple of things we’ll need to understand about this program.

27
Programming in Python, Release 1st Edition

The first line tells Python to load a module named turtle. That module brings us two new types that we can use:
the Turtle type, and the Screen type. The dot notation turtle.Turtle means “The Turtle type that is defined
within the turtle module”. Remember that Python is case sensitive, so the module name, with a lowercase t, is different
from the type Turtle.
We then create and open what it calls a screen (we would prefer to call it a window), which we assign to variable wn.
Every window contains a canvas, which is the area inside the window on which we can draw.
In line 3 we create a turtle. The variable alex is made to refer to this turtle.
So these first three lines have set things up, we’re ready to get our turtle to draw on our canvas.
In lines 5-7, we instruct the object alex to move, and to turn. We do this by invoking, or activating, alex’s methods
— these are the instructions that all turtles know how to respond to.
The last line plays a part too: the wn variable refers to the window shown above. When we invoke its mainloop
method, it enters a state where it waits for events (like keypresses, or mouse movement and clicks). The program will
terminate when the user closes the window.
An object can have various methods — things it can do — and it can also have attributes — (sometimes called
properties). For example, each turtle has a color attribute. The method invocation alex.color("red") will make
alex red, and drawing will be red too. (Note the word color is spelled the American way!)
The color of the turtle, the width of its pen, the position of the turtle within the window, which way it is facing, and so
on are all part of its current state. Similarly, the window object has a background color, and some text in the title bar,
and a size and position on the screen. These are all part of the state of the window object.
Quite a number of methods exist that allow us to modify the turtle and the window objects. We’ll just show a couple.
In this program we’ve only commented those lines that are different from the previous example (and we’ve used a
different variable name for this turtle):

1 import turtle
2 wn = turtle.Screen()
3 wn.bgcolor("lightgreen") # Set the window background color
4 wn.title("Hello, Tess!") # Set the window title
5

6 tess = turtle.Turtle()
7 tess.color("blue") # Tell tess to change her color
8 tess.pensize(3) # Tell tess to set her pen width
9

10 tess.forward(50)
11 tess.left(120)
12 tess.forward(50)
13

14 wn.mainloop()

When we run this program, this new window pops up, and will remain on the screen until we close it.

28 Chapter 4. Looping & the way of the turtle


Programming in Python, Release 1st Edition

Extend this program . . .


1. Modify this program so that before it creates the window, it prompts the user to enter the desired background
color. It should store the user’s responses in a variable, and modify the color of the window according to the
user’s wishes. (Hint: you can find a list of permitted color names at http://www.tcl.tk/man/tcl8.4/TkCmd/colors.
htm. It includes some quite unusual ones, like “peach puff” and “HotPink”.)
2. Do similar changes to allow the user, at runtime, to set tess’ color.
3. Do the same for the width of tess’ pen. Hint: your dialog with the user will return a string, but tess’
pensize method expects its argument to be an int. So you’ll need to convert the string to an int before you
pass it to pensize.

4.2 Instances — a herd of turtles

Just like we can have many different integers in a program, we can have many turtles. Each of them is called an
instance. Each instance has its own attributes and methods — so alex might draw with a thin black pen and be at
some position, while tess might be going in her own direction with a fat pink pen.
1 import turtle
2 wn = turtle.Screen() # Set up the window and its attributes
3 wn.bgcolor("lightgreen")
4 wn.title("Tess & Alex")
5

6 tess = turtle.Turtle() # Create tess and set some attributes


7 tess.color("hotpink")
8 tess.pensize(5)
9

10 alex = turtle.Turtle() # Create alex


11

12 tess.forward(80) # Make tess draw equilateral triangle


13 tess.left(120)
14 tess.forward(80)
15 tess.left(120)
16 tess.forward(80)
17 tess.left(120) # Complete the triangle
(continues on next page)

4.2. Instances — a herd of turtles 29


Programming in Python, Release 1st Edition

(continued from previous page)


18

19 tess.right(180) # Turn tess around


20 tess.forward(80) # Move her away from the origin
21

22 alex.forward(50) # Make alex draw a square


23 alex.left(90)
24 alex.forward(50)
25 alex.left(90)
26 alex.forward(50)
27 alex.left(90)
28 alex.forward(50)
29 alex.left(90)
30

31 wn.mainloop()

Here is what happens when alex completes his rectangle, and tess completes her triangle:

Here are some How to think like a computer scientist observations:


• There are 360 degrees in a full circle. If we add up all the turns that a turtle makes, no matter what steps occurred
between the turns, we can easily figure out if they add up to some multiple of 360. This should convince us that
alex is facing in exactly the same direction as he was when he was first created. (Geometry conventions have
0 degrees facing East, and that is the case here too!)
• We could have left out the last turn for alex, but that would not have been as satisfying. If we’re asked to
draw a closed shape like a square or a rectangle, it is a good idea to complete all the turns and to leave the turtle
back where it started, facing the same direction as it started in. This makes reasoning about the program and
composing chunks of code into bigger programs easier for us humans!
• We did the same with tess: she drew her triangle, and turned through a full 360 degrees. Then we turned her
around and moved her aside. Even the blank line 18 is a hint about how the programmer’s mental chunking is
working: in big terms, tess’ movements were chunked as “draw the triangle” (lines 12-17) and then “move
away from the origin” (lines 19 and 20).
• One of the key uses for comments is to record our mental chunking, and big ideas. They’re not always explicit
in the code.

30 Chapter 4. Looping & the way of the turtle


Programming in Python, Release 1st Edition

• And, uh-huh, two turtles may not be enough for a herd. But the important idea is that the turtle module gives us
a kind of factory that lets us create as many turtles as we need. Each instance has its own state and behaviour.

4.3 The for loop

When we drew the square, it was quite tedious. We had to explicitly repeat the steps of moving and turning four times.
If we were drawing a hexagon, or an octogon, or a polygon with 42 sides, it would have been worse.
So a basic building block of all programs is to be able to repeat some code, over and over again.
Python’s for loop solves this for us. Let’s say we have some friends, and we’d like to send them each an email inviting
them to our party. We don’t quite know how to send email yet, so for the moment we’ll just print a message for each
friend:

1 for f in ["Joe","Zoe","Brad","Angelina","Zuki","Thandi","Paris"]:
2 invite = "Hi " + f + ". Please come to my party on Saturday!"
3 print(invite)
4 # more code can follow here ...

When we run this, the output looks like this:

Hi Joe. Please come to my party on Saturday!


Hi Zoe. Please come to my party on Saturday!
Hi Brad. Please come to my party on Saturday!
Hi Angelina. Please come to my party on Saturday!
Hi Zuki. Please come to my party on Saturday!
Hi Thandi. Please come to my party on Saturday!
Hi Paris. Please come to my party on Saturday!

• The variable f in the for statement at line 1 is called the loop variable. We could have chosen any other
variable name instead.
• Lines 2 and 3 are the loop body. The loop body is always indented. The indentation determines exactly what
statements are “in the body of the loop”.
• On each iteration or pass of the loop, first a check is done to see if there are still more items to be processed.
If there are none left (this is called the terminating condition of the loop), the loop has finished. Program
execution continues at the next statement after the loop body, (e.g. in this case the next statement below the
comment in line 4).
• If there are items still to be processed, the loop variable is updated to refer to the next item in the list. This
means, in this case, that the loop body is executed here 7 times, and each time f will refer to a different friend.
• At the end of each execution of the body of the loop, Python returns to the for statement, to see if there are
more items to be handled, and to assign the next one to f.

Remark
In Python, the exact amount of indentation does not matter, only the relative indentation of nested blocks. Still, it is
encouraged to use four space (or one tab set to four spaces) per indentation level. It does not matter if you use tabs or
spaces as long as you do not mix them. Check your editor settings!

We call ["Joe","Zoe","Brad","Angelina","Zuki","Thandi","Paris"] a list. Lists are among the


coolest features of Python and we explain them in more detail in later chapters. For now, lists are just sequences of
strings and integers. That is right, [1,2,3] is a list too.

4.3. The for loop 31


Programming in Python, Release 1st Edition

4.4 Flow of Execution of the for loop

As a program executes, the interpreter always keeps track of which statement is about to be executed. We call this the
control flow, or the flow of execution of the program. When humans execute programs, they often use their finger to
point to each statement in turn. So we could think of control flow as “Python’s moving finger”.
Control flow until now has been strictly top to bottom, one statement at a time. The for loop changes this.

Flowchart of a for loop


Control flow is often easy to visualize and understand if we draw a flowchart. This shows the exact steps and logic of
how the for statement executes.

4.5 The loop simplifies our turtle program

To draw a square we’d like to do the same thing four times — move the turtle, and turn. We previously used 8 lines to
have alex draw the four sides of a square. This does exactly the same, but using just three lines:

1 for i in [0,1,2,3]:
2 alex.forward(50)
3 alex.left(90)

Some observations:

32 Chapter 4. Looping & the way of the turtle


Programming in Python, Release 1st Edition

• While “saving some lines of code” might be convenient, it is not the big deal here. What is much more important
is that we’ve found a “repeating pattern” of statements, and reorganized our program to repeat the pattern. Find-
ing the chunks and somehow getting our programs arranged around those chunks is a vital skill in computational
thinking.
• The values [0,1,2,3] were provided to make the loop body execute 4 times. We could have used any four values,
but these are the conventional ones to use. In fact, they are so popular that Python gives us special built-in
range objects:

1 for i in range(4):
2 # Executes the body with i = 0, then 1, then 2, then 3
3 for x in range(10):
4 # Sets x to each of ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

• Computer scientists like to count from 0!


• range can deliver a sequence of values to the loop variable in the for loop. They start at 0, and in these cases
do not include the 4 or the 10.
• Our little trick earlier to make sure that alex did the final turn to complete 360 degrees has paid off: if we had
not done that, then we would not have been able to use a loop for the fourth side of the square. It would have
become a “special case”, different from the other sides. When possible, we’d much prefer to make our code fit
a general pattern, rather than have to create a special case.
So to repeat something four times, a good Python programmer would do this:

1 for i in range(4):
2 alex.forward(50)
3 alex.left(90)

By now you should be able to see how to change our previous program so that tess can also use a for loop to draw
her equilateral triangle.
But now, what would happen if we made this change?

1 for c in ["yellow", "red", "purple", "blue"]:


2 alex.color(c)
3 alex.forward(50)
4 alex.left(90)

A variable can also be assigned a value that is a list. So lists can also be used in more general situations, not only in
the for loop. The code above could be rewritten like this:

1 # Assign a list to a variable


2 clrs = ["yellow", "red", "purple", "blue"]
3 for c in clrs:
4 alex.color(c)
5 alex.forward(50)
6 alex.left(90)

4.6 Syntax and semantics of for loops

Syntax & semantics of a for loop


Syntax

4.6. Syntax and semantics of for loops 33


Programming in Python, Release 1st Edition

for loop_variable in list:


loop_body

Semantics The loop_variable is assigned to every value in list. For every assignment
loop_body is then executed.

4.7 Strings

A loop can be directly used to iterate over a string. The following code prints a string letter by letter:

s = "desserts"
for i in range(len(s)):
print(s[i], end="")

In particular,
• the loop variable i iterates over the indices of s: [0,1,...,len(s)-1]
• the statement print(s[i], end="") prints the letter of s with index i. Here, end="" signals that the
current print statement should be followed by "" (the empty string) rather than the default \n (end-of-line).
Consequently, all letters of s are printed on the same line. Just remove ,end="" to see the default behavior
that prints every letter on a separate line.
Of course, the previous program is not very useful as it is equivalent to the much more succinct program

s = "desserts"
print(s)

Let us therefore look at a different program that prints out the reverse of a string:

s = "desserts"
for i in range(len(s)):
print(s[len(s) - 1 - i], end="")

The program outputs:

stressed

Notice that the program prints out the characters s[7],s[6],s[5],s[4],s[3],s[2],s[1],s[0]. Convince
yourself by running the program:

s = "desserts"
for i in range(len(s)):
print(len(s) - 1 - i)

4.8 A few more turtle methods and tricks

Turtle methods can use negative angles or distances. So tess.forward(-100) will move tess backwards, and
tess.left(-30) turns her to the right. Additionally, because there are 360 degrees in a circle, turning 30 to the
left will get tess facing in the same direction as turning 330 to the right! (The on-screen animation will differ, though
— you will be able to tell if tess is turning clockwise or counter-clockwise!)

34 Chapter 4. Looping & the way of the turtle


Programming in Python, Release 1st Edition

This suggests that we don’t need both a left and a right turn method — we could be minimalists, and just have one
method. There is also a backward method. (If you are very nerdy, you might enjoy saying alex.backward(-100)
to move alex forward!)
Part of thinking like a scientist is to understand more of the structure and rich relationships in our field. So revising a
few basic facts about geometry and number lines, and spotting the relationships between left, right, backward, forward,
negative and positive distances or angles values is a good start if we’re going to play with turtles.
A turtle’s pen can be picked up or put down. This allows us to move a turtle to a different place without drawing a
line. The methods are
1 alex.penup()
2 alex.forward(100) # This moves alex, but no line is drawn
3 alex.pendown()

Every turtle can have its own shape. The ones available “out of the box” are arrow, blank, circle, classic,
square, triangle, turtle.
1 alex.shape("turtle")

We can speed up or slow down the turtle’s animation speed. (Animation controls how quickly the turtle turns and
moves forward). Speed settings can be set between 1 (slowest) to 10 (fastest). But if we set the speed to 0, it has a
special meaning — turn off animation and go as fast as possible.
1 alex.speed(10)

A turtle can “stamp” its footprint onto the canvas, and this will remain after the turtle has moved somewhere else.
Stamping works, even when the pen is up.
Let’s do an example that shows off some of these new features:
1 import turtle
2 wn = turtle.Screen()
3 wn.bgcolor("lightgreen")
4 tess = turtle.Turtle()
5 tess.shape("turtle")
6 tess.color("blue")
7

8 tess.penup() # This is new


9 size = 20
10 for i in range(30):
11 tess.stamp() # Leave an impression on the canvas
12 size = size + 3 # Increase the size on every iteration
(continues on next page)

4.8. A few more turtle methods and tricks 35


Programming in Python, Release 1st Edition

(continued from previous page)


13 tess.forward(size) # Move tess along
14 tess.right(24) # ... and turn her
15

16 wn.mainloop()

Be careful now! How many times was the body of the loop executed? How many turtle images do we see on the
screen? All except one of the shapes we see on the screen here are footprints created by stamp. But the program still
only has one turtle instance — can you figure out which one here is the real tess? (Hint: if you’re not sure, write a
new line of code after the for loop to change tess’ color, or to put her pen down and draw a line, or to change her
shape, etc.)

4.9 Glossary

attribute Some state or value that belongs to a particular object. For example, tess has a color.
canvas A surface within a window where drawing takes place.
control flow See flow of execution in the next chapter.
for loop A statement in Python for convenient repetition of statements in the body of the loop.
loop body Any number of statements nested inside a loop. The nesting is indicated by the fact that the statements are
indented under the for loop statement.
loop variable A variable used as part of a for loop. It is assigned a different value on each iteration of the loop.
instance An object of a certain type, or class. tess and alex are different instances of the class Turtle.
method A function that is attached to an object. Invoking or activating the method causes the object to respond in
some way, e.g. forward is the method when we say tess.forward(100).

36 Chapter 4. Looping & the way of the turtle


Programming in Python, Release 1st Edition

invoke An object has methods. We use the verb invoke to mean activate the method. Invoking a method is done
by putting parentheses after the method name, with some possible arguments. So tess.forward() is an
invocation of the forward method.
module A file containing Python definitions and statements intended for use in other Python programs. The contents
of a module are made available to the other program by using the import statement.
object A “thing” to which a variable can refer. This could be a screen window, or one of the turtles we have created.
range A built-in function in Python for generating sequences of integers. It is especially useful when we need to write
a for loop that executes a fixed number of times.
terminating condition A condition that occurs which causes a loop to stop repeating its body. In the for loops we
saw in this chapter, the terminating condition has been when there are no more elements to assign to the loop
variable.

4.9. Glossary 37
Programming in Python, Release 1st Edition

38 Chapter 4. Looping & the way of the turtle


CHAPTER

FIVE

CONDITIONALS

Programs get really interesting when we can test conditions and change the program behaviour depending on the
outcome of the tests. That’s what this chapter is about.

5.1 Boolean values and expressions

A Boolean value is either true or false. It is named after the British mathematician, George Boole, who first formulated
Boolean algebra — some rules for reasoning about and combining these values. This is the basis of all modern
computer logic.
In Python, the two Boolean values are True and False (the capitalization must be exactly as shown), and the Python
type is bool.

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

A Boolean expression is an expression that evaluates to produce a result which is a Boolean value. For example, the
operator == tests if two values are equal. It produces (or yields) a Boolean value:

>>> 5 == (3 + 2) # Is five equal 5 to the result of 3 + 2?


True
>>> 5 == 6
False
>>> j = "hel"
>>> j + "lo" == "hello"
True

In the first statement, the two operands evaluate to equal values, so the expression evaluates to True; in the second
statement, 5 is not equal to 6, so we get False.
The == operator is one of six common comparison operators which all produce a bool result; here are all six:

x == y # Produce True if ... x is equal to y


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

39
Programming in Python, Release 1st Edition

Although these operations are probably familiar, the Python symbols are different from the mathematical symbols. A
common error is to use a single equal sign (=) instead of a double equal sign (==). Remember that = is an assignment
operator and == is a comparison operator. Also, there is no such thing as =< or =>.
Like any other types we’ve seen so far, Boolean values can be assigned to variables, printed, etc.
>>> age = 18
>>> old_enough_to_get_driving_licence = age >= 17
>>> print(old_enough_to_get_driving_licence)
True
>>> type(old_enough_to_get_driving_licence)
<class 'bool'>

5.2 Logical operators

There are three logical operators, and, or, and not, that allow us to build more complex Boolean expressions from
simpler Boolean expressions. The semantics (meaning) of these operators is similar to their meaning in English. For
example, x > 0 and x < 10 produces True only if x is greater than 0 and at the same time, x is less than 10.
n % 2 == 0 or n % 3 == 0 is True if either of the conditions is True, that is, if the number n is divisible
by 2 or it is divisible by 3. (What do you think happens if n is divisible by both 2 and by 3 at the same time? Will the
expression yield True or False? Try it in your Python interpreter.)
Finally, the not operator negates a Boolean value, so not (x > y) is True if (x > y) is False, that is, if x is
less than or equal to y.
The expression on the left of the or operator is evaluated first: if the result is True, Python does not (and need not)
evaluate the expression on the right — this is called short-circuit evaluation. Similarly, for the and operator, if the
expression on the left yields False, Python does not evaluate the expression on the right.

5.3 Truth Tables

A truth table is a small table that allows us to list all the possible inputs, and to give the results for the logical operators.
Because the and and or operators each have two operands, there are only four rows in a truth table that describes the
semantics of and.

a b a and b
False False False
False True False
True False False
True True True

In a Truth Table, we sometimes use T and F as shorthand for the two Boolean values: here is the truth table describing
or:

a b a or b
F F F
F T T
T F T
T T T

The third logical operator, not, only takes a single operand, so its truth table only has two rows:

40 Chapter 5. Conditionals
Programming in Python, Release 1st Edition

a not a
F T
T F

5.4 Simplifying Boolean Expressions

A set of rules for simplifying and rearranging expressions is called an algebra. For example, we are all familiar with
school algebra rules, such as:

n * 0 == 0

Here we see a different algebra — the Boolean algebra — which provides rules for working with Boolean values.
First, the and operator:

x and False == False


False and x == False
y and x == x and y
x and True == x
True and x == x
x and x == x

Here are some corresponding rules for the or operator:

x or False == x
False or x == x
y or x == x or y
x or True == True
True or x == True
x or x == x

Two not operators cancel each other:

not (not x) == x

5.5 Conditional execution

In order to write useful programs, we almost always need the ability to check conditions and change the behavior of
the program accordingly. Conditional statements give us this ability. The simplest form is the if statement:

1 if x % 2 == 0:
2 print(x, " is even.")
3 print("Did you know that 2 is the only even number that is prime?")
4 else:
5 print(x, " is odd.")
6 print("Did you know that multiplying two odd numbers " +
7 "always gives an odd result?")

The Boolean expression after the if statement is called the condition. If it is true, then all the indented statements get
executed. If not, then all the statements indented under the else clause get executed.

Flowchart of an if statement with an else clause

5.4. Simplifying Boolean Expressions 41


Programming in Python, Release 1st Edition

The syntax for an if statement looks like this:

1 if BOOLEAN EXPRESSION:
2 STATEMENTS_1 # Executed if condition evaluates to True
3 else:
4 STATEMENTS_2 # Executed if condition evaluates to False

As with the function definition from the last chapter and other compound statements like for, the if statement
consists of a header line and a body. The header line begins with the keyword if followed by a Boolean expression
and ends with a colon (:).
The indented statements that follow are called a block. The first unindented statement marks the end of the block.
Each of the statements inside the first block of statements are executed in order if the Boolean expression evaluates to
True. The entire first block of statements is skipped if the Boolean expression evaluates to False, and instead all
the statements indented under the else clause are executed.
There is no limit on the number of statements that can appear under the two clauses of an if statement, but there has
to be at least one statement in each block. Occasionally, it is useful to have a section with no statements (usually as
a place keeper, or scaffolding, for code we haven’t written yet). In that case, we can use the pass statement, which
does nothing except act as a placeholder.

1 if True: # This is always True,


2 pass # so this is always executed, but it does nothing
3 else:
4 pass

Remark
We discussed this before: the exact amount of indentation does not matter, only the relative indentation of nested
blocks. Still, it is encouraged to use four space (or one tab set to four spaces) per indentation level. It does not matter
if you use tabs or spaces as long as you do not mix them! Check you editor settings.

42 Chapter 5. Conditionals
Programming in Python, Release 1st Edition

5.6 Omitting the else clause

Flowchart of an if statement with no else clause

Another form of the if statement is one in which the else clause is omitted entirely. In this case, when the condition
evaluates to True, the statements are executed, otherwise the flow of execution continues to the statement after the
if.

1 if x < 0:
2 print("The negative number ", x, " is not valid here.")
3 x = 42
4 print("I've decided to use the number 42 instead.")
5

6 print("The square root of ", x, "is", math.sqrt(x))

In this case, the print function that outputs the square root is the one after the if — not because we left a blank line,
but because of the way the code is indented. Note too that the function call math.sqrt(x) will give an error unless
we have an import math statement, usually placed near the top of our script.

Python terminology
Python documentation sometimes uses the term suite of statements to mean what we have called a block here. They
mean the same thing, and since most other languages and computer scientists use the word block, we’ll stick with that.
Notice too that else is not a statement. The if statement has two clauses, one of which is the (optional) else
clause.

5.6. Omitting the else clause 43


Programming in Python, Release 1st Edition

5.7 Chained conditionals

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

1 if x < y:
2 STATEMENTS_A
3 elif x > y:
4 STATEMENTS_B
5 else:
6 STATEMENTS_C

Flowchart of this chained conditional

elif is an abbreviation of else if. Again, exactly one branch will be executed. There is no limit of the number of
elif statements but only a single (and optional) final else statement is allowed and it must be the last branch in the
statement:

1 if choice == "a":
2 STATEMENTS_A
3 elif choice == "b":
4 STATEMENTS_B
5 elif choice == "c":
6 STATEMENTS_C
7 else:
8 print("Invalid choice.")

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

44 Chapter 5. Conditionals
Programming in Python, Release 1st Edition

5.8 Nested conditionals

One conditional can also be nested within another. (It is the same theme of composability, again!) We could have
written the previous example as follows:

Flowchart of this nested conditional

1 if x < y:
2 STATEMENTS_A
3 else:
4 if x > y:
5 STATEMENTS_B
6 else:
7 STATEMENTS_C

The outer conditional contains two branches. The second branch contains another if statement, which has two
branches of its own. Those two branches could contain conditional statements as well.
Although the indentation of the statements makes the structure apparent, nested conditionals very quickly become
difficult to read. In general, it is a good idea to avoid them when we can.
Logical operators often provide a way to simplify nested conditional statements. For example, we can rewrite the
following code using a single conditional:

1 if 0 < x: # Assume x is an int here


2 if x < 10:
3 print("x is a positive single digit.")

The print function is called only if we make it past both the conditionals, so instead of the above which uses two
if statements each with a simple condition, we could make a more complex condition using the and operator. Now
we only need a single if statement:

5.8. Nested conditionals 45


Programming in Python, Release 1st Edition

1 if 0 < x and x < 10:


2 print("x is a positive single digit.")

5.9 Logical opposites

Each of the six relational operators has a logical opposite: for example, suppose we can get a driving licence when our
age is greater or equal to 17, we can not get the driving licence when we are less than 17.
Notice that the opposite of >= is <.

operator logical opposite


== !=
!= ==
< >=
<= >
> <=
>= <

Understanding these logical opposites allows us to sometimes get rid of not operators. not operators are often quite
difficult to read in computer code, and our intentions will usually be clearer if we can eliminate them.
For example, if we wrote this Python:

1 if not (age >= 17):


2 print("Hey, you're too young to get a driving licence!")

it would probably be clearer to use the simplification laws, and to write instead:

1 if age < 17:


2 print("Hey, you're too young to get a driving licence!")

Two powerful simplification laws (called de Morgan’s laws) that are often helpful when dealing with complicated
Boolean expressions are:

not (x and y) == (not x) or (not y)


not (x or y) == (not x) and (not y)

For example, suppose we can slay the dragon only if our magic lightsabre sword is charged to 90% or higher, and we
have 100 or more energy units in our protective shield. We find this fragment of Python code in the game:

1 if not ((sword_charge >= 0.90) and (shield_energy >= 100)):


2 print("Your attack has no effect, the dragon fries you to a crisp!")
3 else:
4 print("The dragon crumples in a heap. You rescue the gorgeous princess!")

de Morgan’s laws together with the logical opposites would let us rework the condition in a (perhaps) easier to under-
stand way like this:

1 if (sword_charge < 0.90) or (shield_energy < 100):


2 print("Your attack has no effect, the dragon fries you to a crisp!")
3 else:
4 print("The dragon crumples in a heap. You rescue the gorgeous princess!")

46 Chapter 5. Conditionals
Programming in Python, Release 1st Edition

We could also get rid of the not by swapping around the then and else parts of the conditional. So here is a third
version, also equivalent:

1 if (sword_charge >= 0.90) and (shield_energy >= 100):


2 print("The dragon crumples in a heap. You rescue the gorgeous princess!")
3 else:
4 print("Your attack has no effect, the dragon fries you to a crisp!")

This version is probably the best of the three, because it very closely matches the initial English statement. Clarity of
our code (for other humans), and making it easy to see that the code does what was expected should always be a high
priority.
As our programming skills develop we’ll find we have more than one way to solve any problem. So good programs
are designed. We make choices that favour clarity, simplicity, and elegance. The job title software architect says a
lot about what we do — we are architects who engineer our products to balance beauty, functionality, simplicity and
clarity in our creations.

Tip: Once our program works, we should play around a bit trying to polish it up. Write good comments. Think about
whether the code would be clearer with different variable names. Could we have done it more elegantly? Should we
rather use a function? Can we simplify the conditionals?
We think of our code as our creation, our work of art! We make it great.

5.10 Glossary

block A group of consecutive statements with the same indentation.


body The block of statements in a compound statement that follows the header.
Boolean algebra Some rules for rearranging and reasoning about Boolean expressions.
Boolean expression An expression that is either true or false.
Boolean value There are exactly two Boolean values: True and False. Boolean values result when a Boolean
expression is evaluated by the Python interepreter. They have type bool.
branch One of the possible paths of the flow of execution determined by conditional execution.
chained conditional A conditional branch with more than two possible flows of execution. In Python chained condi-
tionals are written with if ... elif ... else statements.
comparison operator One of the six operators that compares two values: ==, !=, >, <, >=, and <=.
condition The Boolean expression in a conditional statement that determines which branch is executed.
conditional statement A statement that controls the flow of execution depending on some condition. In Python the
keywords if, elif, and else are used for conditional statements.
logical operator One of the operators that combines Boolean expressions: and, or, and not.
nesting One program structure within another, such as a conditional statement inside a branch of another conditional
statement.
prompt A visual cue that tells the user that the system is ready to accept input data.
truth table A concise table of Boolean values that can describe the semantics of an operator.
type conversion An explicit function call that takes a value of one type and computes a corresponding value of another
type.

5.10. Glossary 47
Programming in Python, Release 1st Edition

48 Chapter 5. Conditionals
CHAPTER

SIX

ITERATION

Computers are often used to automate repetitive tasks. Repeating identical or similar tasks without making errors is
something that computers do well and people do poorly.
Repeated execution of a set of statements is called iteration. Because iteration is so common, Python provides several
language features to make it easier. We’ve already seen the for statement in chapter 3. This the form of iteration
you’ll likely be using most often. But in this chapter we’ve going to look at the while statement — another way to
have your program do iteration, useful in slightly different circumstances.
Before we do that, let’s just review a few ideas. . .

6.1 Assignment

As we have mentioned previously, it is legal to make more than one assignment to the same variable. A new assignment
makes an existing variable refer to a new value (and stop referring to the old value).

1 airtime_remaining = 15
2 print(airtime_remaining)
3 airtime_remaining = 7
4 print(airtime_remaining)

The output of this program is:

15
7

because the first time airtime_remaining is printed, its value is 15, and the second time, its value is 7.
It is especially important to distinguish between an assignment statement and a Boolean expression that tests for
equality. Because Python uses the equal token (=) for assignment, it is tempting to interpret a statement like a = b
as a Boolean test. Unlike mathematics, it is not! Remember that the Python token for the equality operator is ==.
Note too that an equality test is symmetric, but assignment is not. For example, if a == 7 then 7 == a. But in
Python, the statement a = 7 is legal and 7 = a is not.
In Python, an assignment statement can make two variables equal, but because further assignments can change either
of them, they don’t have to stay that way:

1 a = 5
2 b = a # After executing this line, a and b are now equal
3 a = 3 # After executing this line, a and b are no longer equal

The third line changes the value of a but does not change the value of b, so they are no longer equal. In some
programming languages, a different symbol is used for assignment, such as <- or :=, to avoid confusion. Some

49
Programming in Python, Release 1st Edition

people also think that variable was an unfortunate word to choose, and instead we should have called them assignables.
Python chooses to follow common terminology and token usage, also found in languages like C, C++, Java, and C#,
so we use the tokens = for assignment, == for equality, and we talk of variables.

6.2 Updating variables

When an assignment statement is executed, the right-hand side expression (i.e. the expression that comes after the
assignment token) is evaluated first. This produces a value. Then the assignment is made, so that the variable on the
left-hand side now refers to the new value.
One of the most common forms of assignment is an update, where the new value of the variable depends on its old
value. For instance, deduct 40 cents from my airtime balance or add one run to the scoreboard.

1 n = 5
2 n = 3 * n + 1

Line 2 means get the current value of n, multiply it by three and add one, and assign the answer to n, thus making n
refer to the value. So after executing the two lines above, n will point/refer to the integer 16.
If you try to get the value of a variable that has never been assigned to, you’ll get an error:

>>> w = x + 1
Traceback (most recent call last):
File "<interactive input>", line 1, in
NameError: name 'x' is not defined

Before you can update a variable, you have to initialize it to some starting value, usually with a simple assignment:

1 runs_scored = 0
2 ...
3 runs_scored = runs_scored + 1

Line 3 — updating a variable by adding 1 to it — is very common. It is called an increment of the variable; subtracting
1 is called a decrement. Sometimes programmers also talk about bumping a variable, which means the same as
incrementing it by 1.

6.3 The for loop revisited

Recall that the for loop processes each item in a list. Each item in turn is (re-)assigned to the loop variable, and the
body of the loop is executed. We saw this example in an earlier chapter:

1 for f in ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]:


2 invitation = "Hi " + f + ". Please come to my party on Saturday!"
3 print(invitation)

Running through all the items in a list is called traversing the list, or traversal.
Let us write a program to add up all the elements in a list of numbers. Do this by hand first, and try to isolate exactly
what steps you take. You’ll find you need to keep some “running total” of the sum so far, either on a piece of paper, in
your head, or in your calculator. Remembering things from one step to the next is precisely why we have variables in
a program: so we’ll need some variable to remember the “running total”. It should be initialized with a value of zero,
and then we need to traverse the items in the list. For each item, we’ll want to update the running total by adding the
next number to it.

50 Chapter 6. Iteration
Programming in Python, Release 1st Edition

1 n = 1001
2 running_total = 0
3 for x in range(n):
4 running_total = running_total + x
5 print(running_total)

6.4 The while statement

Here is a fragment of code that demonstrates the use of the while statement:

1 n = 1000
2 sum = 0
3 v = 1
4 while v <= n:
5 sum = sum + v
6 v = v + 1
7 print(sum)

You can almost read the while statement as if it were English. It means, while v is less than or equal to n, continue
executing the body of the loop. Within the body, each time, increment v. When v passes n, return your accumulated
sum.
More formally, here is precise flow of execution for a while statement:
• Evaluate the condition at line 4, yielding a value which is either False or True.
• If the value is False, exit the while statement and continue execution at the next statement (line 7 in this
case).
• If the value is True, execute each of the statements in the body (lines 5 and 6) and then go back to the while
statement at line 4.
The body consists of all of the statements indented below the while keyword.
Notice that if the loop condition is False the first time we get loop, the statements in the body of the loop are never
executed.
The body of the loop should change the value of one or more variables so that eventually the condition becomes false
and the loop terminates. Otherwise the loop will repeat forever, which is called an infinite loop. An endless source
of amusement for computer scientists is the observation that the directions on shampoo, “lather, rinse, repeat”, are an
infinite loop.
In the case here, we can prove that the loop terminates because we know that the value of n is finite, and we can see
that the value of v increments each time through the loop, so eventually it will have to exceed n. In other cases, it is
not so easy, even impossible in some cases, to tell if the loop will ever terminate.
What you will notice here is that the while loop is more work for you — the programmer — than the equivalent
for loop. When using a while loop one has to manage the loop variable yourself: give it an initial value, test for
completion, and then make sure you change something in the body so that the loop terminates.
So why have two kinds of loop if for looks easier? This next example shows a case where we need the extra power
that we get from the while loop.

6.4. The while statement 51


Programming in Python, Release 1st Edition

6.5 The Collatz 3n + 1 sequence

Let’s look at a simple sequence that has fascinated and foxed mathematicians for many years. They still cannot answer
even quite simple questions about this.
The “computational rule” for creating the sequence is to start from some given n, and to generate the next term of
the sequence from n, either by halving n, (whenever n is even), or else by multiplying it by three and adding 1. The
sequence terminates when n reaches 1.
This Python program captures that algorithm:

1 n = 123456
2 while n != 1:
3 print(n, end=", ")
4 if n % 2 == 0: # n is even
5 n = n // 2
6 else: # n is odd
7 n = n * 3 + 1
8 print(n, end=".\n")

Notice first that the print function on line 6 has an extra argument end=", ". This tells the print function to
follow the printed string with whatever the programmer chooses (in this case, a comma followed by a space), instead
of ending the line. So each time something is printed in the loop, it is printed on the same output line, with the numbers
separated by commas. The call to print(n, end=".\n") at line 11 after the loop terminates will then print the
final value of n followed by a period and a newline character. (You’ll cover the \n (newline character) in the next
chapter).
The condition for continuing with this loop is n != 1, so the loop will continue running until it reaches its termination
condition, (i.e. n == 1).
Each time through the loop, the program outputs the value of n and then checks whether it is even or odd. If it is even,
the value of n is divided by 2 using integer division. If it is odd, the value is replaced by n * 3 + 1. Here are some
examples for different values of n:

n = 3
3, 10, 5, 16, 8, 4, 2, 1.

n = 19
19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13,
40, 20, 10, 5, 16, 8, 4, 2, 1.

n = 21
21, 64, 32, 16, 8, 4, 2, 1.

n = 16
16, 8, 4, 2, 1.

Since n sometimes increases and sometimes decreases, there is no obvious proof that n will ever reach 1, or that the
program terminates. For some particular values of n, we can prove termination. For example, if the starting value is
a power of two, then the value of n will be even each time through the loop until it reaches 1. The previous example
ends with such a sequence, starting with 16.
See if you can find a small starting number that needs more than a hundred steps before it terminates.
Particular values aside, the interesting question was first posed by a German mathematician called Lothar Collatz: the
Collatz conjecture (also known as the 3n + 1 conjecture), is that this sequence terminates for all positive values of n.
So far, no one has been able to prove it or disprove it! (A conjecture is a statement that might be true, but nobody
knows for sure.)

52 Chapter 6. Iteration
Programming in Python, Release 1st Edition

Think carefully about what would be needed for a proof or disproof of the conjecture “All positive integers will
eventually converge to 1 using the Collatz rules”. With fast computers we have been able to test every integer up to
very large values, and so far, they have all eventually ended up at 1. But who knows? Perhaps there is some as-yet
untested number which does not reduce to 1.
You’ll notice that if you don’t stop when you reach 1, the sequence gets into its own cyclic loop: 1, 4, 2, 1, 4, 2, 1, 4
. . . So one possibility is that there might be other cycles that we just haven’t found yet.
Wikipedia has an informative article about the Collatz conjecture. The sequence also goes under other names (Hail-
stone sequence, Wonderous numbers, etc.), and you’ll find out just how many integers have already been tested by
computer, and found to converge!

Choosing between for and while


Use a for loop if you know, before you start looping, the maximum number of times that you’ll need to execute the
body. For example, if you’re traversing a list of elements, you know that the maximum number of loop iterations you
can possibly need is “all the elements in the list”. Or if you need to print the 12 times table, we know right away how
many times the loop will need to run.
So any problem like “iterate this weather model for 1000 cycles”, or “search this list of words”, “find all prime numbers
up to 10000” suggest that a for loop is best.
By contrast, if you are required to repeat some computation until some condition is met, and you cannot calculate in
advance when (of if) this will happen, as we did in this 3n + 1 problem, you’ll need a while loop.
We call the first case definite iteration — we know ahead of time some definite bounds for what is needed. The latter
case is called indefinite iteration — we’re not sure how many iterations we’ll need — we cannot even establish an
upper bound!

6.6 Counting digits

The following program counts the number of decimal digits in a positive integer:

1 n = 123456789
2 count = 0
3 while n != 0:
4 count = count + 1
5 n = n // 10
6 print(count)

The output of this program is 9. Trace the execution of this function call with Python Tutor to convince yourself that
it works. See how the value of n decreases at every iteration.
This function demonstrates an important pattern of computation called a counter. The variable count is initialized
to 0 and then incremented each time the loop body is executed. When the loop exits, count contains the result — the
total number of times the loop body was executed, which is the same as the number of digits.
If we wanted to only count digits that are either 0 or 5, adding a conditional before incrementing the counter will do
the trick:

1 n = 123456789
2 count = 0
3 while n > 0:
4 digit = n % 10
5 if digit == 0 or digit == 5:
(continues on next page)

6.6. Counting digits 53


Programming in Python, Release 1st Edition

(continued from previous page)


6 count = count + 1
7 n = n // 10
8 print(count)

6.7 Abbreviated assignment

Incrementing a variable is so common that Python provides an abbreviated syntax for it:

>>> count = 0
>>> count += 1
>>> count
1
>>> count += 1
>>> count
2

count += 1 is an abreviation for count = count + 1 . We pronounce the operator as “plus-equals”. The
increment value does not have to be 1:

>>> n = 2
>>> n += 5
>>> n
7

There are similar abbreviations for -=, *=, /=, //= and %=:

>>> n = 2
>>> n *= 5
>>> n
10
>>> n -= 4
>>> n
6
>>> n //= 2
>>> n
3
>>> n %= 2
>>> n
1

6.8 The break statement

The break statement is used to immediately leave the body of its loop. The next statement to be executed is the first
one after the body:

1 for i in [12, 16, 17, 24, 29]:


2 if i % 2 == 1: # If the number is odd
3 break # ... immediately exit the loop
4 print(i)
5 print("done")

This prints:

54 Chapter 6. Iteration
Programming in Python, Release 1st Edition

12
16
done

Pre-test loops first perform a test and only when the test evaluates to true evaluate the body:

The pre-test loop — standard loop behaviour


for and while loops do their tests at the start, before executing any part of the body. They’re called pre-test loops,
because the test happens before (pre) the body. break and return are our tools for adapting this standard behaviour.
The return statement will be treated in the next chapter.

6.9 Other flavours of loops

Sometimes we’d like to have the middle-test loop with the exit test in the middle of the body, rather than at the
beginning or at the end. Or a post-test loop that puts its exit test as the last thing in the body. Other languages
have different syntax and keywords for these different flavours, but Python just uses a combination of while and if
condition: break to get the job done.
A typical example is a problem where the user has to input numbers to be summed. To indicate that there are no more
inputs, the user enters a special value, often the value -1, or the empty string. This needs a middle-exit loop pattern:
input the next number, then test whether to exit, or else process the number:

The middle-test loop flowchart

6.9. Other flavours of loops 55


Programming in Python, Release 1st Edition

1 total = 0
2 while True:
3 response = input("Enter the next number. (Leave blank to end)")
4 if response == "":
5 break
6 total += int(response)
7 print("The total of the numbers you entered is ", total)

Convince yourself that this fits the middle-exit loop flowchart: line 3 does some useful work, lines 4 and 5 can exit the
loop, and if they don’t line 6 does more useful work before the next iteration starts.
The while bool-expr: uses the Boolean expression to determine whether to iterate again. True is a trivial
Boolean expression, so while True: means always do the loop body again. This is a language idiom — a conven-
tion that most programmers will recognize immediately. Since the expression on line 2 will never terminate the loop,
(it is a dummy test) the programmer must arrange to break out of the loop body elsewhere, in some other way (i.e. in
lines 4 and 5 in this sample). A clever compiler or interpreter will understand that line 2 is a fake test that must always
succeed, so it won’t even generate a test, and our flowchart never even put the diamond-shape dummy test box at the
top of the loop!
By just moving the if condition: break to the end of the loop body we create a pattern for a post-test loop.
Post-test loops are used when you want to be sure that the loop body always executes at least once (because the first
test only happens at the end of the execution of the first loop body). This is useful, for example, if we want to play an
interactive game against the user — we always want to play at least one game:

1 while True:
2 play_the_game_once()
3 response = input("Play again? (yes or no)")
4 if response != "yes":
5 break
6 print("Goodbye!")

We note that the above programs can be easily rewritten to avoid the use of break. Consider for instance the following
program:

1 total = 0
2 response = input("Enter the next number. (Leave blank to end)")
3 while response != "":
4 total += int(response)
(continues on next page)

56 Chapter 6. Iteration
Programming in Python, Release 1st Edition

(continued from previous page)


5 response = input("Enter the next number. (Leave blank to end)")
6 print("The total of the numbers you entered is ", total)

6.10 An example

The following program implements a simple guessing game:


1 import random
2 number = random.randrange(1, 1000) # Get random number between [1 and 1000).
3

4 guesses = 0
5 msg = ""
6

7 while True:
8 guess = int(input(msg + "\nGuess my number between 1 and 1000: "))
9 guesses += 1
10 if guess > number:
11 msg += str(guess) + " is too high.\n"
12 elif guess < number:
13 msg += str(guess) + " is too low.\n"
14 else:
15 break
16

17 print("\n\nGreat, you got it in",guesses, "guesses.")

This program makes use of the mathematical law of trichotomy (given real numbers a and b, exactly one of these
three must be true: a > b, a < b, or a == b).
At line 8 there is a call to the input function, but we don’t do anything with the result, not even assign it to a variable.
This is legal in Python. Here it has the effect of popping up the input dialog window and waiting for the user to respond
before the program terminates. Programmers often use the trick of doing some extra input at the end of a script, just
to keep the window open.
Also notice the use of the msg variable, initially an empty string, on lines 6, 12 and 14. Each time through the loop we
extend the message being displayed: this allows us to display the program’s feedback right at the same place as we’re
asking for the next guess.

6.11 The continue statement

This is a control flow statement that causes the program to immediately skip the processing of the rest of the body of
the loop, for the current iteration. But the loop still carries on running for its remaining iterations:
1 for i in [12, 16, 17, 24, 29, 30]:
2 if i % 2 == 1: # If the number is odd
3 continue # Don't process it
4 print(i)
5 print("done")

This prints:
12
16
(continues on next page)

6.10. An example 57
Programming in Python, Release 1st Edition

(continued from previous page)


24
30
done

Statements like break and continue should be used with moderation and alternatives without them are encouraged
as long as they do not break the readability of the program.

6.12 Syntax and semantics

Syntax & semantics: while


Syntax

while boolean_condition:
while_body

Semantics When boolean_condition evalutes to True the statements in while_body are


executed. The latter process is repeated until boolean_condition evaluates to False.
In that case, control jumps to the first statement after the while loop. Notice that when
boolean_condition is False the first time, the statements in while_body are never ex-
ecuted.

6.13 Glossary

body The statements inside a loop.


breakpoint A place in your program code where program execution will pause (or break), allowing you to inspect
the state of the program’s variables, or single-step through individual statements, executing them one at a time.
bump Programmer slang. Synonym for increment.
continue statement A statement that causes the remainder of the current iteration of a loop to be skipped. The flow
of execution goes back to the top of the loop, evaluates the condition, and if this is true the next iteration of the
loop will begin.
counter A variable used to count something, usually initialized to zero and incremented in the body of a loop.
decrement Decrease by 1.
definite iteration A loop where we have an upper bound on the number of times the body will be executed. Definite
iteration is usually best coded as a for loop.
increment Both as a noun and as a verb, increment means to increase by 1.
infinite loop A loop in which the terminating condition is never satisfied.
indefinite iteration A loop where we just need to keep going until some condition is met. A while statement is
used for this case.
initialization (of a variable) To initialize a variable is to give it an initial value. Since in Python variables don’t exist
until they are assigned values, they are initialized when they are created. In other programming languages this
is not the case, and variables can be created without being initialized, in which case they have either default or
garbage values.

58 Chapter 6. Iteration
Programming in Python, Release 1st Edition

iteration Repeated execution of a set of programming statements.


loop The construct that allows allows us to repeatedly execute a statement or a group of statements until a terminating
condition is satisfied.
loop variable A variable used as part of the terminating condition of a loop.
middle-test loop A loop that executes some of the body, then tests for the exit condition, and then may execute some
more of the body. We don’t have a special Python construct for this case, but can use while and break
together.
nested loop A loop inside the body of another loop.
newline A special character that causes the cursor to move to the beginning of the next line.
post-test loop A loop that executes the body, then tests for the exit condition. We don’t have a special Python construct
for this, but can use while and break together.
pre-test loop A loop that tests before deciding whether the execute its body. for and while are both pre-test loops.

6.13. Glossary 59
Programming in Python, Release 1st Edition

60 Chapter 6. Iteration
CHAPTER

SEVEN

FUNCTIONS

7.1 Functions

In Python, a function is a named sequence of statements that belong together. Their primary purpose is to help us
organize programs into chunks that match how we think about the problem.
The syntax for a function definition is:

def NAME( PARAMETERS ):


STATEMENTS

We can make up any names we want for the functions we create, except that we can’t use a name that is a Python
keyword, and the names must follow the rules for legal identifiers.
There can be any number of statements inside the function, but they have to be indented from the def. In the
examples in this book, we will use the standard indentation of four spaces. A function definition is another instance of
a compound statement, all of which have the same pattern:
1. A header line which begins with a keyword and ends with a colon.
2. A body consisting of one or more Python statements, each indented the same amount — the Python style guide
recommends 4 spaces — from the header line.
We’ve already seen the for loop which follows this pattern.
So looking again at the function definition, the keyword in the header is def, which is followed by the name of the
function and some parameters enclosed in parentheses. The parameter list may be empty, or it may contain any number
of parameters separated from one another by commas. In either case, the parentheses are required. The parameters
specifies what information, if any, we have to provide in order to use the new function.
Suppose we’re working with turtles, and a common operation we need is to draw squares. “Draw a square” is an
abstraction, or a mental chunk, of a number of smaller steps. So let’s write a function to capture the pattern of this
“building block”:

1 import turtle
2

3 def draw_square(t, sz):


4 """Make turtle t draw a square of sz."""
5 for i in range(4):
6 t.forward(sz)
7 t.left(90)
8

10 wn = turtle.Screen() # Set up the window and its attributes


11 wn.bgcolor("lightgreen")
(continues on next page)

61
Programming in Python, Release 1st Edition

(continued from previous page)


12 wn.title("Alex meets a function")
13

14 alex = turtle.Turtle() # Create alex


15 draw_square(alex, 50) # Call the function to draw the square
16 wn.mainloop()

This function is named draw_square. It has two parameters: one to tell the function which turtle to move around,
and the other to tell it the size of the square we want drawn. Make sure you know where the body of the function ends
— it depends on the indentation, and the blank lines don’t count for this purpose!

Docstrings for documentation


If the first thing after the function header is a string, it is treated as a docstring and gets special treatment in Python
and in some programming tools.
Docstrings are the key way to document our functions in Python and the documentation part is important. Because
whoever calls our function shouldn’t have to need to know what is going on in the function or how it works; they just
need to know what arguments our function takes, what it does, and what the expected result is. Enough to be able to
use the function without having to look underneath. This goes back to the concept of abstraction of which we’ll talk
more about.
Docstrings are usually formed using triple-quoted strings as they allow us to easily expand the docstring later on should
we want to write more than a one-liner.
Just to differentiate from comments, a string at the start of a function (a docstring) is retrievable by Python tools at
runtime. By contrast, comments are completely eliminated when the program is parsed.

Defining a new function does not make the function run. To do that we need a function call. We’ve already seen
how to call some built-in functions like print, range and int. Function calls contain the name of the function being
executed followed by a list of values, called arguments, which are assigned to the parameters in the function definition.
So in the second last line of the program, we call the function, and pass alex as the turtle to be manipulated, and 50
as the size of the square we want. While the function is executing, then, the variable sz refers to the value 50, and the
variable t refers to the same turtle instance that the variable alex refers to.
Once we’ve defined a function, we can call it as often as we like, and its statements will be executed each time we
call it. And we could use it to get any of our turtles to draw a square. In the next example, we’ve changed the
draw_square function a little, and we get tess to draw 15 squares, with some variations.

62 Chapter 7. Functions
Programming in Python, Release 1st Edition

1 import turtle
2

3 def draw_multicolor_square(t, sz):


4 """Make turtle t draw a multi-color square of sz."""
5 for i in ["red", "purple", "hotpink", "blue"]:
6 t.color(i)
7 t.forward(sz)
8 t.left(90)
9

10 wn = turtle.Screen() # Set up the window and its attributes


11 wn.bgcolor("lightgreen")
12

13 tess = turtle.Turtle() # Create tess and set some attributes


14 tess.pensize(3)
15

16 size = 20 # Size of the smallest square


17 for i in range(15):
18 draw_multicolor_square(tess, size)
19 size = size + 10 # Increase the size for next time
20 tess.forward(10) # Move tess along a little
21 tess.right(18) # and give her some turn
22

23 wn.mainloop()

7.2 Functions can call other functions

Let’s assume now we want a function to draw a rectangle. We need to be able to call the function with different
arguments for width and height. And, unlike the case of the square, we cannot repeat the same thing 4 times, because
the four sides are not equal.
So we eventually come up with this rather nice code that can draw a rectangle.

1 def draw_rectangle(t, w, h):


2 """Get turtle t to draw a rectangle of width w and height h."""
3 for i in range(2):
4 t.forward(w)
5 t.left(90)
6 t.forward(h)
7 t.left(90)

The parameter names are deliberately chosen as single letters to ensure they’re not misunderstood. In real programs,
once we’ve had more experience, we will insist on better variable names than this. But the point is that the program
doesn’t “understand” that we’re drawing a rectangle, or that the parameters represent the width and the height. Con-

7.2. Functions can call other functions 63


Programming in Python, Release 1st Edition

cepts like rectangle, width, and height are the meaning we humans have, not concepts that the program or the computer
understands.
Thinking like a scientist involves looking for patterns and relationships. In the code above, we’ve done that to some
extent. We did not just draw four sides. Instead, we spotted that we could draw the rectangle as two halves, and used
a loop to repeat that pattern twice.
But now we might spot that a square is a special kind of rectangle. We already have a function that draws a rectangle,
so we can use that to draw our square.

1 def draw_square(tx, sz): # A new version of draw_square


2 draw_rectangle(tx, sz, sz)

There are some points worth noting here:


• Functions can call other functions.
• Rewriting draw_square like this captures the relationship that we’ve spotted between squares and rectangles.
• A caller of this function might say draw_square(tess, 50). The parameters of this function, tx and sz,
are assigned the values of the tess object, and the int 50 respectively.
• In the body of the function they are just like any other variable.
• When the call is made to draw_rectangle, the values in variables tx and sz are fetched first, then the call
happens. So as we enter the top of function draw_rectangle, its variable t is assigned the tess object, and
w and h in that function are both given the value 50.
So far, it may not be clear why it is worth the trouble to create all of these new functions. Actually, there are a lot of
reasons, but this example demonstrates two:
1. Creating a new function gives us an opportunity to name a group of statements. Functions can simplify a
program by hiding a complex computation behind a single command. The function (including its name) can
capture our mental chunking, or abstraction, of the problem.
2. Creating a new function can make a program smaller by eliminating repetitive code.
As we might expect, we have to create a function before we can execute it. In other words, the function definition has
to be executed before the function is called.

7.3 Flow of execution

In order to ensure that a function is defined before its first use, we have to know the order in which statements are
executed, which is called the flow of execution. We’ve already talked about this a little in the previous chapters.
Execution always begins at the first statement of the program. Statements are executed one at a time, in order from top
to bottom. Function definitions do not alter the flow of execution of the program, but remember that statements inside
the function are not executed until the function is called.
Function calls are like a detour in the flow of execution. Instead of going to the next statement, the flow jumps to the
first line of the called function, executes all the statements there, and then comes back to pick up where it left off.
That sounds simple enough, until we remember that one function can call another. While in the middle of one function,
the program might have to execute the statements in another function. But while executing that new function, the
program might have to execute yet another function!
Fortunately, Python is adept at keeping track of where it is, so each time a function completes, the program picks up
where it left off in the function that called it. When it gets to the end of the program, it terminates.
What’s the moral of this sordid tale? When we read a program, don’t read from top to bottom. Instead, follow the flow
of execution.

64 Chapter 7. Functions
Programming in Python, Release 1st Edition

Let us have a look at an example in Python Tutor:

1 def f(x):
2 print("f: before")
3 g(x)
4 print("f: after")
5

6 def g(x):
7 print("g:",x)
8

9 f(10)

Stepping through the program, it becomes apparent that, much like variables, functions are defined or created only
when the Python interpreter encounters a def command. For instance, the function f is created before the function
g. Notice that the creation of functions f and g does not result in executing them. Only, the function call in line 10
results in an execution. Stepping through the program shows how function f calls function g in line 3, only to return
at line 4 after g is completely executed.
Understanding flow of execution is of major importance to predict (and therefore understand) the behavior of programs.
Have a look at the following program. What will happen when it is executed?

1 f(10)
2

3 def f(x):
4 print("f: before")
5 g(x)
6 print("f: after")
7

8 def g(x):
9 print("g:",x)

As mentioned above, functions do not exist before the def command defining them is encountered. As the function
call f(10) is invoked before def f(x): the function f is unkown to the Python interpreter and we get the following
error:

NameError: name 'f' is not defined

Such behavior is typical for interpreted languages like Python.

7.4 Functions that return values

Functions like range, int, abs all return values that can be used to build more complex expressions. So an important
difference between these functions and one like draw_square is that draw_square was not executed because we
wanted it to compute a value — on the contrary, we wrote draw_square because we wanted it to execute a sequence
of steps that caused the turtle to draw.
A function that returns a value is called a fruitful function in this book. The opposite of a fruitful function is
void function — one that is not executed for its resulting value, but is executed because it does something useful.
(Languages like Java, C#, C and C++ use the term “void function”, other languages like Pascal call it a procedure.)
Even though void functions are not executed for their resulting value, Python always wants to return something. So if
the programmer doesn’t arrange to return a value, Python will automatically return the value None.
How do we write our own fruitful function? Recall that the formula for computing the area of a circle is the following:

7.4. Functions that return values 65


Programming in Python, Release 1st Edition

1 def circle_area(r):
2 """
3 Apply the area formula for a circle of
4 radius r.
5 """
6

7 area = 3.14159 * r**2


8 return area
9

10 radius = input("What is your radius? ")


11 radius = float(radius)
12 result = circle_area(radius)
13 print("The area is", result)

• The return statement is followed by an expression (area in this case). This expression will be evaluated and
returned to the caller as the “fruit” of calling this function. Notice that return does not require parenthesis around
its argument. Actually, return is a statement, not a function.
• We prompted the user for the radius. Because it could have decimal places, we’ve used the float type converter
function (rather than int) to parse the string and return a float.
Notice something else very important here. The name of the variable we pass as an argument — radius — has
nothing to do with the name of the parameter — r. It is as if r = radius is executed when circle_area is
called. It doesn’t matter what the value was named in the caller, in circle_area its name is r.
The above can be visualized in Python Tutor .

7.5 Multiple return values

Sometimes it is useful to have multiple return statements, one in each branch of a conditional. We have already seen
the built-in abs, now we see how to write our own:

1 def absolute_value(x):
2 if x < 0:
3 return -x
4 else:
5 return x

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

1 def absolute_value(x):
2 if x < 0:
3 return -x
4 return x

Think about this version and convince yourself it works the same as the first one.
Code that appears after a return statement, or any other place the flow of execution can never reach, is called dead
code, or unreachable code.
In a fruitful function, it is a good idea to ensure that every possible path through the program hits a return statement.
The following version of absolute_value fails to do this:

1 def bad_absolute_value(x):
2 if x < 0:
(continues on next page)

66 Chapter 7. Functions
Programming in Python, Release 1st Edition

(continued from previous page)


3 return -x
4 elif x > 0:
5 return x

This version is not correct because if x happens to be 0, neither condition is true, and the function ends without hitting
a return statement. In this case, the return value is a special value called None:

>>> print(bad_absolute_value(0))
None

All Python functions return None whenever they do not return another value.
It is also possible to use a return statement in the middle of a for loop, in which case control immediately returns
from the function. Let us assume that we want a function which looks through a list of words. It should return the first
2-letter word. If there is not one, it should return the empty string:

1 def find_first_2_letter_word(xs):
2 for wd in xs:
3 if len(wd) == 2:
4 return wd
5 return ""

>>> find_first_2_letter_word(["This", "is", "a", "dead", "parrot"])


'is'
>>> find_first_2_letter_word(["I", "like", "cheese"])
''

Single-step through this code and convince yourself that in the first test case that we’ve provided, the function returns
while processing the second element in the list: it does not have to traverse the whole list.

7.6 Boolean functions

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

1 def is_divisible(x, y):


2 """ Test if x is exactly divisible by y """
3 if x % y == 0:
4 return True
5 else:
6 return False

It is common to give Boolean functions names that sound like yes/no questions. is_divisible returns either
True or False to indicate whether the x is or is not divisible by y.
We can make the function more concise by taking advantage of the fact that the condition of the if statement is itself
a Boolean expression. We can return it directly, avoiding the if statement altogether:

1 def is_divisible(x, y):


2 return x % y == 0

This session shows the new function in action:

7.6. Boolean functions 67


Programming in Python, Release 1st Edition

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

Boolean functions are often used in conditional statements:

1 if is_divisible(x, y):
2 ... # Do something ...
3 else:
4 ... # Do something else ...

It might be tempting to write something like:

1 if is_divisible(x, y) == True:

but the extra comparison is unnecessary.

7.7 Scoping of variables

Variables can occur in the main program (when they occur outside of a def statement) and in functions. A variable’s
scope (also called lifetime) determines where the variable can be used. Python Tutor visualizes a variables scope
by associating each variable to a frame: the global frame for the main program and a frame for every function call
containing the parameters and the variables local to the function.
There are two rules:
• Variables assigned to (and therefore created) in the main program are visible everywhere, that is, in every
function. We call them global variables.
• Variables assigned to in a function or occurring in the parameter list are always newly created variables. In
addition, these variables are only visible in that function and are destroyed after completion of the function.
Variables assigned to in a function are called local variables.
Consider the following program:

1 def f(y):
2 y = 2
3 print(x)
4

5 x = 1
6 f(x)

Here, x is not local to f. Its value in f is 1. Step through the program in Python Tutor to see what happens.
However, if we assign to x in f, x becomes local to f and different from x in the main program:

1 def f(y):
2 y = 2
3 x = 4
4 print(x)
5

6 x = 1
7 f(x)
8 print(x)

68 Chapter 7. Functions
Programming in Python, Release 1st Edition

Step through this program in Python Tutor to see how the variable x lives both in the global as well as in the frame
associated to x.
There is a potentially confusing edge case which is depicted in the following program:

1 def f(y):
2 y = 2
3 print(x)
4 x = 2
5

6 x = 1
7 f(x)

Running this program gives the error:

UnboundLocalError: local variable 'x' referenced before assignment

As x is assigned to in f, x is a local variable. So, print(x) is executed before x has a value.


Can you predict what happens in the following program?

1 def f(x):
2 x = 2
3 print(x)
4

5 x = 1
6 f(x)
7 print(x)

Click here to run the program in Python Tutor.

7.8 Functions recap

We briefly summarize what we have learned about functions.

Syntax & semantics: function definition


Syntax

def func_name ( param1, param2, ...):


func_body

Semantics A function with name func_name and parameters param1 , param2, . . . is defined. Here,
param1, param2, . . . are called formal parameters.

Syntax & semantics: function call


Syntax

func_name (expr1, expr2, ...)

Semantics Expressions expr1, expr2, . . . are evaluated. The resulting values are called the actual pa-
rameters. Function func_name is called. Local variables param1, param2, . . . are created and
assigned the values expr1, expr2,. . . , respectively. The statements in func_body are executed.

7.8. Functions recap 69


Programming in Python, Release 1st Edition

After execution of func_name ends, that is, all statements in func_name are executed, control
is passed to the caller and all variables created during execution of func_name are destroyed.
When func_body contains a return statement, a value is passed to the function (or the main pro-
gram) that called func_body.

7.9 Glossary

argument A value provided to a function when the function is called. This value is assigned to the corresponding
parameter in the function. The argument can be the result of an expression which may involve operators,
operands and calls to other fruitful functions.
body The second part of a compound statement. The body consists of a sequence of statements all indented the
same amount from the beginning of the header. The standard amount of indentation used within the Python
community is 4 spaces.
compound statement A statement that consists of two parts:
1. header - which begins with a keyword determining the statement type, and ends with a colon.
2. body - containing one or more statements indented the same amount from the header.
The syntax of a compound statement looks like this:

keyword ... :
statement
statement ...

docstring A special string that is attached to a function as its __doc__ attribute. Tools like PyScripter can use doc-
strings to provide documentation or hints for the programmer. When we get to modules, classes, and methods,
we’ll see that docstrings can also be used there.
flow of execution The order in which statements are executed during a program run.
frame A box in a stack diagram that represents a function call. It contains the local variables and parameters of the
function.
function A named sequence of statements that performs some useful operation. Functions may or may not take
parameters and may or may not produce a result.
function call A statement that executes a function. It consists of the name of the function followed by a list of
arguments enclosed in parentheses.
function composition Using the output from one function call as the input to another.
function definition A statement that creates a new function, specifying its name, parameters, and the statements it
executes.
fruitful function A function that returns a value when it is called.
header line The first part of a compound statement. A header line begins with a keyword and ends with a colon (:)
import statement A statement which permits functions and variables defined in another Python module to be brought
into the environment of another script. To use the features of the turtle, we need to first import the turtle module.
lifetime Variables and objects have lifetimes — they are created at some point during program execution, and will be
destroyed at some time.
local variable A variable defined inside a function. A local variable can only be used inside its function. Parameters
of a function are also a special kind of local variable.

70 Chapter 7. Functions
Programming in Python, Release 1st Edition

parameter A name used inside a function to refer to the value which was passed to it as an argument.
stack diagram A graphical representation of a stack of functions, their variables, and the values to which they refer.
traceback A list of the functions that are executing, printed when a runtime error occurs. A traceback is also com-
monly referred to as a stack trace, since it lists the functions in the order in which they are stored in the runtime
stack.
void function The opposite of a fruitful function: one that does not return a value. It is executed for the work it does,
rather than for the value it returns.

7.9. Glossary 71
Programming in Python, Release 1st Edition

72 Chapter 7. Functions
CHAPTER

EIGHT

STRINGS

8.1 A compound data type

So far we have seen built-in types like int, float, bool, str and we’ve seen lists. Strings and lists are qualitatively
different from the others because they are made up of smaller pieces. In the case of strings, they’re made up of smaller
strings each containing one character.
Types that comprise smaller pieces are called compound data types. Depending on what we are doing, we may want
to treat a compound data type as a single thing, or we may want to access its parts. This ambiguity is useful.

8.2 Recap: working with the parts of a string

In this section we briefly recall some concepts regarding strings that were introduced earlier. The indexing operator
(Python uses square brackets to enclose the index) selects a single character substring from a string:

>>> fruit = "banana"


>>> m = fruit[1]
>>> print(m)

The expression fruit[1] selects character number 1 from fruit, and creates a new string containing just this one
character. The variable m refers to the result. When we display m, we could get a surprise:

Computer scientists always start counting from zero! The letter at subscript position zero of "banana" is b. So at
position [1] we have the letter a.
If we want to access the zero-eth letter of a string, we just place 0, or any expression that evaluates to 0, in between
the brackets:

>>> m = fruit[0]
>>> print(m)
b

The expression in brackets is called an index. An index specifies a member of an ordered collection, in this case the
collection of characters in the string. The index indicates which one you want, hence the name. It can be any integer
expression.
Note that indexing returns a string — Python has no special type for a single character. It is just a string of length 1.
We’ve also seen lists previously. The same indexing notation works to extract elements from a list:

73
Programming in Python, Release 1st Edition

>>> prime_nums = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
>>> prime_nums[4]
11
>>> friends = ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]
>>> friends[3]
'Angelina'

We come back to lists in a future chapter.


The len function, when applied to a string, returns the number of characters in a string:

>>> fruit = "banana"


>>> len(fruit)
6

To get the last letter of a string, you might be tempted to try something like this:

1 s = len(fruit)
2 last = fruit[s] # ERROR!

That won’t work. It causes the runtime error IndexError: string index out of range. The reason is
that there is no character at index position 6 in "banana". Because we start counting at zero, the six indexes are
numbered 0 to 5. To get the last character, we have to subtract 1 from the length of fruit:

1 s = len(fruit)
2 last = fruit[s-1]

Alternatively, we can use negative indices, which count backward from the end of the string. The expression
fruit[-1] yields the last letter, fruit[-2] yields the second to last, and so on.
As you might have guessed, indexing with a negative index also works like this for lists.
We won’t use negative indexes in the rest of these notes — not many computer languages use this idiom. There is
plenty of Python code out on the Internet that will use this trick, so it is best to know that it exists.

8.3 Traversal and the for loop

A lot of computations involve processing a string one character at a time. Often they start at the beginning, select each
character in turn, do something to it, and continue until the end. This pattern of processing is called a traversal. One
way to encode a traversal is with a while statement:

1 i = 0
2 while i < len(fruit):
3 letter = fruit[i]
4 print(letter)
5 i += 1

This loop traverses the string and displays each letter on a line by itself. The loop condition is ix < len(fruit),
so when ix is equal to the length of the string, the condition is false, and the body of the loop is not executed. The last
character accessed is the one with the index len(fruit)-1, which is the last character in the string.
But we’ve previously seen how the for loop can easily iterate over the elements in a list and it can do so for strings
as well:

1 for c in fruit:
2 print(c)

74 Chapter 8. Strings
Programming in Python, Release 1st Edition

Each time through the loop, the next character in the string is assigned to the variable c. The loop continues until no
characters are left. Here we can see the expressive power the for loop gives us compared to the while loop when
traversing a string.
The following example shows how to use concatenation and a for loop:

1 prefixes = "JKLMNOPQ"
2 suffix = "ack"
3

4 for p in prefixes:
5 if p == "Q" or p == "O":
6 print(p + "u" + suffix)
7 else:
8 print(p + suffix)

The output of this program is:

Jack
Kack
Lack
Mack
Nack
Ouack
Pack
Quack

8.4 Slices

A substring of a string is obtained by taking a slice. Similarly, we can slice a list to refer to some sublist of the items
in the list:

>>> s = "Pirates of the Caribbean"


>>> print(s[0:7])
Pirates
>>> print(s[11:14])
the
>>> print(s[15:24])
Caribbean
>>> friends = ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]
>>> print(friends[2:4])
['Brad', 'Angelina']

The operator [n:m] returns the part of the string from the n’th character to the m’th character, including the first but
excluding the last. This behavior makes sense if you imagine the indices pointing between the characters, as in the
following diagram:

If you imagine this as a piece of paper, the slice operator [n:m] copies out the part of the paper between the n and m
positions. Provided m and n are both within the bounds of the string, your result will be of length (m-n).
Three tricks are added to this: if you omit the first index (before the colon), the slice starts at the beginning of the
string (or list). If you omit the second index, the slice extends to the end of the string (or list). Similarly, if you provide

8.4. Slices 75
Programming in Python, Release 1st Edition

value for n that is bigger than the length of the string (or list), the slice will take all the values up to the end. (It won’t
give an “out of range” error like the normal indexing operation does.) Thus:

>>> fruit = "banana"


>>> fruit[:3]
'ban'
>>> fruit[3:]
'ana'
>>> fruit[3:999]
'ana'

What do you think s[:] means? What about friends[4:]?

8.5 String comparison

The comparison operators work on strings. To see if two strings are equal:

1 if word == "banana":
2 print("Yes, we have no bananas!")

Other comparison operations are useful for putting words in lexicographical order:

1 if word < "banana":


2 print("Your word, " + word + ", comes before banana.")
3 elif word > "banana":
4 print("Your word, " + word + ", comes after banana.")
5 else:
6 print("Yes, we have no bananas!")

This is similar to the alphabetical order you would use with a dictionary, except that all the uppercase letters come
before all the lowercase letters. As a result:

Your word, Zebra, comes before banana.

One way to address this problem is to convert strings to a standard format, such as all lowercase, before performing
the comparison. Of course, the latter only works when input solely consists of plain alphanumeric characters and in
addition avoids accented letters.

8.6 Strings are immutable

It is tempting to use the [] operator on the left side of an assignment, with the intention of changing a character in a
string. For example:

1 greeting = "Hello, world!"


2 greeting[0] = 'J' # ERROR!
3 print(greeting)

Instead of producing the output Jello, world!, this code produces the runtime error TypeError: 'str'
object does not support item assignment.
Strings are immutable, which means you can’t change an existing string. The best you can do is create a new string
that is a variation on the original:

76 Chapter 8. Strings
Programming in Python, Release 1st Edition

1 greeting = "Hello, world!"


2 new_greeting = "J" + greeting[1:]
3 print(new_greeting)

The solution here is to concatenate a new first letter onto a slice of greeting. This operation has no effect on the
original string.

8.7 The in and not in operators

The in operator tests for membership. When both of the arguments to in are strings, in checks whether the left
argument is a substring of the right argument.

>>> "p" in "apple"


True
>>> "i" in "apple"
False
>>> "ap" in "apple"
True
>>> "pa" in "apple"
False

Note that a string is a substring of itself, and the empty string is a substring of any other string.

>>> "a" in "a"


True
>>> "apple" in "apple"
True
>>> "" in "a"
True
>>> "" in "apple"
True

The not in operator returns the logical opposite results of in:

>>> "x" not in "apple"


True

Combining the in operator with string concatenation using +, we can write a function that removes all the vowels
from a string:

1 def remove_vowels(s):
2 vowels = "aeiouAEIOU"
3 s_sans_vowels = ""
4 for x in s:
5 if x not in vowels:
6 s_sans_vowels += x
7 return s_sans_vowels

Then, remove_vowels("aAbEefIijOopUus") results in "bfjps".

8.8 A find function

What does the following function do?

8.7. The in and not in operators 77


Programming in Python, Release 1st Edition

1 def find(s, ch):


2 """
3 Find and return the index of ch in s.
4 Return -1 if ch does not occur in s.
5 """
6 x = 0
7 while x < len(s):
8 if s[x] == ch:
9 return x
10 x += 1
11 return -1

Then, find("Compsci", "p") equals 3 while find("Compsci", "y") returns -1.


In a sense, find is the opposite of the indexing operator. Instead of taking an index and extracting the corresponding
character, it takes a character and finds the index where that character appears. If the character is not found, the
function returns -1.
This is another example where we see a return statement inside a loop. If s[x] == ch, the function returns
immediately, breaking out of the loop prematurely.
If the character doesn’t appear in the string, then the program exits the loop normally and returns -1.
This pattern of computation is sometimes called a eureka traversal or short-circuit evaluation, because as soon as
we find what we are looking for, we can cry “Eureka!”, take the short-circuit, and stop looking.

8.9 Optional parameters

To find the locations of the second or third occurrence of a character in a string, we can modify the find function,
adding a third parameter for the starting position in the search string:

1 def find2(s, ch, start):


2 x = start
3 while x < len(s):
4 if s[x] == ch:
5 return x
6 x += 1
7 return -1

The call find2("banana", "a", 2) now returns 3, the index of the first occurrence of “a” in “banana” starting
the search at index 2. What does find2("banana", "n", 3) return? If you said, 4, there is a good chance you
understand how find2 works.
Better still, we can combine find and find2 using an optional parameter:

1 def find(s, ch, start=0):


2 x = start
3 while x < len(s):
4 if s[x] == ch:
5 return x
6 x += 1
7 return -1

When a function has an optional parameter, the caller may provide a matching argument. If the third argument is
provided to find, it gets assigned to start. But if the caller leaves the argument out, then start is given a default
value indicated by the assignment start=0 in the function definition.

78 Chapter 8. Strings
Programming in Python, Release 1st Edition

So the call find("banana", "a", 2) to this version of find behaves just like find2, while in the call
find("banana", "a"), start will be set to the default value of 0.
Adding another optional parameter to find makes it search from a starting position, up to but not including the end
position:

1 def find(s, ch, start=0, end=None):


2 x = start
3 if end is None:
4 end = len(s)
5 while x < end:
6 if s[x] == ch:
7 return x
8 x += 1
9 return -1

The optional value for end is interesting: we give it a default value None if the caller does not supply any argument.
In the body of the function we test what end is, and if the caller did not supply any argument, we reassign end to be
the length of the string. If the caller has supplied an argument for end, however, the caller’s value will be used in the
loop. The semantics of start and end in this function are precisely the same as they are in the range function.
When a function has multiple optional parameters, it is not required to specify each of these parameters. If we, for
example, only want to search in the first four characters, we write find(ss, 's', end=4). Observe that we
have already seen this syntax when using the sep and end-parameters of the print() function.
Here is some example output when ss has the value "Python strings have some interesting
methods.":

find(ss, "s") returns 7


find(ss, "s", 7) returns 7
find(ss, "s", 8) returns 13
find(ss, "s", 8, 13) returns -1
find(ss, ".") returns len(ss)-1

Interestingly, named arguments (like start and end in the previous example) can also be used without default
values, for instance, to make function calls more readable. The following code illustrates this:

find(s=ss, ch="s")
find(ch="s",s=ss)

Both function calls are equivalent, so it is not necessary to preserve the ordering of arguments when all of them are
named. It now becomes immediately clear that ss is the string in which to find the character s and not vice-versa.
While this might seem like magic what happens is that under the hood arguments are simply a dictionary (discussed
in a later chapter).

8.10 Working with strings as single things

We previously saw that each turtle instance has its own attributes and a number of methods that can be applied to the
instance. For example, we could set the turtle’s color, and we wrote tess.turn(90).
Just like a turtle, a string is also an object. So each string instance has its own attributes and methods.
For example:

>>> ss = "Hello, World!"


>>> tt = ss.upper()
(continues on next page)

8.10. Working with strings as single things 79


Programming in Python, Release 1st Edition

(continued from previous page)


>>> tt
'HELLO, WORLD!'

upper is a method that can be invoked on any string object to create a new string, in which all the characters are in
uppercase. (The original string ss remains unchanged.)
There are also methods such as lower, capitalize, and swapcase that do other interesting stuff.
To learn what methods are available, you can consult the Python documentation, look for string methods, and read the
documentation.

8.11 The built-in find method

Now that we’ve done all this work to write a powerful find function, we can reveal that strings already have their
own built-in find method. It can do everything that our code can do, and more!

ss.find("s") returns 7
ss.find("s", 7) returns 7
ss.find("s", 8) returns 13
ss.find("s", 8, 13) returns -1
ss.find(".") returns len(ss)-1

The built-in find method is more general than our version. It can find substrings, not just single characters:

>>> "banana".find("nan")
2
>>> "banana".find("na", 3)
4

Usually we’d prefer to use the methods that Python provides rather than reinvent our own equivalents. But many of
the built-in functions and methods make good teaching exercises, and the underlying techniques you learn are your
building blocks to becoming a proficient programmer.

8.12 The split method

One of the most useful methods on strings is the split method: it splits a single multi-word string into a list of
individual words, removing all the whitespace between them. (Whitespace means any tabs, newlines, or spaces.) This
allows us to read input as a single string, and split it into words.

>>> ss = "Well I never did said Alice"


>>> wds = ss.split()
>>> wds
['Well', 'I', 'never', 'did', 'said', 'Alice']

8.13 Cleaning up your strings

We’ll often work with strings containing punctuation, tabs, and/or newline characters, especially, as we’ll see in a
future chapter, when we read our text from files or from the Internet. But if we’re writing a program, say, to count
word frequencies or check the spelling of each word, we’d prefer to strip off these unwanted characters.

80 Chapter 8. Strings
Programming in Python, Release 1st Edition

We’ll show just one example of how to strip punctuation from a string. Remember that strings are immutable, so
we cannot change the string with the punctuation — we need to traverse the original string and create a new string,
omitting any punctuation:

1 punctuation = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
2

3 def remove_punctuation(s):
4 s_sans_punct = ""
5 for letter in s:
6 if letter not in punctuation:
7 s_sans_punct += letter
8 return s_sans_punct

Setting up that first assignment is messy and error-prone. Fortunately, the Python string module already does it for
us. So we will make a slight improvement to this program — we’ll import the string module and use its definition:

1 import string
2

3 def remove_punctuation(s):
4 s_without_punct = ""
5 for letter in s:
6 if letter not in string.punctuation:
7 s_without_punct += letter
8 return s_without_punct

Then, remove_punctuation('"Well, I never did!", said Alice.') returns Well I never


did said Alice.
Composing together this function and the split method from the previous section makes a useful combination —
we’ll clean out the punctuation, and split will clean out the newlines and tabs while turning the string into a list of
words:

1 my_story = """
2 Pythons are constrictors, which means that they will 'squeeze' the life
3 out of their prey. They coil themselves around their prey and with
4 each breath the creature takes the snake will squeeze a little tighter
5 until they stop breathing completely. Once the heart stops the prey
6 is swallowed whole. The entire animal is digested in the snake's
7 stomach except for fur or feathers. What do you think happens to the fur,
8 feathers, beaks, and eggshells? The 'extra stuff' gets passed out as ---
9 you guessed it --- snake POOP! """
10

11 wds = remove_punctuation(my_story).split()
12 print(wds)

The output:

['Pythons', 'are', 'constrictors', ... , 'it', 'snake', 'POOP']

There are other useful string methods, but this book isn’t intended to be a reference manual. On the other hand, the
Python Library Reference is. Along with a wealth of other documentation, it is available at the Python website.

8.14 Summary

This chapter introduced a lot of new ideas. The following summary may prove helpful in remembering what you
learned.

8.14. Summary 81
Programming in Python, Release 1st Edition

indexing ([]) Access a single character in a string using its position (starting from 0). Example: "This"[2]
evaluates to "i".
length function (len) Returns the number of characters in a string. Example: len("happy") evaluates to 5.
for loop traversal (for) Traversing a string means accessing each character in the string, one at a time. For example,
the following for loop:

for ch in "Example":
...

executes the body of the loop 7 times with different values of ch each time.
slicing ([:]) A slice is a substring of a string. Example: 'bananas and cream'[3:6] evaluates to ana (so
does 'bananas and cream'[1:4]).
string comparison (>, <, >=, <=, ==, !=) The six common comparison operators work with strings, evalu-
ating according to lexicographical order. Examples: "apple" < "banana" evaluates to True. "Zeta"
< "Appricot" evaluates to False. "Zebra" <= "aardvark" evaluates to True because all upper
case letters precede lower case letters.
in and not in operator (in, not in) The in operator tests for membership. In the case of strings, it tests whether
one string is contained inside another string. Examples: "heck" in "I'll be checking for you."
evaluates to True. "cheese" in "I'll be checking for you." evaluates to False.

8.15 Glossary

compound data type A data type in which the values are made up of components, or elements, that are themselves
values.
default value The value given to an optional parameter if no argument for it is provided in the function call.
docstring A string constant on the first line of a function or module definition (and as we will see later, in class
and method definitions as well). Docstrings provide a convenient way to associate documentation with code.
Docstrings are also used by programming tools to provide interactive help.
dot notation Use of the dot operator, ., to access methods and attributes of an object.
immutable data value A data value which cannot be modified. Assignments to elements or slices (sub-parts) of
immutable values cause a runtime error.
index A variable or value used to select a member of an ordered collection, such as a character from a string, or an
element from a list.
mutable data value A data value which can be modified. The types of all mutable values are compound types. Lists
are mutable; strings and tuples are not.
optional parameter A parameter written in a function header with an assignment to a default value which it will
receive if no corresponding argument is given for it in the function call.
short-circuit evaluation A style of programming that shortcuts extra work as soon as the outcome is know with
certainty. In this chapter our find function returned as soon as it found what it was looking for; it didn’t
traverse all the rest of the items in the string.
slice A part of a string (substring) specified by a range of indices. More generally, a subsequence of any sequence
type in Python can be created using the slice operator (sequence[start:stop]).
traverse To iterate through the elements of a collection, performing a similar operation on each.
whitespace Any of the characters that move the cursor without printing visible characters. The constant string.
whitespace contains all the white-space characters.

82 Chapter 8. Strings
Programming in Python, Release 1st Edition

8.15. Glossary 83
Programming in Python, Release 1st Edition

84 Chapter 8. Strings
CHAPTER

NINE

FILES

9.1 About files

While a program is running, its data is stored in random access memory (RAM). RAM is fast and inexpensive, but
it is also volatile, which means that when the program ends, or the computer shuts down, data in RAM disappears.
To make data available the next time the computer is turned on and the program is started, it has to be written to a
non-volatile storage medium, such a hard drive, usb drive, or CD-RW.
Data on non-volatile storage media is stored in named locations on the media called files. By reading and writing files,
programs can save information between program runs.
Working with files is a lot like working with a notebook. To use a notebook, it has to be opened. When done, it has
to be closed. While the notebook is open, it can either be read from or written to. In either case, the notebook holder
knows where they are. They can read the whole notebook in its natural order or they can skip around.
All of this applies to files as well. To open a file, we specify its name and indicate whether we want to read or write.

9.2 Writing our first file

Let’s begin with a simple program that writes three lines of text into a file:

1 myfile = open("test.txt", "w")


2 myfile.write("My first file written from Python\n")
3 myfile.write("---------------------------------\n")
4 myfile.write("Hello, world!\n")
5 myfile.close()

Opening a file creates what we call a file handle. In this example, the variable myfile refers to the new handle
object. Our program calls methods on the handle, and this makes changes to the actual file which is usually located on
our disk.
On line 1, the open function takes two arguments. The first is the name of the file, and the second is the mode. Mode
"w" means that we are opening the file for writing.
With mode "w", if there is no file named test.txt on the disk, it will be created. If there already is one, it will be
replaced by the file we are writing.
To put data in the file we invoke the write method on the handle, shown in lines 2, 3 and 4 above. In bigger programs,
lines 2–4 will usually be replaced by a loop that writes many more lines into the file.
Closing the file handle (line 5) tells the system that we are done writing and makes the disk file available for reading
by other programs (or by our own program).

85
Programming in Python, Release 1st Edition

9.3 Reading a file line-at-a-time

Now that the file exists on our disk, we can open it, this time for reading, and read all the lines in the file, one at a time.
This time, the mode argument is "r" for reading:

1 mynewhandle = open("test.txt", "r")


2 while True: # Keep reading forever
3 theline = mynewhandle.readline() # Try to read next line
4 if len(theline) == 0: # If there are no more lines
5 break # leave the loop
6

7 # Now process the line we've just read


8 print(theline, end="")
9

10 mynewhandle.close()

This is a handy pattern for our toolbox. In bigger programs, we’d squeeze more extensive logic into the body of the
loop at line 8 — for example, if each line of the file contained the name and email address of one of our friends,
perhaps we’d split the line into some pieces and call a function to send the friend a party invitation.
On line 8 we suppress the newline character that print usually appends to our strings. Why? This is because
the string already has its own newline: the readline method in line 3 returns everything up to and including the
newline character. This also explains the end-of-file detection logic: when there are no more lines to be read from the
file, readline returns an empty string — one that does not even have a newline at the end, hence its length is 0.
If we try to open a file that doesn’t exist, we get an error:

>>> mynewhandle = open("wharrah.txt", "r")


IOError: [Errno 2] No such file or directory: "wharrah.txt"

9.4 Turning a file into a list of lines

It is often useful to fetch data from a disk file and turn it into a list of lines. Suppose we have a file containing our
friends and their email addresses, one per line in the file. But we’d like the lines sorted into alphabetical order. A good
plan is to read everything into a list of lines, then sort the list, and then write the sorted list back to another file:

1 f = open("friends.txt", "r")
2 xs = f.readlines()
3 f.close()
4

5 xs.sort()
6

7 g = open("sortedfriends.txt", "w")
8 for v in xs:
9 g.write(v)
10 g.close()

The readlines method in line 2 reads all the lines and returns a list of the strings. The statement xs.sort()
invokes the sort method to the list xs. This will be discussed in more detail in the chapter on lists.
We could have used the template from the previous section to read each line one-at-a-time, and to build up the list
ourselves, but it is a lot easier to use the method that the Python implementors gave us!
A more compact way to iterate over a file as if it is a list of lines is the following:

86 Chapter 9. Files
Programming in Python, Release 1st Edition

1 f = open("test.txt")
2 for line in f:
3 print(line,end="")

9.5 Reading the whole file at once

Another way of working with text files is to read the complete contents of the file into a string, and then to use our
string-processing skills to work with the contents.
We’d normally use this method of processing files if we were not interested in the line structure of the file. For
example, we’ve seen the split method on strings which can break a string into words. So here is how we might
count the number of words in a file:

1 f = open("somefile.txt")
2 content = f.read()
3 f.close()
4

5 words = content.split()
6 print("There are", len(words),"words in the file.")

Notice here that we left out the "r" mode in line 1. By default, if we don’t supply the mode, Python opens the file for
reading.

Your file paths may need to be explicitly named.


In the above example, we’re assuming that the file somefile.txt is in the same directory as your Python source
code. If this is not the case, you may need to provide a full or a relative path to the file. On Windows, a full path
could look like "C:\\temp\\somefile.txt", while on a Unix system the full path could be "/home/jimmy/
somefile.txt".
We’ll return to this later in this chapter.

9.6 An example

Many useful line-processing programs will read a text file line-at-a-time and do some minor processing as they write
the lines to an output file. They might number the lines in the output file, or insert extra blank lines after every 60
lines to make it convenient for printing on sheets of paper, or extract some specific columns only from each line in the
source file, or only print lines that contain a specific substring. We call this kind of program a filter.
Here is a filter that copies one file to another, omitting any lines that begin with #:

1 def filter(oldfile, newfile):


2 infile = open(oldfile, "r")
3 outfile = open(newfile, "w")
4 while True:
5 text = infile.readline()
6 if len(text) == 0:
7 break
8 if text[0] == "#":
9 continue
10

(continues on next page)

9.5. Reading the whole file at once 87


Programming in Python, Release 1st Edition

(continued from previous page)


11 # Put any more processing logic here
12 outfile.write(text)
13

14 infile.close()
15 outfile.close()

The continue statement at line 9 skips over the remaining lines in the current iteration of the loop, but the loop will
still iterate. This style looks a bit contrived here, but it is often useful to say “get the lines we’re not concerned with
out of the way early, so that we have cleaner, more focused logic in the meaty part of the loop that might be written
around line 11.”
Thus, if text is the empty string, the loop exits. If the first character of text is a hash mark, the flow of execution
goes to the top of the loop, ready to start processing the next line. Only if both conditions fail do we fall through to do
the processing at line 11, in this example, writing the line into the new file.
Let’s consider one more case: suppose our original file contained empty lines. At line 6 above, would this program find
the first empty line in the file, and terminate immediately? No! Recall that readline always includes the newline
character in the string it returns. It is only when we try to read beyond the end of the file that we get back the empty
string of length 0.

9.7 Directories

Files on non-volatile storage media are organized by a set of rules known as a file system. File systems are made up
of files and directories, which are containers for both files and other directories.
When we create a new file by opening it and writing, the new file goes in the current directory (wherever we were
when we ran the program). Similarly, when we open a file for reading, Python looks for it in the current directory.
If we want to open a file somewhere else, we have to specify the path to the file, which is the name of the directory
(or folder) where the file is located:

>>> wordsfile = open("/usr/share/dict/words", "r")


>>> wordlist = wordsfile.readlines()
>>> print(wordlist[:6])
['\n', 'A\n', "A's\n", 'AOL\n', "AOL's\n", 'Aachen\n']

This (Unix) example opens a file named words that resides in a directory named dict, which resides in share,
which resides in usr, which resides in the top-level directory of the system, called /. It then reads in each line into a
list using readlines, and prints out the first 5 elements from that list.
A Windows path might be "c:/temp/words.txt" or "c:\\temp\\words.txt". Because backslashes are
used to escape things like newlines and tabs, we need to write two backslashes in a literal string to get one! So the
length of these two strings is the same!
We cannot use / or \ as part of a filename; they are reserved as a delimiter between directory and filenames.
The file /usr/share/dict/words should exist on Unix-based systems, and contains a list of words in alphabet-
ical order.

9.8 Glossary

delimiter A sequence of one or more characters used to specify the boundary between separate parts of text.

88 Chapter 9. Files
Programming in Python, Release 1st Edition

directory A named collection of files, also called a folder. Directories can contain files and other directories, which
are referred to as subdirectories of the directory that contains them.
file A named entity, usually stored on a hard drive, floppy disk, or CD-ROM, that contains a stream of characters.
file system A method for naming, accessing, and organizing files and the data they contain.
handle An object in our program that is connected to an underlying resource (e.g. a file). The file handle lets our
program manipulate/read/write/close the actual file that is on our disk.
mode A distinct method of operation within a computer program. Files in Python can be opened in one of four modes:
read ("r"), write ("w"), append ("a"), and read and write ("+").
non-volatile memory Memory that can maintain its state without power. Hard drives, flash drives, and rewritable
compact disks (CD-RW) are each examples of non-volatile memory.
path A sequence of directory names that specifies the exact location of a file.
text file A file that contains printable characters organized into lines separated by newline characters.
volatile memory Memory which requires an electrical current to maintain state. The main memory or RAM of a
computer is volatile. Information stored in RAM is lost when the computer is turned off.

9.8. Glossary 89
Programming in Python, Release 1st Edition

90 Chapter 9. Files
CHAPTER

TEN

LISTS

A list is an ordered collection of values. The values that make up a list are called its elements, or its items. We will
use the term element or item to mean the same thing. Lists are similar to strings, which are ordered collections of
characters, except that the elements of a list can be of any type. Lists and strings — and other collections that maintain
the order of their items — are called sequences.

10.1 List values

There are several ways to create a new list; the simplest is to enclose the elements in square brackets ([ and ]):

1 ps = [10, 20, 30, 40]


2 qs = ["spam", "bungee", "swallow"]

The first example is a list of four integers. The second is a list of three strings. The elements of a list don’t have to be
the same type. The following list contains a string, a float, an integer, and (amazingly) another list:

1 zs = ["hello", 2.0, 5, [10, 20]]

A list within another list is said to be nested.


Finally, a list with no elements is called an empty list, and is denoted [].
We have already seen that we can assign list values to variables or pass lists as parameters to functions:

1 >>> vocabulary = ["apple", "cheese", "dog"]


2 >>> numbers = [17, 123]
3 >>> an_empty_list = []
4 >>> print(vocabulary, numbers, an_empty_list)
5 ["apple", "cheese", "dog"] [17, 123] []

10.2 Accessing elements

The syntax for accessing the elements of a list is the same as the syntax for accessing the characters of a string — the
index operator: [] (not to be confused with an empty list). The expression inside the brackets specifies the index.
Remember that the indices start at 0:

>>> numbers[0]
17

Any expression evaluating to an integer can be used as an index:

91
Programming in Python, Release 1st Edition

>>> numbers[9-8]
5
>>> numbers[1.0]
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: list indices must be integers, not float

If you try to access or assign to an element that does not exist, you get a runtime error:

>>> numbers[2]
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
IndexError: list index out of range

It is common to use a loop variable as a list index.

1 horsemen = ["war", "famine", "pestilence", "death"]


2

3 for i in [0, 1, 2, 3]:


4 print(horsemen[i])

Each time through the loop, the variable i is used as an index into the list, printing the i’th element. This pattern of
computation is called a list traversal.
The above sample doesn’t need or use the index i for anything besides getting the items from the list, so this more
direct version — where the for loop gets the items — might be preferred:

1 horsemen = ["war", "famine", "pestilence", "death"]


2

3 for h in horsemen:
4 print(h)

10.3 List length

The function len returns the length of a list, which is equal to the number of its elements. If you are going to use an
integer index to access the list, it is a good idea to use this value as the upper bound of a loop instead of a constant.
That way, if the size of the list changes, you won’t have to go through the program changing all the loops; they will
work correctly for any size list:

1 horsemen = ["war", "famine", "pestilence", "death"]


2

3 for i in range(len(horsemen)):
4 print(horsemen[i])

The last time the body of the loop is executed, i is len(horsemen) - 1, which is the index of the last element.
Although a list can contain another list, the nested list still counts as a single element in its parent list. The length of
this list is only 4:

>>> len(["car makers", 1, ["Ford", "Toyota", "BMW"], [1, 2, 3]])


4

92 Chapter 10. Lists


Programming in Python, Release 1st Edition

10.4 List membership

in and not in are Boolean operators that test membership in a sequence. We used them previously with strings, but
they also work with lists and other sequences:

>>> horsemen = ["war", "famine", "pestilence", "death"]


>>> "pestilence" in horsemen
True
>>> "debauchery" in horsemen
False
>>> "debauchery" not in horsemen
True

10.5 List operations

The + operator concatenates lists:

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> c
[1, 2, 3, 4, 5, 6]

Similarly, the * operator repeats a list a given number of times:

>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

The first example repeats [0] four times. The second example repeats the list [1, 2, 3] three times.

10.6 List slices

The slice operations we saw previously with strings, also work with lists:

>>> a_list = ["a", "b", "c", "d", "e", "f"]


>>> a_list[1:3]
['b', 'c']
>>> a_list[:4]
['a', 'b', 'c', 'd']
>>> a_list[3:]
['d', 'e', 'f']
>>> a_list[:]
['a', 'b', 'c', 'd', 'e', 'f']

10.7 Lists are mutable

Unlike strings, lists are mutable, which means we can change their elements. Using the index operator on the left side
of an assignment, we can update one of the elements:

10.4. List membership 93


Programming in Python, Release 1st Edition

>>> fruit = ["banana", "apple", "quince"]


>>> fruit[0] = "pear"
>>> fruit[2] = "orange"
>>> fruit
['pear', 'apple', 'orange']

The bracket operator applied to a list can appear anywhere in an expression. When it appears on the left side of
an assignment, it changes one of the elements in the list, so the first element of fruit has been changed from
"banana" to "pear", and the last from "quince" to "orange". An assignment to an element of a list is called
item assignment. Item assignment does not work for strings:

>>> my_string = "TEST"


>>> my_string[2] = "X"
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: 'str' object does not support item assignment

but it does for lists:

>>> my_list = ["T", "E", "S", "T"]


>>> my_list[2] = "X"
>>> my_list
['T', 'E', 'X', 'T']

With the slice operator we can update a whole sublist at once:

>>> a_list = ["a", "b", "c", "d", "e", "f"]


>>> a_list[1:3] = ["x", "y"]
>>> a_list
['a', 'x', 'y', 'd', 'e', 'f']

The semantics is that in a slice assignment the indicated slice is replaced with the result of the expression at the right
hand side of the assignment operator. In particular, we can also remove elements from a list by assigning an empty list
to them:

>>> a_list = ["a", "b", "c", "d", "e", "f"]


>>> a_list[1:3] = []
>>> a_list
['a', 'd', 'e', 'f']

Furthermore, we can add elements to a list by squeezing them into an empty slice at the desired location:

>>> a_list = ["a", "d", "f"]


>>> a_list[1:1] = ["b", "c"]
>>> a_list
['a', 'b', 'c', 'd', 'f']
>>> a_list[4:4] = ["e"]
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f']

10.8 List deletion

Using slices to delete list elements can be error-prone. Python provides an alternative that is more readable. The del
statement removes an element from a list:

94 Chapter 10. Lists


Programming in Python, Release 1st Edition

>>> a = ["one", "two", "three"]


>>> del a[1]
>>> a
['one', 'three']

As you might expect, del causes a runtime error if the index is out of range.
You can also use del with a slice to delete a sublist:

>>> a_list = ["a", "b", "c", "d", "e", "f"]


>>> del a_list[1:5]
>>> a_list
['a', 'f']

As usual, the sublist selected by slice contains all the elements up to, but not including, the second index.

10.9 Objects and references

After we execute these assignment statements

1 a = "banana"
2 b = "banana"

we know that a and b will refer to a string object with the letters "banana". But we don’t know yet whether they
point to the same string object.
There are two possible ways the Python interpreter could arrange its memory:

In one case, a and b refer to two different objects that have the same value. In the second case, they refer to the same
object.
We can test whether two names refer to the same object using the is operator:

>>> a is b
True

This tells us that both a and b refer to the same object, and that it is the second of the two state snapshots that accurately
describes the relationship.
Since strings are immutable, Python optimizes resources by making two names that refer to the same string value refer
to the same object.
This is not the case with lists:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a == b
True
>>> a is b
False

The state snapshot here looks like this:

10.9. Objects and references 95


Programming in Python, Release 1st Edition

a and b have the same value but do not refer to the same object.

10.10 Aliasing

Since variables refer to objects, if we assign one variable to another, both variables refer to the same object:

>>> a = [1, 2, 3]
>>> b = a
>>> a is b
True

In this case, the state snapshot looks like this:

Because the same list has two different names, a and b, we say that it is aliased. Changes made with one alias affect
the other:

>>> b[0] = 5
>>> a
[5, 2, 3]

Although this behavior can be useful, it is sometimes unexpected or undesirable. In general, it is safer to avoid aliasing
when you are working with mutable objects (i.e. lists at this point in our textbook, but there are other mutable objects
like dictionaries and sets). Of course, for immutable objects (i.e. strings, tuples), there’s no problem — it is just not
possible to change something and get a surprise when you access an alias name. That’s why Python is free to alias
strings (and any other immutable kinds of data) when it sees an opportunity to economize.

10.11 Cloning lists

If we want to modify a list and also keep a copy of the original, we need to be able to make a copy of the list itself, not
just the reference. This process is sometimes called cloning, to avoid the ambiguity of the word copy.
The easiest way to clone a list is to use the slice operator:

>>> a = [1, 2, 3]
>>> b = a[:]
>>> b
[1, 2, 3]

Taking a slice of a list in an expression occurring on the right hand side of an assignment operator always creates a
new list. In the code sample above, the slice happens to consist of the whole list. So now the relationship is like this:

96 Chapter 10. Lists


Programming in Python, Release 1st Edition

Now we are free to make changes to b without worrying that we’ll inadvertently be changing a:

>>> b[0] = 5
>>> a
[1, 2, 3]

10.12 Lists and for loops

The for loop also works with lists, as we’ve already seen. The generalized syntax of a for loop is:

for VARIABLE in LIST:


BODY

So, as we’ve seen

1 friends = ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]


2 for friend in friends:
3 print(friend)

It almost reads like English: For (every) friend in (the list of) friends, print (the name of the) friend.
Any list expression can be used in a for loop:

1 for number in range(20):


2 if number % 3 == 0:
3 print(number)
4

5 for fruit in ["banana", "apple", "quince"]:


6 print("I like to eat " + fruit + "s!")

The first example prints all the multiples of 3 between 0 and 19. The second example expresses enthusiasm for various
fruits.
Since lists are mutable, we often want to traverse a list, changing each of its elements. The following squares all the
numbers in the list xs:

1 xs = [1, 2, 3, 4, 5]
2

3 for i in range(len(xs)):
4 xs[i] = xs[i]**2

10.13 List parameters

Passing a list as an argument actually passes a reference to the list, not a copy or clone of the list. So parameter passing
creates an alias for you: the caller has one variable referencing the list, and the called function has an alias, but there
is only one underlying list object. For example, the function below takes a list as an argument and multiplies each
element in the list by 2:

10.12. Lists and for loops 97


Programming in Python, Release 1st Edition

1 def double_stuff(a_list):
2 """ Overwrite each element in a_list with double its value. """
3 for i in range(a_list):
4 a_list[i] *= 2

If we add the following onto our script:

1 things = [2, 5, 9]
2 double_stuff(things)
3 print(things)

When we run it we’ll get:

[4, 10, 18]

In the function above, the parameter a_list and the variable things are aliases for the same object. So before any
changes to the elements in the list, the state snapshot looks like this:

Since the list object is shared by two frames, we drew it between them.
If a function modifies the items of a list parameter, the caller sees the change. Test the code yourself in Python Tutor
and see how double_stuff modifies the list things.

Remark
Parameter passing always happens through assignment. Therefore,
• an immutable object (like int or string) can never be modified when passed as an argument;
• a mutable object (like a list) can be modified when passed as an argument.

10.14 List methods

The dot operator can also be used to access built-in methods of list objects. We’ll start with the most useful method
for adding something onto the end of an existing list:

>>> mylist = []
>>> mylist.append(5)
>>> mylist.append(27)
>>> mylist.append(3)
>>> mylist.append(12)
>>> mylist
[5, 27, 3, 12]

append is a list method which adds the argument passed to it to the end of the list. We’ll use it heavily when we’re
creating new lists. Continuing with this example, we show several other list methods:

>>> mylist.insert(1, 12) # Insert 12 at pos 1, shift other items up


>>> mylist
(continues on next page)

98 Chapter 10. Lists


Programming in Python, Release 1st Edition

(continued from previous page)


[5, 12, 27, 3, 12]
>>> mylist.count(12) # How many times is 12 in mylist?
2
>>> mylist.extend([5, 9, 5, 11]) # Put whole list onto end of mylist
>>> mylist
[5, 12, 27, 3, 12, 5, 9, 5, 11])
>>> mylist.index(9) # Find index of first 9 in mylist
6
>>> mylist.reverse()
>>> mylist
[11, 5, 9, 5, 12, 3, 27, 12, 5]
>>> mylist.sort()
>>> mylist
[3, 5, 5, 5, 9, 11, 12, 12, 27]
>>> mylist.remove(12) # Remove the first 12 in the list
>>> mylist
[3, 5, 5, 5, 9, 11, 12, 27]

Experiment and play with the list methods shown here, and read their documentation until you feel confident that you
understand how they work.
It is important to understand that each of these methods changes the list for which the method call is invoked.

10.15 Be wary of mutable default arguments

Using lists as default arguments can lead to unexpected behavior. Consider the following program:

def append_to(element, to=[]):


to.append(element)
return to

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)

One would expect the output to be

[12]
[42]

but instead it is

[12]
[12,42]

If you do not believe it, run the program in Python Tutor.


What happens is that in Python default arguments are created once (during the first function call) and the same value
is used in each successive function call. So, when the argument is mutable and is changed by the function call this
affects future function calls as well.
When this behavior is not intended, the solution is to not use mutables as default arguments, perhaps as follows:

10.15. Be wary of mutable default arguments 99


Programming in Python, Release 1st Edition

def append_to(element, to=None):


if to is None:
to = []
to.append(element)
return to

10.16 Pure functions and modifiers

Functions which take lists as arguments and change them during execution are called modifiers and the changes they
make are called side effects.
A pure function does not produce side effects. It communicates with the calling program only through parameters,
which it does not modify, and a return value. Here is double_stuff written as a pure function:

1 def double_stuff(a_list):
2 """ Return a new list which contains
3 doubles of the elements in a_list.
4 """
5 new_list = []
6 for value in a_list:
7 new_elem = 2 * value
8 new_list.append(new_elem)
9

10 return new_list

This version of double_stuff does not change its arguments:

>>> things = [2, 5, 9]


>>> xs = double_stuff(things)
>>> things
[2, 5, 9]
>>> xs
[4, 10, 18]

An early rule we saw for assignment said “first evaluate the right hand side, then assign the resulting value to the
variable”. So it is quite safe to assign the function result to the same variable that was passed to the function:

>>> things = [2, 5, 9]


>>> things = double_stuff(things)
>>> things
[4, 10, 18]

Which style is better?


Anything that can be done with modifiers can also be done with pure functions. In fact, some programming languages
only allow pure functions. There is some evidence that programs that use pure functions are faster to develop and
less error-prone than programs that use modifiers. Nevertheless, modifiers are convenient at times, and in some cases,
functional programs are less efficient.
In general, we recommend that you write pure functions whenever it is reasonable to do so and resort to modifiers only
if there is a compelling advantage. This approach might be called a functional programming style.

100 Chapter 10. Lists


Programming in Python, Release 1st Edition

10.17 Functions that produce lists

The pure version of double_stuff above made use of an important pattern for your toolbox. Whenever you need
to write a function that creates and returns a list, the pattern is usually:

1 initialize a result variable to be an empty list


2 loop
3 create a new element
4 append it to result
5 return the result

Let us show another use of this pattern. Assume you already have a function is_prime(x) that can test if x is
prime. Write a function to return a list of all prime numbers less than n:

1 def primes_lessthan(n):
2 """ Return a list of all prime numbers less than n. """
3 result = []
4 for i in range(2, n):
5 if is_prime(i):
6 result.append(i)
7 return result

10.18 Strings and lists

Two of the most useful methods on strings involve conversion to and from lists of substrings. The split method
(which we’ve already seen) breaks a string into a list of words. By default, any number of whitespace characters is
considered a word boundary:

>>> song = "The rain in Spain..."


>>> wds = song.split()
>>> wds
['The', 'rain', 'in', 'Spain...']

An optional argument called a delimiter can be used to specify which string to use as the boundary marker between
substrings. The following example uses the string ai as the delimiter:

>>> song.split("ai")
['The r', 'n in Sp', 'n...']

Notice that the delimiter doesn’t appear in the result.


The inverse of the split method is join. You choose a desired separator string, (often called the glue) and join
the list with the glue between each of the elements:

>>> glue = ";"


>>> s = glue.join(wds)
>>> s
'The;rain;in;Spain...'

The list that you glue together (wds in this example) is not modified. Also, as these next examples show, you can use
empty glue or multi-character strings as glue:

>>> " --- ".join(wds)


'The --- rain --- in --- Spain...'
(continues on next page)

10.17. Functions that produce lists 101


Programming in Python, Release 1st Edition

(continued from previous page)


>>> "".join(wds)
'TheraininSpain...'

10.19 Mutability revisited

Lists are the first mutable type we encounter, so let us look at some more examples. The following code

1 A = [1, 2]
2 X = A
3 X[0] = 2
4 print(A)
5 X = [8,9]
6 print(A)

produces the output

[2, 2]
[2, 2]

Visualize the execution yourself to see what happens. The assigment in line 1 creates a new list [1,2] and links it
to the variable A. The assignment in line 2 links the content of A to variable X So, X and A refer to the same object.
Evaluating line 3 modifies the list to which both X and A refer. Therefore, the first line of the output is [2, 2]. Line
5, creates the new list [8,9] and names it X. But, A itself is not changed. Why? Lists can only be changed in place
though indexing or a function call like append. An assignment like X = [8,9] always creates a novel object.
Therefore, also the second line of the output is [2, 2].
As another example, running the program:

1 A = [1,2]
2 X = A + [3,4]
3 print(A)
4 print(X)

results in the output

[1, 2]
[1, 2, 3, 4]

Indeed, the assignment X = A + [3,4] first evaluates the expression A + [3,4] creating the new list [1, 2,
3, 4] that is subsequently linked to the variable X. The content of A is not changed.
Contrast this with the following program

1 A = [1,2]
2 X = A
3 X.append(3)
4 X.append(4)
5 print(A)
6 print(X)

that outputs

[1, 2, 3, 4]
[1, 2, 3, 4]

102 Chapter 10. Lists


Programming in Python, Release 1st Edition

Here, the list both X and A refer to is modified in place. Notice that neither A nor X is reassigned.
Consider the following program:

A = [1,2,4,6,7]
for x in A:
if x % 2 == 0:
A.remove(x)

print(A)

The program seems to remove all even numbers from the list A. But when the program is run, it outputs

[1, 4, 7]

What happened here? 4 is an even number and is not removed from the list. The problem is that A is modified during
the iteration. There is a hidden counter that is initially assigned the value 0 and is incremented by 1 whenever there is
another value in the list. Initially, the counter is assigned to 0 and element 1 is processed. Then the counter is assigned
to 1, processing element 2. As 2 is even, the element is removed from A resulting in the list [1,4,6,7]. The hidden
counter is now increased to 2 and element 6 is processed thereby effectively surpassing element 4. See for yourself in
Python Tutor.
A solution is to iterate over a copy a A

A = [1,2,4,6,7]
for x in A[:]:
if x % 2 == 0:
A.remove(x)

print(A)

which produces the correct output:

[1, 7]

Another solution is to use list comprehension as described in the next section.

10.20 List Comprehension

List comprehension is a powerful method to easily construct lists.

Syntax & semantics: list comprehension


Syntax

[item_expr for var i list]

Semantics For every value in list assigned to var the expression item_expr is evaluated to generate an item in
the list.

Let us have a look at an example:

L = [3*x+10 for x in range(10)]

which generates the list

10.20. List Comprehension 103


Programming in Python, Release 1st Edition

[10, 13, 16, 19, 22, 25, 28, 31, 34, 37]

Specifically, the expression 3*x+10 is evaluated for the values in range(10) for x.
An additional filter expression can be added as well, filtering out those item_expr that satisfy a condition:

Syntax & semantics: extended list comprehension


Syntax

[item_expr for var in list if boolean_condition]

Semantics For every value in list assigned to var that satisfies boolean_condition, the expression
item_expr is evaluated to generate an item in the list.

Consider the following example:

L = [3*x+10 for x in range(10) if x % 2 == 0]

which generates the list

[10, 16, 22, 28, 34]

Notice that the last example is equivalent to the more verbose program

res = []
for x in range(10):
if x % 2 == 0:
res.append(3*x+10)

The following function filters out even numbers in a list:

def filter_out_even_numbers(A):
return [x for x in A if x % 2 != 0]

10.21 Nested lists

A nested list is a list that appears as an element in another list. In this list, the element with index 3 is a nested list:

>>> nested = ["hello", 2.0, 5, [10, 20]]

If we output the element at index 3, we get:

>>> print(nested[3])
[10, 20]

To extract an element from the nested list, we can proceed in two steps:

>>> elem = nested[3]


>>> elem[0]
10

Or we can combine them:

104 Chapter 10. Lists


Programming in Python, Release 1st Edition

>>> nested[3][1]
20

Bracket operators evaluate from left to right, so this expression gets the 3’th element of nested and extracts the 1’th
element from it.
We remark that copying lists with the slice operator only duplicates the elements at the highest level. This is referred
to as shallow copying. As an example consider the following code that you can run in Python Tutor.

mylist = [1,2,3]
yourlist = [4,5,6]
nested = [mylist,yourlist]
shallow_copy = nested[:]
mylist[0] = 9
print(shallow_copy)

The example shows that shallow_copy is indeed a copy of nested in that the first and second element of the list
refer to the same values (mylist and yourlist). The output is therefore [[9, 2, 3], [4, 5, 6]] and not
[[0, 2, 3], [4, 5, 6]] as changing mylist affects both nested and shallow_copy. To make a deep
copy of a list you can use a method from the copy module:

import copy

mylist = [1,2,3]
yourlist = [4,5,6]
nested = [mylist,yourlist]
shallow_copy = nested[:]
deep_copy = copy.deepcopy(nested)
mylist[0] = 9
print(shallow_copy)
print(deep_copy)

The output now is

[[9, 2, 3], [4, 5, 6]]


[[1, 2, 3], [4, 5, 6]]

10.22 Matrices

Nested lists are often used to represent matrices. For example, the matrix:

might be represented as:

>>> mx = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

mx is a list with three elements, where each element is a row of the matrix. We can select an entire row from the matrix
in the usual way:

10.22. Matrices 105


Programming in Python, Release 1st Edition

>>> mx[1]
[4, 5, 6]

Or we can extract a single element from the matrix using the double-index form:
>>> mx[1][2]
6

The first index selects the row, and the second index selects the column. Have a look at how mx is visualized by Python
Tutor.
As an example consider the following code that computes the sum of all elements in a matrix:
1 def compute_sum(mx):
2 total = 0
3 for row in mx:
4 for element in row:
5 total += element
6 return total
7

8 print(compute_sum([[1,2,3],[4,5,6],[7,8,9]]))

Notice that line 3 iterates over rows (which are lists) as mx is a list of lists, while line 4 iterates over elements in a
non-nested list (given that the input is a matrix). See what happens using Python Tutor.
Here is the same function that uses indices (Python Tutor):
1 def compute_sum(mx):
2 total = 0
3 for r in range(len(mx)):
4 for c in range(len(mx[r])):
5 total += mx[r][c]
6 return total
7

8 print(compute_sum([[1,2,3],[4,5,6],[7,8,9]]))

List comprehension can be used to easily extract a column from a matrix. The function column returns the i-th
column of matrix m. We assume here that this column exists.
def column(i, m):
return [m[j][i] for j in range(len(m))]

So, column(0, [[1,2,3],[4,5,6],[7,8,9]])) returns [1, 4, 7].

10.23 Glossary

aliases Multiple variables that contain references to the same object.


clone To create a new object that has the same value as an existing object. Copying a reference to an object creates
an alias but doesn’t clone the object.
delimiter A character or string used to indicate where a string should be split.
element One of the values in a list (or other sequence). The bracket operator selects elements of a list. Also called
item.
immutable data value A data value which cannot be modified. Assignments to elements or slices (sub-parts) of
immutable values cause a runtime error.

106 Chapter 10. Lists


Programming in Python, Release 1st Edition

index An integer value that indicates the position of an item in a list. Indexes start from 0.
item See element.
list A collection of values, each in a fixed position within the list. Like other types str, int, float, etc. there is
also a list type-converter function that tries to turn whatever argument you give it into a list.
list traversal The sequential accessing of each element in a list.
modifier A function which changes its arguments inside the function body. Only mutable types can be changed by
modifiers.
mutable data value A data value which can be modified. The types of all mutable values are compound types. Lists
and dictionaries are mutable; strings and tuples are not.
nested list A list that is an element of another list.
object A thing to which a variable can refer.
pattern A sequence of statements, or a style of coding something that has general applicability in a number of different
situations. Part of becoming a mature Computer Scientist is to learn and establish the patterns and algorithms
that form your toolkit. Patterns often correspond to your “mental chunking”.
promise An object that promises to do some work or deliver some values if they’re eventually needed, but it lazily
puts off doing the work immediately. Calling range produces a promise.
pure function A function which has no side effects. Pure functions only make changes to the calling program through
their return values.
sequence Any of the data types that consist of an ordered collection of elements, with each element identified by an
index. In this course, the only sequences we consider are strings.
side effect A change in the state of a program made by calling a function. Side effects can only be produced by
modifiers.
step size The interval between successive elements of a linear sequence. The third (and optional argument) to the
range function is called the step size. If not specified, it defaults to 1.

10.23. Glossary 107


Programming in Python, Release 1st Edition

108 Chapter 10. Lists


CHAPTER

ELEVEN

RECURSION

Recursion means “defining something in terms of itself” usually at some smaller scale, perhaps multiple times, to
achieve your objective. For example, we might say “A human being is someone whose mother is a human being”, or
“a directory is a structure that holds files and (smaller) directories”, or “a family tree starts with a couple who have
children, each with their own family sub-trees”.
Programming languages generally support recursion, which means that, in order to solve a problem, functions can
call themselves to solve smaller subproblems.

11.1 Drawing Fractals

For our purposes, a fractal is a drawing which also has self-similar structure, where it can be defined in terms of itself.
Let us start by looking at the famous Koch fractal. An order 0 Koch fractal is simply a straight line of a given size.

An order 1 Koch fractal is obtained like this: instead of drawing just one line, draw instead four smaller segments, in
the pattern shown here:

Now what would happen if we repeated this Koch pattern again on each of the order 1 segments? We’d get this order
2 Koch fractal:

Repeating our pattern again gets us an order 3 Koch fractal:

109
Programming in Python, Release 1st Edition

Now let us think about it the other way around. To draw a Koch fractal of order 3, we can simply draw four order 2
Koch fractals. But each of these in turn needs four order 1 Koch fractals, and each of those in turn needs four order 0
fractals. Ultimately, the only drawing that will take place is at order 0. This is very simple to code up in Python:

1 def koch(t, order, size):


2 """
3 Make turtle t draw a Koch fractal of 'order' and 'size'.
4 Leave the turtle facing the same direction.
5 """
6

7 if order == 0: # The base case is just a straight line


8 t.forward(size)
9 else:
10 koch(t, order-1, size/3) # Go 1/3 of the way
11 t.left(60)
12 koch(t, order-1, size/3)
13 t.right(120)
14 koch(t, order-1, size/3)
15 t.left(60)
16 koch(t, order-1, size/3)

The key thing that is new here is that if order is not zero, koch calls itself recursively to get its job done.
Let’s make a simple observation and tighten up this code. Remember that turning right by 120 is the same as turning
left by -120. So with a bit of clever rearrangement, we can use a loop instead of lines 10-16:

1 def koch(t, order, size):


2 if order == 0:
3 t.forward(size)
4 else:
5 for angle in [60, -120, 60, 0]:
6 koch(t, order-1, size/3)
7 t.left(angle)
8

9 t = turtle.Turtle()
10 koch(t,4,1000)

The final turn is 0 degrees — so it has no effect. But it has allowed us to find a pattern and reduce seven lines of code
to three, which will make things easier for our next observations.

Recursion, the high-level view


One way to think about this is to convince yourself that the function works correctly when you call it for an order 0
fractal. Then do a mental leap of faith, saying Python knows how to handle the recursive level 0 calls for me on lines
11, 13, 15, and 17, so I don’t need to think about that detail!” All I need to focus on is how to draw an order 1 fractal
if I can assume the order 0 one is already working.
You’re practicing mental abstraction — ignoring the subproblem while you solve the big problem.
If this mode of thinking works (and you should practice it!), then take it to the next level. Aha! now can I see that it
will work when called for order 2 under the assumption that it is already working for level 1.

110 Chapter 11. Recursion


Programming in Python, Release 1st Edition

And, in general, if I can assume the order n-1 case works, can I just solve the level n problem?
Students of mathematics who have played with proofs of induction should see some very strong similarities here.

Recursion, the low-level operational view


Another way of trying to understand recursion is to get rid of it! If we had separate functions to draw a level 3 fractal,
a level 2 fractal, a level 1 fractal and a level 0 fractal, we could simplify the above code, quite mechanically, to a
situation where there was no longer any recursion, like this:

1 def koch_0(t, size):


2 t.forward(size)
3

4 def koch_1(t, size):


5 for angle in [60, -120, 60, 0]:
6 koch_0(t, size/3)
7 t.left(angle)
8

9 def koch_2(t, size):


10 for angle in [60, -120, 60, 0]:
11 koch_1(t, size/3)
12 t.left(angle)
13

14 def koch_3(t, size):


15 for angle in [60, -120, 60, 0]:
16 koch_2(t, size/3)
17 t.left(angle)

This trick of “unrolling” the recursion gives us an operational view of what happens. You can trace the program into
koch_3, and from there, into koch_2, and then into koch_1, etc., all the way down the different layers of the
recursion.
This might be a useful hint to build your understanding. The mental goal is, however, to be able to do the abstraction!

11.2 Recursion versus iteration

The following program computes the factorial in an iterative fashion:

1 def fac_iter(n):
2 res = 1
3 i = 1
4 while i <= n:
5 res *= i
6 i+=1
7 return res

The factorial can also be computed in a recursive fashion as follows:

1 def fac_rec(n):
2 if n == 0 or n == 1:
3 return 1
4 else:
5 return n * fac_rec(n-1)

11.2. Recursion versus iteration 111


Programming in Python, Release 1st Edition

Check for yourself in Python Tutor how the code is executed. Notice that after finishing the i-th function call returns
a value to the (i-1)-th function call propagating the computed value upwards to the first call of fac_rec.

11.3 Processing nested lists

All of the Python data types we have seen can be grouped inside lists in a variety of ways. Lists can also be nested,
providing many possibilities for organizing data. The organization of data for the purpose of making it easier to use is
called a data structure.
It’s election time and we are helping to compute the votes as they come in. Votes arriving from individual wards,
precincts, municipalities, counties, and states are sometimes reported as a sum total of votes and sometimes as a list
of subtotals of votes. After considering how best to store the tallies, we decide to use a nested number list, which we
define as follows:
A nested number list is a list whose elements are either:
a. numbers
b. nested number lists
Notice that the term, nested number list is used in its own definition. Recursive definitions like this are quite common
in mathematics and computer science. They provide a concise and powerful way to describe recursive data structures
that are partially composed of smaller and simpler instances of themselves. The definition is not circular, since at some
point we will reach a list that does not have any lists as elements.
Now suppose our job is to write a function that will sum all of the values in a nested number list. Python has a built-in
function which finds the sum of a sequence of numbers:

>>> sum([1, 2, 8])


11

For our nested number list, however, sum will not work:

>>> sum([1, 2, [11, 13], 8])


Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>>

The problem is that the third element of this list, [11, 13], is itself a list, so it cannot just be added to 1, 2, and 8.
To sum all the numbers in our recursive nested number list we need to traverse the list, visiting each of the elements
within its nested structure, adding any numeric elements to our sum, and recursively repeating the summing process
with any elements which are themselves sub-lists.
Thanks to recursion, the Python code needed to sum the values of a nested number list is surprisingly short:

1 def r_sum(nested_num_list):
2 tot = 0
3 for element in nested_num_list:
4 if type(element) == type([]):
5 tot += r_sum(element)
6 else:
7 tot += element
8 return tot

The body of r_sum consists mainly of a for loop that traverses nested_num_list. If element is a numerical
value (the else branch), it is simply added to tot. If element is a list, then r_sum is called again, with the

112 Chapter 11. Recursion


Programming in Python, Release 1st Edition

element as an argument. The statement inside the function definition in which the function calls itself is known as the
recursive call.
The example above has a base case (on line 13) which does not lead to a recursive call: the case where the element is
not a (sub-) list. Without a base case, you’ll have infinite recursion, and your program will not work.
Recursion is truly one of the most beautiful and elegant tools in computer science.

11.4 Case study: Fibonacci numbers

The famous Fibonacci sequence 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 134, . . . was devised by Fibonacci (1170-1250),
who used this to model the breeding of (pairs) of rabbits. If, in generation 7 you had 21 pairs in total, of which 13
were adults, then next generation the adults will all have bred new children, and the previous children will have grown
up to become adults. So in generation 8 you’ll have 13+21=34, of which 21 are adults.
This model to explain rabbit breeding made the simplifying assumption that rabbits never died. Scientists often make
(unrealistic) simplifying assumptions and restrictions to make some headway with the problem.
If we number the terms of the sequence from 0, we can describe each term recursively as the sum of the previous two
terms:

fib(0) = 0
fib(1) = 1
fib(n) = fib(n-1) + fib(n-2) for n >= 2

This translates very directly into some Python:

1 def fib(n):
2 if n <= 1:
3 return n
4 t = fib(n-1) + fib(n-2)
5 return t

This is a particularly inefficient algorithm:

1 import time
2 t0 = time.clock()
3 n = 35
4 result = fib(n)
5 t1 = time.clock()
6

7 print("fib({0}) = {1}, ({2:.2f} secs)".format(n, result, t1-t0))

We get the correct result, but an exploding amount of work!

fib(35) = 9227465, (10.54 secs)

Run the recursive program in Python Tutor and try to see why the program is inefficient.

11.5 Glossary

base case A branch of the conditional statement in a recursive function that does not give rise to further recursive
calls.

11.4. Case study: Fibonacci numbers 113


Programming in Python, Release 1st Edition

infinite recursion A function that calls itself recursively without ever reaching any base case. Eventually, infinite
recursion causes a runtime error.
recursion The process of calling a function that is already executing.
recursive call The statement that calls an already executing function. Recursion can also be indirect — function f
can call g which calls h, and h could make a call back to f.
recursive definition A definition which defines something in terms of itself. To be useful it must include base cases
which are not recursive. In this way it differs from a circular definition. Recursive definitions often provide an
elegant way to express complex data structures, like a directory that can contain other directories, or a menu that
can contain other menus.

114 Chapter 11. Recursion


CHAPTER

TWELVE

SYNTAX DIAGRAMS

In this chapter, we present syntax diagrams for a subset of Python. In particular, the diagrams do not take indentation
into account nor default or optional parameters. A syntax diagram provides definitions for concepts.
Before we dive into Python let us consider the following toy example (only slightly related to English grammar):

sentence ::= subject verb adjective ["and" adjective]* "."


subject ::= "she" | "he"
verb ::= "is" | "wants to be"
adjective ::= "clever" | "charming" | "communicative" | "determined"

In the example syntax diagram, sentence, subject, verb and adjective are concepts that have their own definition. That
is, they occur as left-hand side of rules.
In the diagrams, we use the following conventions:
• underlined words are concepts (e.g. subject)
• every concept has its own definition (e.g. subject occurs at the left hand side of a rule and is therefore defined
by that rule)
• words between double quotes are constants and appear literally (e.g. “she”)
• right-hand sides of rules are regular expressions where
– | means or
– * means repetition
– any expression between rectangular brackets is optional
So, a sentence consists of a subject followed by a verb and an adjective and optionally followed by an arbitrary number
of repetitions of the constant “and” and another adjective. In addition, every sentence has to end with a period (“.”).
Then the sentences she is charming and clever. and he wants to be communicative. are correct according to the syntax
diagram while the sentences he is and she is clever and. are not.
Syntax diagrams provide insight in the syntax of a program language.

stmt ::= if_stmt


| while_stmt
| for_stmt
| funcdef
| simple_stmt
| stmt*

115
Programming in Python, Release 1st Edition

if_stmt ::= "if" boolean_expression ":" stmt


[( "elif" boolean_expression ":" stmt )*]
[ "else" ":" stmt]

while_stmt ::= "while" boolean_expression ":" stmt

for_stmt ::= "for" name "in" list_expression ":" stmt

funcdef ::= "def" name "(" [parameter_list] ")" ":" stmt


parameter_list ::= [name("," name)*]

function_call ::= name "(" argument_list ")"


argument_list ::= [expression ("," expression)*]

simple_stmt ::= assignment_stmt


| pass_stmt
| return_stmt
| break_stmt
| continue_stmt
| import_stmt
| global_stmt
| nonlocal_stmt

assignment_stmt ::= target assignment_operator expression


target ::= name | slicing
assignment_operator ::= "=" | "+=" | "-=" | "/=" | "*=" | "//=" | "%=" | "**="

pass_stmt ::= "pass"

return_stmt ::= "return" expression

break_stmt ::= "break"

continue_stmt ::= "continue"

import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*

116 Chapter 12. Syntax diagrams


Programming in Python, Release 1st Edition

| "from" module "import" name ["as" name]


module ::= (name ".")* name

global_stmt ::= "global" name ("," name)*

nonlocal_stmt ::= "nonlocal" name ("," name)*

expression ::= boolean_expression | arithmetic_expression

boolean_expression ::= "True" | "False"


| "(" boolean_expression ")"
| "not" boolean_expression
| boolean_expression "or" boolean_expression
| boolean_expression "and" boolean_expression
| function_call
| expression comparison_operator expression
comparison_operator ::= ">" | ">=" | "!=" | "==" | "<" | "<="

arithmetic_expression ::= number


| "(" arithmetic_expression ")"
| arithmetic_expression math_operator arithmetic_expression
| function_call
math_operator ::= "+" | "*" | "**" | "/" | "//" | "-"

list_expression ::= name


| slicing
| name "[" arithmetic_expression "]"
| function_call

slicing ::= name "[" [arithmetic_expression] ":" [arithmetic_expression] "]"

name ::= non-empty sequence of characters

number ::= non-empty sequence of digits

As an example consider the concept stmt (statement) that in turn can be one of five other concepts (if_stmt,
while_stmt, for_stmt, funcdef, simple_stmt) or a sequence of statements (stmt *).
As another example consider the concept if_stmt. The rule with left-hand side if_stmt stipulates that an if-statement
has the following form:
• the keyword ‘if’ followed by a Boolean expression, a colon, and a statement

117
Programming in Python, Release 1st Edition

• optionally followed by an arbitrary number of repetitions of the following pattern: the keyword elif, a Boolean
expression, a colon, and a statement
• optionally followed by the following pattern: the keyword else followed by a colon and a statement

118 Chapter 12. Syntax diagrams


CHAPTER

THIRTEEN

TUPLES

13.1 Tuples are used for grouping data

We can group together pairs of values by surrounding them with parentheses. Consider the example:

>>> year_born = ("Paris Hilton", 1981)

This is an example of a data structure — a mechanism for grouping and organizing data to make it easier to use.
The pair is an example of a tuple. Generalizing this, a tuple can be used to group any number of items into a single
compound value. Syntactically, a tuple is a comma-separated sequence of values. Although it is not necessary, it is
conventional to enclose tuples in parentheses:

>>> julia = ("Julia", "Roberts", 1967, "Duplicity", 2009, "Actress",


˓→"Atlanta, Georgia")

Tuples are useful for representing what other languages often call records — some related information that belongs
together, like your student record. There is no description of what each of these fields means, but we can guess. A
tuple lets us “chunk” together related information and use it as a single thing.
Tuples support the same sequence operations as strings. The index operator selects an element from a tuple.

>>> julia[2]
1967

But if we try to use item assignment to modify one of the elements of the tuple, we get an error:

>>> julia[0] = "X"


TypeError: 'tuple' object does not support item assignment

So like strings, tuples are immutable. Once Python has created a tuple in memory, it cannot be changed.
Of course, even if we can’t modify the elements of a tuple, we can always make the julia variable reference a new
tuple holding different information. To construct the new tuple, it is convenient that we can slice parts of the old
tuple and join up the bits to make the new tuple. So if julia has a new recent film, we could change her variable to
reference a new tuple that used some information from the old one:

>>> julia = julia[:3] + ("Eat Pray Love", 2010) + julia[5:]


>>> julia
("Julia", "Roberts", 1967, "Eat Pray Love", 2010, "Actress", "Atlanta,
˓→Georgia")

To create a tuple with a single element (but you’re probably not likely to do that too often), we have to include the final
comma, because without the final comma, Python treats the (5) below as an integer in parentheses:

119
Programming in Python, Release 1st Edition

>>> tup = (5,)


>>> type(tup)
<class 'tuple'>
>>> x = (5)
>>> type(x)
<class 'int'>

13.2 Tuple assignment

Python has a very powerful tuple assignment feature that allows a tuple of variables on the left of an assignment to
be assigned values from a tuple on the right of the assignment.

(name, surname, b_year, movie, m_year, profession, b_place) = julia

This does the equivalent of seven assignment statements, all on one easy line. One requirement is that the number of
variables on the left must match the number of elements in the tuple.
One way to think of tuple assignment is as tuple packing/unpacking.
In tuple packing, the values on the left are ‘packed’ together in a tuple:

>>> b = ("Bob", 19, "CS") # tuple packing

In tuple unpacking, the values in a tuple on the right are ‘unpacked’ into the variables/names on the right:

>>> b = ("Bob", 19, "CS")


>>> (name, age, studies) = b # tuple unpacking
>>> name
'Bob'
>>> age
19
>>> studies
'CS'

Once in a while, it is useful to swap the values of two variables. With conventional assignment statements, we have to
use a temporary variable. For example, to swap a and b:

1 temp = a
2 a = b
3 b = temp

Tuple assignment solves this problem neatly:

1 (a, b) = (b, a)

The left side is a tuple of variables; the right side is a tuple of values. Each value is assigned to its respective variable.
All the expressions on the right side are evaluated before any of the assignments. This feature makes tuple assignment
quite versatile.
Naturally, the number of variables on the left and the number of values on the right have to be the same:

>>> (a, b, c, d) = (1, 2, 3)


ValueError: need more than 3 values to unpack

120 Chapter 13. Tuples


Programming in Python, Release 1st Edition

13.3 Tuples as return values

Functions can always only return a single value, but by making that value a tuple, we can effectively group together
as many values as we like, and return them together. This is very useful — we often want to know some batsman’s
highest and lowest score, or we want to find the mean and the standard deviation, or we want to know the year, the
month, and the day, or if we’re doing some some ecological modelling we may want to know the number of rabbits
and the number of wolves on an island at a given time.
For example, we could write a function that returns both the area and the circumference of a circle of radius r:

1 def f(r):
2 """ Return (circumference, area) of a circle of radius r """
3 c = 2 * math.pi * r
4 a = math.pi * r * r
5 return (c, a)

13.4 Composability of Data Structures

We saw in an earlier chapter that we could make a list of pairs, and we had an example where one of the items in the
tuple was itself a list:

students = [
("John", ["CompSci", "Physics"]),
("Vusi", ["Maths", "CompSci", "Stats"]),
("Jess", ["CompSci", "Accounting", "Economics", "Management"]),
("Sarah", ["InfSys", "Accounting", "Economics", "CommLaw"]),
("Zuki", ["Sociology", "Economics", "Law", "Stats", "Music"])]

Tuples items can themselves be other tuples. For example, we could improve the information about our movie stars to
hold the full date of birth rather than just the year, and we could have a list of some of her movies and dates that they
were made, and so on:

julia_more_info = ( ("Julia", "Roberts"), (8, "October", 1967),


"Actress", ("Atlanta", "Georgia"),
[ ("Duplicity", 2009),
("Notting Hill", 1999),
("Pretty Woman", 1990),
("Erin Brockovich", 2000),
("Eat Pray Love", 2010),
("Mona Lisa Smile", 2003),
("Oceans Twelve", 2004) ])

Notice in this case that the tuple has just five elements — but each of those in turn can be another tuple, a list, a string,
or any other kind of Python value. This property is known as being heterogeneous, meaning that it can be composed
of elements of different types.

13.5 Glossary

data structure An organization of data for the purpose of making it easier to use.
immutable data value A data value which cannot be modified. Assignments to elements or slices (sub-parts) of
immutable values cause a runtime error.

13.3. Tuples as return values 121


Programming in Python, Release 1st Edition

mutable data value A data value which can be modified. The types of all mutable values are compound types. Lists
and dictionaries are mutable; strings and tuples are not.
tuple An immutable data value that contains related elements. Tuples are used to group together related data, such as
a person’s name, their age, and their gender.
tuple assignment An assignment to all of the elements in a tuple using a single assignment statement. Tuple assign-
ment occurs simultaneously rather than in sequence, making it useful for swapping values.

122 Chapter 13. Tuples


CHAPTER

FOURTEEN

DICTIONARIES

All of the compound data types we have studied in detail so far — strings, lists, and tuples — are sequence types,
which use integers as indices to access the values they contain within them.
Dictionaries are yet another kind of compound type. They are Python’s built-in mapping type. They map keys,
which can be any immutable type, to values, which can be any type (heterogeneous), just like the elements of a list or
tuple. In other languages, they are called associative arrays since they associate a key with a value.
As an example, we will create a dictionary to translate English words into Spanish. For this dictionary, the keys are
strings.
One way to create a dictionary is to start with the empty dictionary and add key:value pairs. The empty dictionary is
denoted {}:
>>> eng2sp = {}
>>> eng2sp["one"] = "uno"
>>> eng2sp["two"] = "dos"

The first assignment creates a dictionary named eng2sp; the other assignments add new key:value pairs to the dic-
tionary. We can print the current value of the dictionary in the usual way:
>>> print(eng2sp)
{"two": "dos", "one": "uno"}

The key:value pairs of the dictionary are separated by commas. Each pair contains a key and a value separated by a
colon.

Hashing
The order of the pairs may not be what was expected. Python uses complex algorithms, designed for very fast access,
to determine where the key:value pairs are stored in a dictionary. For our purposes we can think of this ordering as
unpredictable.
You also might wonder why we use dictionaries at all when the same concept of mapping a key to a value could be
implemented using a list of tuples:
>>> {"apples": 430, "bananas": 312, "oranges": 525, "pears": 217}
{'pears': 217, 'apples': 430, 'oranges': 525, 'bananas': 312}
>>> [('apples', 430), ('bananas', 312), ('oranges', 525), ('pears', 217)]
[('apples', 430), ('bananas', 312), ('oranges', 525), ('pears', 217)]

The reason is dictionaries are very fast, implemented using a technique called hashing, which allows us to access a
value very quickly. By contrast, the list of tuples implementation is slow. If we wanted to find a value associated with
a key, we would have to iterate over every tuple, checking the 0th element. What if the key wasn’t even in the list? We
would have to get to the end of it to find out.

123
Programming in Python, Release 1st Edition

Another way to create a dictionary is to provide a list of key:value pairs using the same syntax as the previous output:
>>> eng2sp = {"one": "uno", "two": "dos", "three": "tres"}

It doesn’t matter what order we write the pairs. The values in a dictionary are accessed with keys, not with indices, so
there is no need to care about ordering.
Here is how we use a key to look up the corresponding value:
>>> print(eng2sp["two"])
'dos'

The key "two" yields the value "dos".


Lists, tuples, and strings have been called sequences, because their items occur in order. The dictionary is the first
compound type that we’ve seen that is not a sequence, so we can’t index or slice a dictionary.

14.1 Dictionary operations

The del statement removes a key:value pair from a dictionary. For example, the following dictionary contains the
names of various fruits and the number of each fruit in stock:
>>> inventory = {"apples": 430, "bananas": 312, "oranges": 525, "pears": 217}
>>> print(inventory)
{'pears': 217, 'apples': 430, 'oranges': 525, 'bananas': 312}

If someone buys all of the pears, we can remove the entry from the dictionary:
>>> del inventory["pears"]
>>> print(inventory)
{'apples': 430, 'oranges': 525, 'bananas': 312}

Or if we’re expecting more pears soon, we might just change the value associated with pears:
>>> inventory["pears"] = 0
>>> print(inventory)
{'pears': 0, 'apples': 430, 'oranges': 525, 'bananas': 312}

A new shipment of bananas arriving could be handled like this:


>>> inventory["bananas"] += 200
>>> print(inventory)
{'pears': 0, 'apples': 430, 'oranges': 525, 'bananas': 512}

The len function also works on dictionaries; it returns the number of key:value pairs:
>>> len(inventory)
4

14.2 Dictionary methods

Dictionaries have a number of useful built-in methods.


The keys method returns what Python 3 calls a view of its underlying keys. We can iterate over the view, or turn the
view into a list like this:

124 Chapter 14. Dictionaries


Programming in Python, Release 1st Edition

1 for k in eng2sp.keys(): # The order of the k's is not defined


2 print("Got key", k, "which maps to value", eng2sp[k])
3

4 ks = list(eng2sp.keys())
5 print(ks)

This produces this output:

Got key three which maps to value tres


Got key two which maps to value dos
Got key one which maps to value uno
['three', 'two', 'one']

It is so common to iterate over the keys in a dictionary that we can omit the keys method call in the for loop —
iterating over a dictionary implicitly iterates over its keys:

1 for k in eng2sp:
2 print("Got key", k)

The values method is similar; it returns a view object which can be turned into a list:

>>> list(eng2sp.values())
['tres', 'dos', 'uno']

The items method also returns a view, which promises a list of tuples — one tuple for each key:value pair:

>>> list(eng2sp.items())
[('three', 'tres'), ('two', 'dos'), ('one', 'uno')]

Tuples are often useful for getting both the key and the value at the same time while we are looping:

1 for (k,v) in eng2sp.items():


2 print("Got",k,"that maps to",v)

This produces:

Got three that maps to tres


Got two that maps to dos
Got one that maps to uno

The in and not in operators can test if a key is in the dictionary:

>>> "one" in eng2sp


True
>>> "six" in eng2sp
False
>>> "tres" in eng2sp # Note that 'in' tests keys, not values.
False

This method can be very useful, since looking up a non-existent key in a dictionary causes a runtime error:

>>> eng2esp["dog"]
Traceback (most recent call last):
...
KeyError: 'dog'

14.2. Dictionary methods 125


Programming in Python, Release 1st Edition

14.3 Aliasing and copying

As in the case of lists, because dictionaries are mutable, we need to be aware of aliasing. Whenever two variables refer
to the same object, changes to one affect the other.
If we want to modify a dictionary and keep a copy of the original, use the copy method. For example, opposites
is a dictionary that contains pairs of opposites:

>>> opposites = {"up": "down", "right": "wrong", "yes": "no"}


>>> alias = opposites
>>> copy = opposites.copy() # Shallow copy

alias and opposites refer to the same object; copy refers to a fresh copy of the same dictionary. If we modify
alias, opposites is also changed:

>>> alias["right"] = "left"


>>> opposites["right"]
'left'

If we modify copy, opposites is unchanged:

>>> copy["right"] = "privilege"


>>> opposites["right"]
'left'

14.4 Sparse matrices

We previously used a list of lists to represent a matrix. That is a good choice for a matrix with mostly nonzero values,
but consider a sparse matrix like this one:

The list representation contains a lot of zeroes:

matrix = [[0, 0, 0, 1, 0],


[0, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 3, 0]]

An alternative is to use a dictionary. For the keys, we can use tuples that contain the row and column numbers. Here
is the dictionary representation of the same matrix:

>>> matrix = {(0, 3): 1, (2, 1): 2, (4, 3): 3}

We only need three key:value pairs, one for each nonzero element of the matrix. Each key is a tuple, and each value is
an integer.
To access an element of the matrix, we could use the [] operator:

126 Chapter 14. Dictionaries


Programming in Python, Release 1st Edition

>>> matrix[(0, 3)]


1

Notice that the syntax for the dictionary representation is not the same as the syntax for the nested list representation.
Instead of two integer indices, we use one index, which is a tuple of integers.
There is one problem. If we specify an element that is zero, we get an error, because there is no entry in the dictionary
with that key:

>>> matrix[(1, 3)]


KeyError: (1, 3)

The get method solves this problem:

>>> matrix.get((0, 3), 0)


1

The first argument is the key; the second argument is the value get should return if the key is not in the dictionary:

>>> matrix.get((1, 3), 0)


0

get definitely improves the semantics of accessing a sparse matrix. Shame about the syntax.

14.5 Memoization

If you played around with the fibo function from the chapter on recursion, you might have noticed that the bigger the
argument you provide, the longer the function takes to run. Furthermore, the run time increases very quickly. On one
of our machines, fib(20) finishes instantly, fib(30) takes about a second, and fib(40) takes roughly forever.
To understand why, consider this call graph for fib with n = 4:

A call graph shows some function frames (instances when the function has been invoked), with lines connecting each
frame to the frames of the functions it calls. At the top of the graph, fib with n = 4 calls fib with n = 3 and n
= 2. In turn, fib with n = 3 calls fib with n = 2 and n = 1. And so on.
Count how many times fib(0) and fib(1) are called. This is an inefficient solution to the problem, and it gets far
worse as the argument gets bigger.
A good solution is to keep track of values that have already been computed by storing them in a dictionary. A
previously computed value that is stored for later use is called a memo. Here is an implementation of fib using

14.5. Memoization 127


Programming in Python, Release 1st Edition

memos:

1 alreadyknown = {0: 0, 1: 1}
2

3 def fib(n):
4 if n not in alreadyknown:
5 new_value = fib(n-1) + fib(n-2)
6 alreadyknown[n] = new_value
7 return alreadyknown[n]

The dictionary named alreadyknown keeps track of the Fibonacci numbers we already know. We start with only
two pairs: 0 maps to 1; and 1 maps to 1.
Whenever fib is called, it checks the dictionary to determine if it contains the result. If it’s there, the function can
return immediately without making any more recursive calls. If not, it has to compute the new value. The new value
is added to the dictionary before the function returns.
Using this version of fib, our machines can compute fib(100) in an eyeblink.

>>> fib(100)
354224848179261915075

14.6 Counting letters

In the exercises in Chapter 8 (Strings) we wrote a function that counted the number of occurrences of a letter in a
string. A more general version of this problem is to form a frequency table of the letters in the string, that is, how
many times each letter appears.
Such a frequency table might be useful for compressing a text file. Because different letters appear with different
frequencies, we can compress a file by using shorter codes for common letters and longer codes for letters that appear
less frequently.
Dictionaries provide an elegant way to generate a frequency table:

>>> letter_counts = {}
>>> for letter in "Mississippi":
... letter_counts[letter] = letter_counts.get(letter, 0) + 1
...
>>> letter_counts
{'M': 1, 's': 4, 'p': 2, 'i': 4}

We start with an empty dictionary. For each letter in the string, we find the current count (possibly zero) and increment
it. At the end, the dictionary contains pairs of letters and their frequencies.
It might be more appealing to display the frequency table in alphabetical order. We can do that with the items and
sort methods:

>>> letter_items = list(letter_counts.items())


>>> letter_items.sort()
>>> print(letter_items)
[('M', 1), ('i', 4), ('p', 2), ('s', 4)]

Notice in the first line we had to call the type conversion function list. That turns the promise we get from items
into a list, a step that is needed before we can use the list’s sort method.

128 Chapter 14. Dictionaries


Programming in Python, Release 1st Edition

14.7 Glossary

call graph A graph consisting of nodes which represent function frames (or invocations), and directed edges (lines
with arrows) showing which frames gave rise to other frames.
dictionary A collection of key:value pairs that maps from keys to values. The keys can be any immutable value, and
the associated value can be of any type.
immutable data value A data value which cannot be modified. Assignments to elements or slices (sub-parts) of
immutable values cause a runtime error.
key A data item that is mapped to a value in a dictionary. Keys are used to look up values in a dictionary. Each key
must be unique across the dictionary.
key:value pair One of the pairs of items in a dictionary. Values are looked up in a dictionary by key.
mapping type A mapping type is a data type comprised of a collection of keys and associated values. Python’s only
built-in mapping type is the dictionary. Dictionaries implement the associative array abstract data type.
memo Temporary storage of precomputed values to avoid duplicating the same computation.
mutable data value A data value which can be modified. The types of all mutable values are compound types. Lists
and dictionaries are mutable; strings and tuples are not.

14.7. Glossary 129


Programming in Python, Release 1st Edition

130 Chapter 14. Dictionaries


CHAPTER

FIFTEEN

MODULES

A module is a file containing Python definitions and statements intended for use in other Python programs. There
are many Python modules that come with Python as part of the standard library. We have seen at least two of these
already, the turtle module and the string module.
We have also shown you how to access help. The help system contains a listing of all the standard modules that are
available with Python. Play with help!

15.1 Random numbers

We often want to use random numbers in programs, here are a few typical uses:
• To play a game of chance where the computer needs to throw some dice, pick a number, or flip a coin,
• To shuffle a deck of playing cards randomly,
• To allow/make an enemy spaceship appear at a random location and start shooting at the player,
• To simulate possible rainfall when we make a computerized model for estimating the environmental impact of
building a dam,
• For encrypting banking sessions on the Internet.
Python provides a module random that helps with tasks like this. You can look it up using help, but here are the key
things we’ll do with it:

1 import random
2

3 # Create a black box object that generates random numbers


4 rng = random.Random()
5

6 dice_throw = rng.randrange(1,7) # Return an int, one of 1,2,3,4,5,6


7 delay_in_seconds = rng.random() * 5.0

The randrange method call generates an integer between its lower and upper argument, using the same semantics
as range — so the lower bound is included, but the upper bound is excluded. All the values have an equal probability
of occurring (i.e. the results are uniformly distributed). Like range, randrange can also take an optional step
argument. So let’s assume we needed a random odd number less than 100, we could say:

1 r_odd = rng.randrange(1, 100, 2)

Other methods can also generate other distributions e.g. a bell-shaped, or “normal” distribution might be more appro-
priate for estimating seasonal rainfall, or the concentration of a compound in the body after taking a dose of medicine.

131
Programming in Python, Release 1st Edition

The random method returns a floating point number in the interval [0.0, 1.0) — the square bracket means “closed
interval on the left” and the round parenthesis means “open interval on the right”. In other words, 0.0 is possible, but
all returned numbers will be strictly less than 1.0. It is usual to scale the results after calling this method, to get them
into an interval suitable for your application. In the case shown here, we’ve converted the result of the method call to
a number in the interval [0.0, 5.0). Once more, these are uniformly distributed numbers — numbers close to 0 are just
as likely to occur as numbers close to 0.5, or numbers close to 1.0.
This example shows how to shuffle a list. (shuffle cannot work directly with a lazy promise, so notice that we had
to convert the range object using the list type converter first.)

1 cards = list(range(52)) # Generate ints [0 .. 51]


2 # representing a pack of cards.
3 rng.shuffle(cards) # Shuffle the pack

15.1.1 Repeatability and Testing

Random number generators are based on a deterministic algorithm — repeatable and predictable. So they’re called
pseudo-random generators — they are not genuinely random. They start with a seed value. Each time you ask for
another random number, you’ll get one based on the current seed attribute, and the state of the seed (which is one of
the attributes of the generator) will be updated.
For debugging and for writing unit tests, it is convenient to have repeatability — programs that do the same thing every
time they are run. We can arrange this by forcing the random number generator to be initialized with a known seed
every time. (Often this is only wanted during testing — playing a game of cards where the shuffled deck was always
in the same order as last time you played would get boring very rapidly!)

1 drng = random.Random(123) # Create generator with known starting state

This alternative way of creating a random number generator gives an explicit seed value to the object. Without this
argument, the system probably uses something based on the time. So grabbing some random numbers from drng
today will give you precisely the same random sequence as it will tomorrow!

15.1.2 Picking balls from bags, throwing dice, shuffling a pack of cards

Here is an example to generate a list containing n random ints between a lower and an upper bound:

1 import random
2

3 def make_random_ints(num, lower_bound, upper_bound):


4 """
5 Generate a list containing num random ints between lower_bound
6 and upper_bound. upper_bound is an open bound.
7 """
8 rng = random.Random() # Create a random number generator
9 result = []
10 for i in range(num):
11 result.append(rng.randrange(lower_bound, upper_bound))
12 return result

>>> make_random_ints(5, 1, 13) # Pick 5 random month numbers


[8, 1, 8, 5, 6]

Notice that we got a duplicate in the result. Often this is wanted, e.g. if we throw a die five times, we would expect
some duplicates.

132 Chapter 15. Modules


Programming in Python, Release 1st Edition

But what if you don’t want duplicates? If you wanted 5 distinct months, then this algorithm is wrong. In this case a
good algorithm is to generate the list of possibilities, shuffle it, and slice off the number of elements you want:
1 xs = list(range(1,13)) # Make list 1..12 (there are no duplicates)
2 rng = random.Random() # Make a random number generator
3 rng.shuffle(xs) # Shuffle the list
4 result = xs[:5] # Take the first five elements

In statistics courses, the first case — allowing duplicates — is usually described as pulling balls out of a bag with
replacement — you put the drawn ball back in each time, so it can occur again. The latter case, with no duplicates, is
usually described as pulling balls out of the bag without replacement. Once the ball is drawn, it doesn’t go back to be
drawn again. TV lotto games work like this.
The second “shuffle and slice” algorithm would not be so great if you only wanted a few elements, but from a very
large domain. Suppose I wanted five numbers between one and ten million, without duplicates. Generating a list of
ten million items, shuffling it, and then slicing off the first five would be a performance disaster! So let us have another
try:
1 import random
2

3 def make_random_ints_no_dups(num, lower_bound, upper_bound):


4 """
5 Generate a list containing num random ints between
6 lower_bound and upper_bound. upper_bound is an open bound.
7 The result list cannot contain duplicates.
8 """
9 result = []
10 rng = random.Random()
11 for i in range(num):
12 while True:
13 candidate = rng.randrange(lower_bound, upper_bound)
14 if candidate not in result:
15 break
16 result.append(candidate)
17 return result
18

19 xs = make_random_ints_no_dups(5, 1, 10000000)
20 print(xs)

This agreeably produces 5 random numbers, without duplicates:


[3344629, 1735163, 9433892, 1081511, 4923270]

Even this function has its pitfalls. Can you spot what is going to happen in this case?
1 xs = make_random_ints_no_dups(10, 1, 6)

15.2 The math module

The math module contains the kinds of mathematical functions you’d typically find on your calculator (sin, cos,
sqrt, asin, log, log10) and some mathematical constants like pi and e:
>>> import math
>>> math.pi # Constant pi
3.141592653589793
(continues on next page)

15.2. The math module 133


Programming in Python, Release 1st Edition

(continued from previous page)


>>> math.e # Constant natural log base
2.718281828459045
>>> math.sqrt(2.0) # Square root function
1.4142135623730951
>>> math.radians(90) # Convert 90 degrees to radians
1.5707963267948966
>>> math.sin(math.radians(90)) # Find sin of 90 degrees
1.0
>>> math.asin(1.0) * 2 # Double the arcsin of 1.0 to get pi
3.141592653589793

Like almost all other programming languages, angles are expressed in radians rather than degrees. There are two
functions radians and degrees to convert between these two popular ways of measuring angles.
Notice another difference between this module and our use of random and turtle: in random and turtle we
create objects and we call methods on the object. This is because objects have state — a turtle has a color, a position,
a heading, etc., and every random number generator has a seed value that determines its next result.
Mathematical functions are “pure” and don’t have any state — calculating the square root of 2.0 doesn’t depend on
any kind of state or history about what happened in the past. So the functions are not methods of an object — they are
simply functions that are grouped together in a module called math.

15.3 Three import statement variants

Here are three different ways to import names into the current namespace, and to use them:

1 import math
2 x = math.sqrt(10)

Here just the single identifier math is added to the current namespace. If you want to access one of the functions in
the module, you need to use the dot notation to get to it.
Here is a different arrangement:

1 from math import cos, sin, sqrt


2 x = sqrt(10)

The names are added directly to the current namespace, and can be used without qualification. The name math is not
itself imported, so trying to use the qualified form math.sqrt would give an error.
Then we have a convenient shorthand:

1 from math import * # Import all the identifiers from math,


2 # adding them to the current namespace.
3 x = sqrt(10) # Use them without qualification.

Of these three, the first method is generally preferred, even though it means a little more typing each time. Although,
we can make things shorter by importing a module under a different name:

1 >>> import math as m


2 >>> m.pi
3 3.141592653589793

But hey, with nice editors that do auto-completion, and fast fingers, that’s a small price!
Finally, observe this case:

134 Chapter 15. Modules


Programming in Python, Release 1st Edition

1 def area(radius):
2 import math
3 return math.pi * radius * radius
4

5 x = math.sqrt(10) # This gives an error

Here we imported math, but we imported it into the local namespace of area. So the name is usable within the
function body, but not in the enclosing script, because it is not in the global namespace.

15.3. Three import statement variants 135


Programming in Python, Release 1st Edition

136 Chapter 15. Modules


CHAPTER

SIXTEEN

DEBUGGING

Different kinds of errors can occur in a program, and it is useful to distinguish among them in order to track them
down more quickly:
1. Syntax errors are produced by Python when it is translating the source code into byte code. They usually indicate
that there is something wrong with the syntax of the program. Example: Omitting the colon at the end of a def
statement yields the somewhat redundant message SyntaxError: invalid syntax.
2. Runtime errors are produced by the runtime system if something goes wrong while the program is running. Most
runtime error messages include information about where the error occurred and what functions were executing.
Example: An infinite recursion eventually causes a runtime error of maximum recursion depth exceeded.
3. Semantic errors are problems with a program that compiles and runs but doesn’t do the right thing. Example:
An expression may not be evaluated in the order you expect, yielding an unexpected result.
The first step in debugging is to figure out which kind of error you are dealing with. Although the following sections
are organized by error type, some techniques are applicable in more than one situation.

16.1 Syntax errors

Syntax errors are usually easy to fix once you figure out what they are. Unfortunately, the error messages are of-
ten not helpful. The most common messages are SyntaxError: invalid syntax and SyntaxError:
invalid token, neither of which is very informative.
On the other hand, the message does tell you where in the program the problem occurred. Actually, it tells you where
Python noticed a problem, which is not necessarily where the error is. Sometimes the error is prior to the location of
the error message, often on the preceding line.
If you are building the program incrementally, you should have a good idea about where the error is. It will be in the
last line you added.
If you are copying code from a book, start by comparing your code to the book’s code very carefully. Check every
character. At the same time, remember that the book might be wrong, so if you see something that looks like a syntax
error, it might be.
Here are some ways to avoid the most common syntax errors:
1. Make sure you are not using a Python keyword for a variable name.
2. Check that you have a colon at the end of the header of every compound statement, including for, while, if,
and def statements.
3. Check that indentation is consistent. You may indent with either spaces or tabs but it’s best not to mix them.
Each level should be nested the same amount.
4. Make sure that any strings in the code have matching quotation marks.

137
Programming in Python, Release 1st Edition

5. If you have multiline strings with triple quotes (single or double), make sure you have terminated the string
properly. An unterminated string may cause an invalid token error at the end of your program, or it may
treat the following part of the program as a string until it comes to the next string. In the second case, it might
not produce an error message at all!
6. An unclosed bracket — (, {, or [ — makes Python continue with the next line as part of the current statement.
Generally, an error occurs almost immediately in the next line.
7. Check for the classic = instead of == inside a conditional.
If nothing works, move on to the next section. . .

16.2 I can’t get my program to run no matter what I do.

If the compiler says there is an error and you don’t see it, that might be because you and the compiler are not looking
at the same code. Check your programming environment to make sure that the program you are editing is the one
Python is trying to run. If you are not sure, try putting an obvious and deliberate syntax error at the beginning of the
program. Now run (or import) it again. If the compiler doesn’t find the new error, there is probably something wrong
with the way your environment is set up.
If this happens, one approach is to start again with a new program like Hello, World!, and make sure you can get a
known program to run. Then gradually add the pieces of the new program to the working one.

16.3 Runtime errors

Once your program is syntactically correct, Python can import it and at least start running it. What could possibly go
wrong?

16.4 My program does absolutely nothing.

This problem is most common when your file consists of functions and classes but does not actually invoke anything
to start execution. This may be intentional if you only plan to import this module to supply classes and functions.
If it is not intentional, make sure that you are invoking a function to start execution, or execute one from the interactive
prompt. Also see the Flow of Execution section below.

16.5 My program hangs.

If a program stops and seems to be doing nothing, we say it is hanging. Often that means that it is caught in an infinite
loop or an infinite recursion.
1. If there is a particular loop that you suspect is the problem, add a print statement immediately before the loop
that says entering the loop and another immediately after that says exiting the loop.
2. Run the program. If you get the first message and not the second, you’ve got an infinite loop. Go to the Infinite
Loop section below.
3. Most of the time, an infinite recursion will cause the program to run for a while and then produce a RuntimeError:
Maximum recursion depth exceeded error. If that happens, go to the Infinite Recursion section below.
4. If you are not getting this error but you suspect there is a problem with a recursive method or function, you can
still use the techniques in the Infinite Recursion section.

138 Chapter 16. Debugging


Programming in Python, Release 1st Edition

5. If neither of those steps works, start testing other loops and other recursive functions and methods.
6. If that doesn’t work, then it is possible that you don’t understand the flow of execution in your program. Go to
the Flow of Execution section below.

16.6 Infinite Loop

If you think you have an infinite loop and you think you know what loop is causing the problem, add a print
statement at the end of the loop that prints the values of the variables in the condition and the value of the condition.
For example:

1 while x > 0 and y < 0:


2 # Do something to x
3 # Do something to y
4

5 print("x: ", x)
6 print("y: ", y)
7 print("condition: ", (x > 0 and y < 0))

Now when you run the program, you will see three lines of output for each time through the loop. The last time
through the loop, the condition should be False. If the loop keeps going, you will be able to see the values of x and
y, and you might figure out why they are not being updated correctly.
In a development environment like PyScripter, one can also set a breakpoint at the start of the loop, and single-step
through the loop. While you do this, inspect the values of x and y by hovering your cursor over them.
Of course, all programming and debugging require that you have a good mental model of what the algorithm ought
to be doing: if you don’t understand what ought to happen to x and y, printing or inspecting its value is of little use.
Probably the best place to debug the code is away from your computer, working on your understanding of what should
be happening.

16.7 Infinite Recursion

Most of the time, an infinite recursion will cause the program to run for a while and then produce a Maximum
recursion depth exceeded error.
If you suspect that a function or method is causing an infinite recursion, start by checking to make sure that there is
a base case. In other words, there should be some condition that will cause the function or method to return without
making a recursive invocation. If not, then you need to rethink the algorithm and identify a base case.
If there is a base case but the program doesn’t seem to be reaching it, add a print statement at the beginning of the
function or method that prints the parameters. Now when you run the program, you will see a few lines of output every
time the function or method is invoked, and you will see the parameters. If the parameters are not moving toward the
base case, you will get some ideas about why not.
Once again, if you have an environment that supports easy single-stepping, breakpoints, and inspection, learn to use
them well. It is our opinion that walking through code step-by-step builds the best and most accurate mental model of
how computation happens. Use it if you have it!

16.6. Infinite Loop 139


Programming in Python, Release 1st Edition

16.8 Flow of Execution

If you are not sure how the flow of execution is moving through your program, add print statements to the beginning
of each function with a message like entering function foo, where foo is the name of the function.
Now when you run the program, it will print a trace of each function as it is invoked.
If you’re not sure, step through the program with your debugger.

16.9 When I run the program I get an exception.

If something goes wrong during runtime, Python prints a message that includes the name of the exception, the line of
the program where the problem occurred, and a traceback.
Put a breakpoint on the line causing the exception, and look around!
The traceback identifies the function that is currently running, and then the function that invoked it, and then the
function that invoked that, and so on. In other words, it traces the path of function invocations that got you to where
you are. It also includes the line number in your file where each of these calls occurs.
The first step is to examine the place in the program where the error occurred and see if you can figure out what
happened. These are some of the most common runtime errors:
NameError You are trying to use a variable that doesn’t exist in the current environment. Remember that local
variables are local. You cannot refer to them from outside the function where they are defined.
TypeError There are several possible causes:
1. You are trying to use a value improperly. Example: indexing a string, list, or tuple with something other
than an integer.
2. There is a mismatch between the items in a format string and the items passed for conversion. This can
happen if either the number of items does not match or an invalid conversion is called for.
3. You are passing the wrong number of arguments to a function or method. For methods, look at the method
definition and check that the first parameter is self. Then look at the method invocation; make sure you
are invoking the method on an object with the right type and providing the other arguments correctly.
KeyError You are trying to access an element of a dictionary using a key value that the dictionary does not contain.
AttributeError You are trying to access an attribute or method that does not exist.
IndexError The index you are using to access a list, string, or tuple is greater than its length minus one. Immediately
before the site of the error, add a print statement to display the value of the index and the length of the
sequence. Is the sequence the right size? Is the index the right value?

16.10 I added so many print statements I get inundated with output.

One of the problems with using print statements for debugging is that you can end up buried in output. There are
two ways to proceed: simplify the output or simplify the program.
To simplify the output, you can remove or comment out print statements that aren’t helping, or combine them, or
format the output so it is easier to understand.
To simplify the program, there are several things you can do. First, scale down the problem the program is working
on. For example, if you are sorting a sequence, sort a small sequence. If the program takes input from the user, give it
the simplest input that causes the problem.

140 Chapter 16. Debugging


Programming in Python, Release 1st Edition

Second, clean up the program. Remove dead code and reorganize the program to make it as easy to read as possible.
For example, if you suspect that the problem is in a deeply nested part of the program, try rewriting that part with
simpler structure. If you suspect a large function, try splitting it into smaller functions and testing them separately.
Often the process of finding the minimal test case leads you to the bug. If you find that a program works in one
situation but not in another, that gives you a clue about what is going on.
Similarly, rewriting a piece of code can help you find subtle bugs. If you make a change that you think doesn’t affect
the program, and it does, that can tip you off.
You can also wrap your debugging print statements in some condition, so that you suppress much of the output.
For example, if you are trying to find an element using a binary search, and it is not working, you might code up a
debugging print statement inside a conditional: if the range of candidate elements is less that 6, then print debugging
information, otherwise don’t print.
Similarly, breakpoints can be made conditional: you can set a breakpoint on a statement, then edit the breakpoint to
say “only break if this expression becomes true”.

16.11 Semantic errors

In some ways, semantic errors are the hardest to debug, because the compiler and the runtime system provide no
information about what is wrong. Only you know what the program is supposed to do, and only you know that it isn’t
doing it.
The first step is to make a connection between the program text and the behavior you are seeing. You need a hypothesis
about what the program is actually doing. One of the things that makes that hard is that computers run so fast.
You will often wish that you could slow the program down to human speed, and with some debuggers you can. But
the time it takes to insert a few well-placed print statements is often short compared to setting up the debugger,
inserting and removing breakpoints, and walking the program to where the error is occurring.

16.12 My program doesn’t work.

You should ask yourself these questions:


1. Is there something the program was supposed to do but which doesn’t seem to be happening? Find the section
of the code that performs that function and make sure it is executing when you think it should.
2. Is something happening that shouldn’t? Find code in your program that performs that function and see if it is
executing when it shouldn’t.
3. Is a section of code producing an effect that is not what you expected? Make sure that you understand the code
in question, especially if it involves invocations to functions or methods in other Python modules. Read the
documentation for the functions you invoke. Try them out by writing simple test cases and checking the results.
In order to program, you need to have a mental model of how programs work. If you write a program that doesn’t do
what you expect, very often the problem is not in the program; it’s in your mental model.
The best way to correct your mental model is to break the program into its components (usually the functions and
methods) and test each component independently. Once you find the discrepancy between your model and reality, you
can solve the problem.
Of course, you should be building and testing components as you develop the program. If you encounter a problem,
there should be only a small amount of new code that is not known to be correct.

16.11. Semantic errors 141


Programming in Python, Release 1st Edition

16.13 I’ve got a big hairy expression and it doesn’t do what I expect.

Writing complex expressions is fine as long as they are readable, but they can be hard to debug. It is often a good idea
to break a complex expression into a series of assignments to temporary variables.
For example:

1 self.hands[i].add_card(self.hands[self.find_neighbor(i)].pop_card())

This can be rewritten as:

1 neighbor = self.find_neighbor (i)


2 picked_card = self.hands[neighbor].pop_card()
3 self.hands[i].add_card(picked_card)

The explicit version is easier to read because the variable names provide additional documentation, and it is easier to
debug because you can check the types of the intermediate variables and display or inspect their values.
Another problem that can occur with big expressions is that the order of evaluation may not be what you expect. For
example, if you are translating the expression x/2pi into Python, you might write:

y = x / 2 * math.pi

That is not correct because multiplication and division have the same precedence and are evaluated from left to right.
So this expression computes (x/2)pi.
A good way to debug expressions is to add parentheses to make the order of evaluation explicit:

y = x / (2 * math.pi)

Whenever you are not sure of the order of evaluation, use parentheses. Not only will the program be correct (in the
sense of doing what you intended), it will also be more readable for other people who haven’t memorized the rules of
precedence.

16.14 I’ve got a function or method that doesn’t return what I expect.

If you have a return statement with a complex expression, you don’t have a chance to print the return value
before returning. Again, you can use a temporary variable. For example, instead of:

return self.hands[i].remove_matches()

you could write:

count = self.hands[i].remove_matches()
return count

Now you have the opportunity to display or inspect the value of count before returning.

16.15 I’m really, really stuck and I need help.

First, try getting away from the computer for a few minutes. Computers emit waves that affect the brain, causing these
effects:
1. Frustration and/or rage.

142 Chapter 16. Debugging


Programming in Python, Release 1st Edition

2. Superstitious beliefs (the computer hates me) and magical thinking (the program only works when I wear my
hat backward).
3. Random-walk programming (the attempt to program by writing every possible program and choosing the one
that does the right thing).
If you find yourself suffering from any of these symptoms, get up and go for a walk. When you are calm, think about
the program. What is it doing? What are some possible causes of that behavior? When was the last time you had a
working program, and what did you do next?
Sometimes it just takes time to find a bug. We often find bugs when we are away from the computer and let our minds
wander. Some of the best places to find bugs are trains, showers, and in bed, just before you fall asleep.

16.16 No, I really need help.

It happens. Even the best programmers occasionally get stuck. Sometimes you work on a program so long that you
can’t see the error. A fresh pair of eyes is just the thing.
Before you bring someone else in, make sure you have exhausted the techniques described here. Your program should
be as simple as possible, and you should be working on the smallest input that causes the error. You should have
print statements in the appropriate places (and the output they produce should be comprehensible). You should
understand the problem well enough to describe it concisely.
When you bring someone in to help, be sure to give them the information they need:
1. If there is an error message, what is it and what part of the program does it indicate?
2. What was the last thing you did before this error occurred? What were the last lines of code that you wrote, or
what is the new test case that fails?
3. What have you tried so far, and what have you learned?
Good instructors and helpers will also do something that should not offend you: they won’t believe when you tell
them “I’m sure all the input routines are working just fine, and that I’ve set up the data correctly!”. They will want
to validate and check things for themselves. After all, your program has a bug. Your understanding and inspection of
the code have not found it yet. So you should expect to have your assumptions challenged. And as you gain skills and
help others, you’ll need to do the same for them.
When you find the bug, take a second to think about what you could have done to find it faster. Next time you see
something similar, you will be able to find the bug more quickly.
Remember, the goal is not just to make the program work. The goal is to learn how to make the program work.

16.16. No, I really need help. 143


Programming in Python, Release 1st Edition

144 Chapter 16. Debugging


CHAPTER

SEVENTEEN

SOME TIPS, TRICKS, AND COMMON ERRORS

These are small summaries of ideas, tips, and commonly seen errors that might be helpful to those beginning Python.

17.1 Functions

Functions help us with our mental chunking: they allow us to group together statements for a high-level purpose, e.g.
a function to sort a list of items, a function to make the turtle draw a spiral, or a function to compute the mean and
standard deviation of some measurements.
There are two kinds of functions: fruitful, or value-returning functions, which calculate and return a value, and we use
them because we’re primarily interested in the value they’ll return. Void (non-fruitful) functions are used because they
perform actions that we want done — e.g. make a turtle draw a rectangle, or print the first thousand prime numbers.
They always return None — a special dummy value.

Tip: None is not a string


Values like None, True and False are not strings: they are special values in Python, and are in the list of keywords
we gave in chapter 2 (Variables, expressions, and statements). Keywords are special in the language: they are part of
the syntax. So we cannot create our own variable or function with a name True — we’ll get a syntax error. (Built-in
functions are not privileged like keywords: we can define our own variable or function called len, but we’d be silly
to do so!)

Along with the fruitful/void families of functions, there are two flavors of the return statement in Python: one that
returns a useful value, and the other that returns nothing, or None. And if we get to the end of any function and we
have not explicitly executed any return statement, Python automatically returns the value None.

Tip: Understand what the function needs to return


Perhaps nothing — some functions exists purely to perform actions rather than to calculate and return a result. But if
the function should return a value, make sure all execution paths do return the value.

To make functions more useful, they are given parameters. So a function to make a turtle draw a square might have
two parameters — one for the turtle that needs to do the drawing, and another for the size of the square. See the first
example in Chapter 4 (Functions) — that function can be used with any turtle, and for any size square. So it is much
more general than a function that always uses a specific turtle, say tess to draw a square of a specific size, say 30.

Tip: Use parameters to generalize functions

145
Programming in Python, Release 1st Edition

Understand which parts of the function will be hard-coded and unchangeable, and which parts should become param-
eters so that they can be customized by the caller of the function.

Tip: Try to relate Python functions to ideas we already know


In math, we’re familiar with functions like f(x) = 3x + 5. We already understand that when we call the function
f(3) we make some association between the parameter x and the argument 3. Try to draw parallels to argument
passing in Python.

Quiz: Is the function f(z) = 3z + 5 the same as function f above?

17.1.1 Problems with logic and flow of control

We often want to know if some condition holds for any item in a list, e.g. “does the list have any odd numbers?” This
is a common mistake:

1 def any_odd(xs): # Buggy version


2 """ Return True if there is an odd number in xs, a list of integers. """
3 for v in xs:
4 if v % 2 == 1:
5 return True
6 else:
7 return False

Can we spot two problems here? As soon as we execute a return, we’ll leave the function. So the logic of saying
“If I find an odd number I can return True” is fine. However, we cannot return False after only looking at one item
— we can only return False if we’ve been through all the items, and none of them are odd. So line 6 should not be
there, and line 7 has to be outside the loop. To find the second problem above, consider what happens if you call this
function with an argument that is an empty list. Here is a corrected version:

1 def any_odd(xs):
2 """ Return True if there is an odd number in xs, a list of integers. """
3 for v in xs:
4 if v % 2 == 1:
5 return True
6 return False

This “eureka”, or “short-circuit” style of returning from a function as soon as we are certain what the outcome will be
was first seen in Section 8.10, in the chapter on strings.
It is preferred over this one, which also works correctly:

1 def any_odd(xs):
2 """ Return True if there is an odd number in xs, a list of integers. """
3 count = 0
4 for v in xs:
5 if v % 2 == 1:
6 count += 1 # Count the odd numbers
7 if count > 0:
8 return True
9 else:
10 return False

The performance disadvantage of this one is that it traverses the whole list, even if it knows the outcome very early on.

146 Chapter 17. Some Tips, Tricks, and Common Errors


Programming in Python, Release 1st Edition

Tip: Think about the return conditions of the function


Do I need to look at all elements in all cases? Can I shortcut and take an early exit? Under what conditions? When
will I have to examine all the items in the list?

The code in lines 7-10 can also be tightened up. The expression count > 0 evaluates to a Boolean value, either
True or False. The value can be used directly in the return statement. So we could cut out that code and simply
have the following:

1 def any_odd(xs):
2 """ Return True if there is an odd number in xs, a list of integers. """
3 count = 0
4 for v in xs:
5 if v % 2 == 1:
6 count += 1 # Count the odd numbers
7 return count > 0 # Aha! a programmer who understands that Boolean
8 # expressions are not just used in if statements!

Although this code is tighter, it is not as nice as the one that did the short-circuit return as soon as the first odd number
was found.

Tip: Generalize your use of Booleans


Mature programmers won’t write if is_prime(n) == True: when they could say instead if
is_prime(n): Think more generally about Boolean values, not just in the context of if or while state-
ments. Like arithmetic expressions, they have their own set of operators (and, or, not) and values (True,
False) and can be assigned to variables, put into lists, etc. A good resource for improving your use of Booleans
is http://en.wikibooks.org/wiki/Non-Programmer%27s_Tutorial_for_Python_3/Boolean_Expressions

Exercise time:
• How would we adapt this to make another function which returns True if all the numbers are odd? Can you
still use a short-circuit style?
• How would we adapt it to return True if at least three of the numbers are odd? Short-circuit the traversal when
the third odd number is found — don’t traverse the whole list unless we have to.

17.1.2 Local variables

Functions are called, or activated, and while they’re busy they create their own stack frame which holds local variables.
A local variable is one that belongs to the current activation. As soon as the function returns (whether from an explicit
return statement or because Python reached the last statement), the stack frame and its local variables are all destroyed.
The important consequence of this is that a function cannot use its own variables to remember any kind of state between
different activations. It cannot count how many times it has been called, or remember to switch colors between red
and blue UNLESS it makes use of variables that are global. Global variables will survive even after our function has
exited, so they are the correct way to maintain information between calls.

1 sz = 2
2 def h2():
3 """ Draw the next step of a spiral on each call. """
4 global sz
5 tess.turn(42)
6 tess.forward(sz)
7 sz += 1

17.1. Functions 147


Programming in Python, Release 1st Edition

This fragment assumes our turtle is tess. Each time we call h2() it turns, draws, and increases the global variable
sz. Python always assumes that an assignment to a variable (as in line 7) means that we want a new local variable,
unless we’ve provided a global declaration (on line 4). So leaving out the global declaration means this does not
work.

Tip: Local variables do not survive when you exit the function
Use a Python visualizer like the one at http://netserv.ict.ru.ac.za/python3_viz to build a strong understanding of func-
tion calls, stack frames, local variables, and function returns.

Tip: Assignment in a function creates a local variable


Any assignment to a variable within a function means Python will make a local variable, unless we override with
global.

17.1.3 Event handler functions

Our chapter on event handling showed three different kinds of events that we could handle. They each have their own
subtle points that can trip us up.
• Event handlers are void functions — they don’t return any values.
• They’re automatically called by the Python interpreter in response to an event, so we don’t get to see the code
that calls them.
• A mouse-click event passes two coordinate arguments to its handler, so when we write this handler we have to
provide for two parameters (usually named x and y). This is how the handler knows where the mouse click
occurred.
• A keypress event handler has to be bound to the key it responds to. There is a messy extra step when using
keypresses: we have to remember to issue a wn.listen() before our program will receive any keypresses.
But if the user presses the key 10 times, the handler will be called ten times.
• Using a timer to create a future-dated event only causes one call to the handler. If we want repeated periodic
handler activations, then from within the handler we call wn.ontimer(....) to set up the next event.

17.2 String handling

There are only four really important operations on strings, and we’ll be able to do just about anything. There are many
more nice-to-have methods (we’ll call them sugar coating) that can make life easier, but if we can work with the basic
four operations smoothly, we’ll have a great grounding.
• len(str) finds the length of a string.
• str[i] the subscript operation extracts the i’th character of the string, as a new string.
• str[i:j] the slice operation extracts a substring out of a string.
• str.find(target) returns the index where target occurs within the string, or -1 if it is not found.
So if we need to know if “snake” occurs as a substring within s, we could write

1 if s.find("snake") >= 0: ...


2 if "snake" in s: ... # Also works, nice-to-know sugar coating!

148 Chapter 17. Some Tips, Tricks, and Common Errors


Programming in Python, Release 1st Edition

It would be wrong to split the string into words unless we were asked whether the word “snake” occurred in the string.
Suppose we’re asked to read some lines of data and find function definitions, e.g.: def
some_function_name(x, y):, and we are further asked to isolate and work with the name of the func-
tion. (Let’s say, print it.)

1 s = "..." # Get the next line from somewhere


2 def_pos = s.find("def ") # Look for "def " in the line
3 if def_pos == 0: # If it occurs at the left margin
4 op_index = s.find("(") # Find the index of the open parenthesis
5 fnname = s[4:op_index] # Slice out the function name
6 print(fnname) # ... and work with it.

One can extend these ideas:


• What if the function def was indented, and didn’t start at column 0? The code would need a bit of adjustment,
and we’d probably want to be sure that all the characters in front of the def_pos position were spaces. We
would not want to do the wrong thing on data like this: # I def initely like Python!
• We’ve assumed on line 3 that we will find an open parenthesis. It may need to be checked that we did!
• We have also assumed that there was exactly one space between the keyword def and the start of the function
name. It will not work nicely for def f(x)
As we’ve already mentioned, there are many more “sugar-coated” methods that let us work more easily with strings.
There is an rfind method, like find, that searches from the end of the string backwards. It is useful if we want
to find the last occurrence of something. The lower and upper methods can do case conversion. And the split
method is great for breaking a string into a list of words, or into a list of lines. We’ve also made extensive use in this
book of the format method. In fact, if we want to practice reading the Python documentation and learning some new
methods on our own, the string methods are an excellent resource.
Exercises:
• Suppose any line of text can contain at most one url that starts with “http://” and ends at the next space in the
line. Write a fragment of code to extract and print the full url if it is present. (Hint: read the documentation for
find. It takes some extra arguments, so you can set a starting point from which it will search.)
• Suppose a string contains at most one substring “< . . . >”. Write a fragment of code to extract and print the
portion of the string between the angle brackets.

17.3 Looping and lists

Computers are useful because they can repeat computation, accurately and fast. So loops are going to be a central
feature of almost all programs you encounter.

Tip: Don’t create unnecessary lists


Lists are useful if you need to keep data for later computation. But if you don’t need lists, it is probably better not to
generate them.

Here are two functions that both generate ten million random numbers, and return the sum of the numbers. They both
work.

1 import random
2 joe = random.Random()
3

(continues on next page)

17.3. Looping and lists 149


Programming in Python, Release 1st Edition

(continued from previous page)


4 def sum1():
5 """ Build a list of random numbers, then sum them """
6 xs = []
7 for i in range(10000000):
8 num = joe.randrange(1000) # Generate one random number
9 xs.append(num) # Save it in our list
10

11 tot = sum(xs)
12 return tot
13

14 def sum2():
15 """ Sum the random numbers as we generate them """
16 tot = 0
17 for i in range(10000000):
18 num = joe.randrange(1000)
19 tot += num
20 return tot
21

22 print(sum1())
23 print(sum2())

What reasons are there for preferring the second version here? (Hint: open a tool like the Performance Monitor on
your computer, and watch the memory usage. How big can you make the list before you get a fatal memory error in
sum1?)
In a similar way, when working with files, we often have an option to read the whole file contents into a single string,
or we can read one line at a time and process each line as we read it. Line-at-a-time is the more traditional and perhaps
safer way to do things — you’ll be able to work comfortably no matter how large the file is. (And, of course, this mode
of processing the files was essential in the old days when computer memories were much smaller.) But you may find
whole-file-at-once is sometimes more convenient!

150 Chapter 17. Some Tips, Tricks, and Common Errors


CHAPTER

EIGHTEEN

GNU FREE DOCUMENTATION LICENSE

Version 1.3, 3 November 2008


Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

18.1 0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other functional and useful document “free” in the sense
of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either
commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit
for their work, while not being considered responsible for modifications made by others.
This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the
same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free
documentation: a free program should come with manuals providing the same freedoms that the software does. But
this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter
or whether it is published as a printed book. We recommend this License principally for works whose purpose is
instruction or reference.

18.2 1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder
saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license,
unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such
manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you
copy, modify or distribute the work in a way requiring permission under copyright law.
A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied
verbatim, or with modifications and/or translated into another language.
A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the
relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and
contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of
mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position
regarding them.

151
Programming in Python, Release 1st Edition

The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sec-
tions, in the notice that says that the Document is released under this License. If a section does not fit the above
definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant
Sections. If the Document does not identify any Invariant Sections then there are none.
The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the
notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a
Back-Cover Text may be at most 25 words.
A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification
is available to the general public, that is suitable for revising the document straightforwardly with generic text editors
or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor,
and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has
been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not
Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”.
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, La-
TeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript
or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque
formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML
for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript
or PDF produced by some word processors for output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold,
legibly, the material this License requires to appear in the title page. For works in formats which do not have any
title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the
beginning of the body of the text.
The “publisher” means any person or entity that distributes copies of the Document to the public.
A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains
XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section
name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve
the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according
to this definition.
The Document may include Warranty Disclaimers next to the notice which states that this License applies to the
Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards
disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on
the meaning of this License.

18.3 2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that
this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced
in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical
measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may
accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow
the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.

152 Chapter 18. GNU Free Documentation License


Programming in Python, Release 1st Edition

18.4 3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering
more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that
carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the
back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover
must present the full title with all words of the title equally prominent and visible. You may add other material on the
covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and
satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as
fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a
machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-
network location from which the general network-using public has access to download using public-standard network
protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must
take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent
copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque
copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large
number of copies, to give them a chance to provide you with an updated version of the Document.

18.5 4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above,
provided that you release the Modified Version under precisely this License, with the Modified Version filling the role
of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of
it. In addition, you must do these things in the Modified Version:
• A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of
previous versions (which should, if there were any, be listed in the History section of the Document). You may
use the same title as a previous version if the original publisher of that version gives permission.
• B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifi-
cations in the Modified Version, together with at least five of the principal authors of the Document (all of its
principal authors, if it has fewer than five), unless they release you from this requirement.
• C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
• D. Preserve all the copyright notices of the Document.
• E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
• F. Include, immediately after the copyright notices, a license notice giving the public permission to use the
Modified Version under the terms of this License, in the form shown in the Addendum below.
• G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the
Document’s license notice.
• H. Include an unaltered copy of this License.
• I. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year,
new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled
“History” in the Document, create one stating the title, year, authors, and publisher of the Document as given
on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.

18.4. 3. COPYING IN QUANTITY 153


Programming in Python, Release 1st Edition

• J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of
the Document, and likewise the network locations given in the Document for previous versions it was based on.
These may be placed in the “History” section. You may omit a network location for a work that was published at
least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
• K. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and pre-
serve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications
given therein.
• L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers
or the equivalent are not considered part of the section titles.
• M. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version.
• N. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant
Section.
• O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and
contain no material copied from the Document, you may at your option designate some or all of these sections as
invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These
titles must be distinct from any other section titles.
You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Ver-
sion by various parties—for example, statements of peer review or that the text has been approved by an organization
as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover
Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one
of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you
are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the
previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity
for or to assert or imply endorsement of any Modified Version.

18.6 5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section
4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the
original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice,
and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be
replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make
the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or
publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list
of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled “History” in the various original documents, forming one
section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled
“Dedications”. You must delete all sections Entitled “Endorsements”.

154 Chapter 18. GNU Free Documentation License


Programming in Python, Release 1st Edition

18.7 6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace
the individual copies of this License in the various documents with a single copy that is included in the collection,
provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided
you insert a copy of this License into the extracted document, and follow this License in all other respects regarding
verbatim copying of that document.

18.8 7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on
a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation
is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the
Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not
themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less
than one half of the entire aggregate, the Document’s Cover Texts may be placed on covers that bracket the Document
within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must
appear on printed covers that bracket the whole aggregate.

18.9 8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms
of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders,
but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant
Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty
Disclaimers, provided that you also include the original English version of this License and the original versions of
those notices and disclaimers. In case of a disagreement between the translation and the original version of this License
or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section
4) to Preserve its Title (section 1) will typically require changing the actual title.

18.10 9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License.
Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights
under this License.
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently,
if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you
of the violation by some reasonable means, this is the first time you have received notice of violation of this License
(for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

18.7. 6. COLLECTIONS OF DOCUMENTS 155


Programming in Python, Release 1st Edition

Termination of your rights under this section does not terminate the licenses of parties who have received copies or
rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a
copy of some or all of the same material does not give you any rights to use it.

18.11 10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time
to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new
problems or concerns. See http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular
numbered version of this License “or any later version” applies to it, you have the option of following the terms and
conditions either of that specified version or of any later version that has been published (not as a draft) by the Free
Software Foundation. If the Document does not specify a version number of this License, you may choose any version
ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide
which future versions of this License can be used, that proxy’s public statement of acceptance of a version permanently
authorizes you to choose that version for the Document.

18.12 11. RELICENSING

“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any World Wide Web server that publishes copy-
rightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody
can edit is an example of such a server. A “Massive Multiauthor Collaboration” (or “MMC”) contained in the site
means any set of copyrightable works thus published on the MMC site.
“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons
Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as
future copyleft versions of that license published by that same organization.
“Incorporate” means to publish or republish a Document, in whole or in part, as part of another Document.
An MMC is “eligible for relicensing” if it is licensed under this License, and if all works that were first published
under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC,
(1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any
time before August 1, 2009, provided the MMC is eligible for relicensing.

18.13 ADDENDUM: How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the
following copyright and license notices just after the title page:

Copyright (C) YEAR YOUR NAME.


Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with . . . Texts.” line with this:

156 Chapter 18. GNU Free Documentation License


Programming in Python, Release 1st Edition

with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alterna-
tives to suit the situation.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel
under your choice of free software license, such as the GNU General Public License, to permit their use in free
software.

18.13. ADDENDUM: How to use this License for your documents 157
Programming in Python, Release 1st Edition

158 Chapter 18. GNU Free Documentation License


CHAPTER

NINETEEN

COPYRIGHT NOTICE

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

159
Programming in Python, Release 1st Edition

160 Chapter 19. Copyright Notice


CHAPTER

TWENTY

FOREWORD

By David Beazley
As an educator, researcher, and book author, I am delighted to see the completion of this book. Python is a fun and
extremely easy-to-use programming language that has steadily gained in popularity over the last few years. Developed
over ten years ago by Guido van Rossum, Python’s simple syntax and overall feel is largely derived from ABC, a
teaching language that was developed in the 1980’s. However, Python was also created to solve real problems and it
borrows a wide variety of features from programming languages such as C++, Java, Modula-3, and Scheme. Because
of this, one of Python’s most remarkable features is its broad appeal to professional software developers, scientists,
researchers, artists, and educators.
Despite Python’s appeal to many different communities, you may still wonder why Python? or why teach programming
with Python? Answering these questions is no simple task—especially when popular opinion is on the side of more
masochistic alternatives such as C++ and Java. However, I think the most direct answer is that programming in Python
is simply a lot of fun and more productive.
When I teach computer science courses, I want to cover important concepts in addition to making the material inter-
esting and engaging to students. Unfortunately, there is a tendency for introductory programming courses to focus far
too much attention on mathematical abstraction and for students to become frustrated with annoying problems related
to low-level details of syntax, compilation, and the enforcement of seemingly arcane rules. Although such abstraction
and formalism is important to professional software engineers and students who plan to continue their study of com-
puter science, taking such an approach in an introductory course mostly succeeds in making computer science boring.
When I teach a course, I don’t want to have a room of uninspired students. I would much rather see them trying
to solve interesting problems by exploring different ideas, taking unconventional approaches, breaking the rules, and
learning from their mistakes. In doing so, I don’t want to waste half of the semester trying to sort out obscure syntax
problems, unintelligible compiler error messages, or the several hundred ways that a program might generate a general
protection fault.
One of the reasons why I like Python is that it provides a really nice balance between the practical and the conceptual.
Since Python is interpreted, beginners can pick up the language and start doing neat things almost immediately without
getting lost in the problems of compilation and linking. Furthermore, Python comes with a large library of modules
that can be used to do all sorts of tasks ranging from web-programming to graphics. Having such a practical focus is
a great way to engage students and it allows them to complete significant projects. However, Python can also serve as
an excellent foundation for introducing important computer science concepts. Since Python fully supports procedures
and classes, students can be gradually introduced to topics such as procedural abstraction, data structures, and object-
oriented programming — all of which are applicable to later courses on Java or C++. Python even borrows a number
of features from functional programming languages and can be used to introduce concepts that would be covered in
more detail in courses on Scheme and Lisp.
In reading Jeffrey’s preface, I am struck by his comments that Python allowed him to see a higher level of success and a
lower level of frustration and that he was able to move faster with better results. Although these comments refer to his
introductory course, I sometimes use Python for these exact same reasons in advanced graduate level computer science
courses at the University of Chicago. In these courses, I am constantly faced with the daunting task of covering a lot
of difficult course material in a blistering nine week quarter. Although it is certainly possible for me to inflict a lot of

161
Programming in Python, Release 1st Edition

pain and suffering by using a language like C++, I have often found this approach to be counterproductive—especially
when the course is about a topic unrelated to just programming. I find that using Python allows me to better focus on
the actual topic at hand while allowing students to complete substantial class projects.
Although Python is still a young and evolving language, I believe that it has a bright future in education. This book is
an important step in that direction. David Beazley University of Chicago Author of the Python Essential Reference

162 Chapter 20. Foreword


CHAPTER

TWENTYONE

PREFACE

By Jeffrey Elkner
This book owes its existence to the collaboration made possible by the Internet and the free software movement. Its
three authors—a college professor, a high school teacher, and a professional programmer—never met face to face to
work on it, but we have been able to collaborate closely, aided by many other folks who have taken the time and energy
to send us their feedback.
We think this book is a testament to the benefits and future possibilities of this kind of collaboration, the framework
for which has been put in place by Richard Stallman and the Free Software Foundation.

21.1 How and why I came to use Python

In 1999, the College Board’s Advanced Placement (AP) Computer Science exam was given in C++ for the first time.
As in many high schools throughout the country, the decision to change languages had a direct impact on the computer
science curriculum at Yorktown High School in Arlington, Virginia, where I teach. Up to this point, Pascal was the
language of instruction in both our first-year and AP courses. In keeping with past practice of giving students two
years of exposure to the same language, we made the decision to switch to C++ in the first year course for the 1997-98
school year so that we would be in step with the College Board’s change for the AP course the following year.
Two years later, I was convinced that C++ was a poor choice to use for introducing students to computer science.
While it is certainly a very powerful programming language, it is also an extremely difficult language to learn and
teach. I found myself constantly fighting with C++’s difficult syntax and multiple ways of doing things, and I was
losing many students unnecessarily as a result. Convinced there had to be a better language choice for our first-year
class, I went looking for an alternative to C++.
I needed a language that would run on the machines in our GNU/Linux lab as well as on the Windows and Macintosh
platforms most students have at home. I wanted it to be free software, so that students could use it at home regardless
of their income. I wanted a language that was used by professional programmers, and one that had an active developer
community around it. It had to support both procedural and object-oriented programming. And most importantly, it
had to be easy to learn and teach. When I investigated the choices with these goals in mind, Python stood out as the
best candidate for the job.
I asked one of Yorktown’s talented students, Matt Ahrens, to give Python a try. In two months he not only learned the
language but wrote an application called pyTicket that enabled our staff to report technology problems via the Web. I
knew that Matt could not have finished an application of that scale in so short a time in C++, and this accomplishment,
combined with Matt’s positive assessment of Python, suggested that Python was the solution I was looking for.

163
Programming in Python, Release 1st Edition

21.2 Finding a textbook

Having decided to use Python in both of my introductory computer science classes the following year, the most
pressing problem was the lack of an available textbook.
Free documents came to the rescue. Earlier in the year, Richard Stallman had introduced me to Allen Downey. Both
of us had written to Richard expressing an interest in developing free educational materials. Allen had already written
a first-year computer science textbook, How to Think Like a Computer Scientist. When I read this book, I knew
immediately that I wanted to use it in my class. It was the clearest and most helpful computer science text I had seen.
It emphasized the processes of thought involved in programming rather than the features of a particular language.
Reading it immediately made me a better teacher.
How to Think Like a Computer Scientist was not just an excellent book, but it had been released under the GNU public
license, which meant it could be used freely and modified to meet the needs of its user. Once I decided to use Python,
it occurred to me that I could translate Allen’s original Java version of the book into the new language. While I would
not have been able to write a textbook on my own, having Allen’s book to work from made it possible for me to do so,
at the same time demonstrating that the cooperative development model used so well in software could also work for
educational materials.
Working on this book for the last two years has been rewarding for both my students and me, and my students played
a big part in the process. Since I could make instant changes whenever someone found a spelling error or difficult
passage, I encouraged them to look for mistakes in the book by giving them a bonus point each time they made a
suggestion that resulted in a change in the text. This had the double benefit of encouraging them to read the text more
carefully and of getting the text thoroughly reviewed by its most important critics, students using it to learn computer
science.
For the second half of the book on object-oriented programming, I knew that someone with more real programming
experience than I had would be needed to do it right. The book sat in an unfinished state for the better part of a year
until the open source community once again provided the needed means for its completion.
I received an email from Chris Meyers expressing interest in the book. Chris is a professional programmer who started
teaching a programming course last year using Python at Lane Community College in Eugene, Oregon. The prospect
of teaching the course had led Chris to the book, and he started helping out with it immediately. By the end of the
school year he had created a companion project on our Website at http://openbookproject.net called *Python for Fun*
and was working with some of my most advanced students as a master teacher, guiding them beyond where I could
take them.

21.3 Introducing programming with Python

The process of translating and using How to Think Like a Computer Scientist for the past two years has confirmed
Python’s suitability for teaching beginning students. Python greatly simplifies programming examples and makes
important programming ideas easier to teach.
The first example from the text illustrates this point. It is the traditional hello, world program, which in the Java version
of the book looks like this:

class Hello {

public static void main (String[] args) {


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

in the Python version it becomes:

164 Chapter 21. Preface


Programming in Python, Release 1st Edition

print("Hello, World!")

Even though this is a trivial example, the advantages of Python stand out. Yorktown’s Computer Science I course
has no prerequisites, so many of the students seeing this example are looking at their first program. Some of them
are undoubtedly a little nervous, having heard that computer programming is difficult to learn. The Java version has
always forced me to choose between two unsatisfying options: either to explain the class Hello, public static void
main, String[] args, {, and }, statements and risk confusing or intimidating some of the students right at the start, or
to tell them, Just don’t worry about all of that stuff now; we will talk about it later, and risk the same thing. The
educational objectives at this point in the course are to introduce students to the idea of a programming statement
and to get them to write their first program, thereby introducing them to the programming environment. The Python
program has exactly what is needed to do these things, and nothing more.
Comparing the explanatory text of the program in each version of the book further illustrates what this means to the
beginning student. There are seven paragraphs of explanation of Hello, world! in the Java version; in the Python
version, there are only a few sentences. More importantly, the missing six paragraphs do not deal with the big ideas in
computer programming but with the minutia of Java syntax. I found this same thing happening throughout the book.
Whole paragraphs simply disappear from the Python version of the text because Python’s much clearer syntax renders
them unnecessary.
Using a very high-level language like Python allows a teacher to postpone talking about low-level details of the
machine until students have the background that they need to better make sense of the details. It thus creates the
ability to put first things first pedagogically. One of the best examples of this is the way in which Python handles
variables. In Java a variable is a name for a place that holds a value if it is a built-in type, and a reference to an object if
it is not. Explaining this distinction requires a discussion of how the computer stores data. Thus, the idea of a variable
is bound up with the hardware of the machine. The powerful and fundamental concept of a variable is already difficult
enough for beginning students (in both computer science and algebra). Bytes and addresses do not help the matter.
In Python a variable is a name that refers to a thing. This is a far more intuitive concept for beginning students and
is much closer to the meaning of variable that they learned in their math courses. I had much less difficulty teaching
variables this year than I did in the past, and I spent less time helping students with problems using them.
Another example of how Python aids in the teaching and learning of programming is in its syntax for functions. My
students have always had a great deal of difficulty understanding functions. The main problem centers around the
difference between a function definition and a function call, and the related distinction between a parameter and an
argument. Python comes to the rescue with syntax that is nothing short of beautiful. Function definitions begin with
the keyword def, so I simply tell my students, When you define a function, begin with def, followed by the name
of the function that you are defining; when you call a function, simply call (type) out its name. Parameters go with
definitions; arguments go with calls. There are no return types, parameter types, or reference and value parameters to
get in the way, so I am now able to teach functions in less than half the time that it previously took me, with better
comprehension.
Using Python improved the effectiveness of our computer science program for all students. I saw a higher general
level of success and a lower level of frustration than I experienced teaching with either C++ or Java. I moved faster
with better results. More students left the course with the ability to create meaningful programs and with the positive
attitude toward the experience of programming that this engenders.

21.4 Building a community

I have received email from all over the globe from people using this book to learn or to teach programming. A user
community has begun to emerge, and many people have been contributing to the project by sending in materials for
the companion Website at http://openbookproject.net/pybiblio.
With the continued growth of Python, I expect the growth in the user community to continue and accelerate. The
emergence of this user community and the possibility it suggests for similar collaboration among educators have been
the most exciting parts of working on this project for me. By working together, we can increase the quality of materials

21.4. Building a community 165


Programming in Python, Release 1st Edition

available for our use and save valuable time. I invite you to join our community and look forward to hearing from you.
Please write to me at [email protected].

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

166 Chapter 21. Preface


CHAPTER

TWENTYTWO

THE RHODES LOCAL EDITION (RLE) (VERSION OF AUGUST, 2012)

By Peter Wentworth

A word of thanks . . .
We switched from Java to Python in our introductory courses in 2010. So far we think the results look positive. More
time will tell.
This predecessor to this book was a great starting point for us, especially because of the liberal permission to change
things. Having our own in-house course notes or handouts allows us to adapt and stay fresh, rearrange, see what works,
and it gives us agility. We can also ensure that every student in the course gets a copy of the handouts — something
that doesn’t always happen if we prescribe costly textbooks.
Many thanks to all the contributors and the authors for making their hard work available to the Python community and
to our students.

A colleague and friend, Peter Warren, once made the remark that learning introductory programming is as much about
the environment as it is about the programming language.
I’m a big fan of IDEs (Integrated Development Environments). I want help to be integrated into my editor, as a first-
class citizen, available at the press of a button. I want syntax highlighting. I want immediate syntax checking, and
sensible autocompletion. I’d like an editor that can fold function bodies or regions of code away, because it promotes
and encourages how we build mental abstractions.
I’m especially keen on having a single-stepping debugger and breakpoints with code inspection built in. We’re trying
to build a conceptual model of program execution in the student’s mind, so I find most helpful for teaching to have the
call stack and variables explicitly visible, and to be able to immediately inspect the result of executing a statement.
My philosophy, then, is not to look for a language to teach, but to look for a combination of IDE and language that are
packaged together, and evaluated as a whole.
I’ve made some quite deep changes to the original book to reflect this (and various other opinionated views that I hold),
and I have no doubt that more changes will follow as we mature our course.
Here are some of the key things I’ve approached differently:
• Our local situation demands that we have a large number of service course students in an introductory course of
just 3 weeks, and then we get another semester of teaching with those going into our mainstream program. So
the book is in two parts: we’ll do the first five chapters in the big “get your toes wet” course, and the rest of the
material in a separate semester.
• We’re using Python 3. It is cleaner, more object oriented, and has fewer ad-hoc irregularities than earlier versions
of Python.
• We’re using PyScripter as our IDE, on Windows. And it is hardwired into parts of these notes, with screenshots,
etc.

167
Programming in Python, Release 1st Edition

• I’ve dropped GASP.


• For graphics we start with the Turtle module. As things move along, we use PyGame for more advanced
graphics.
• I’ve introduced some event-driven programming using the turtle.
• I have tried to push more object-oriented notions earlier, without asking students to synthesize objects or write
their own classes. So, for example, in the chapter about the turtle, we create multiple instances of turtles, talk
about their attributes and state (color, position, etc), and we favour method-call style to move them around, i.e.
tess.forward(100). Similarly, when we use random numbers, we avoid the “hidden singleton generator”
in the random module — we prefer to create an instance of a generator, and invoke methods on the instance.
• The ease of constructing lists and the for loop seem to be winners in Python, so rather than use the traditional
command-line input for data, I’ve favoured using loops and lists right up front, like this:

1 friends = ["Zoe", "Joe", "Bill"]


2 for f in friends:
3 invitation = "Hi " + f + ". Please come to my party on Saturday!"
4 print(invitation)

This also means that I bumped range up for early exposure. I envisage that over time we’ll see more opportu-
nities to exploit “early lists, early iteration” in its most simple form.
• I dumped doctest: it is too quirky for my liking. For example, it fails a test if the spacing between list
elements is not precisely the same as the output string, or if Python prints a string with single quotes, but you
wrote up the test case with double quotes. Cases like this also confused students (and instructors) quite badly:

1 def addlist(xs):
2 """
3 >>> xs = [2,3,4]
4 >>> addlist(xs)
5 9
6 """
7 return

If you can explain the difference in scope rules and lifetimes between the parameter xs and the doctest variable
xs elegantly, please let me know. Yes, I know doctest creates its own scope behind our back, but it is exactly
this black magic that we’re trying to avoid. From the usual indentation rules, also looks like the doctests are
nested inside the function scope, but they are not. Students thought that the parameter had been given its value
by the assignment to xs in the doctest!
I also think that keeping the test suite separate from the functions under test leads to a cleaner relationship
between caller and callee, and gives a better chance of getting argument passing / parameter concepts taught
accurately.
There is a good unit testing module in Python, (and PyScripter offers integrated support for it, and automated
generation of skeleton test modules), but it looked too advanced for beginners, because it requires multi-module
concepts.
So I’ve favoured my own test scaffolding in Chapter 6 (about 10 lines of code) that the students must insert into
whatever file they’re working on.
• I’ve played down command-line input / process / output where possible. Many of our students have never seen
a command-line shell, and it is arguably quite intimidating.
• We’ve gone back to a more “classic / static” approach to writing our own classes and objects. Python (in
company with languages like Javascript, Ruby, Perl, PHP, etc.) don’t really emphasize notions of “sealed”
classes or “private” members, or even “sealed instances”.

168 Chapter 22. The Rhodes Local Edition (RLE) (Version of August, 2012)
Programming in Python, Release 1st Edition

So one teaching approach is to allocate each instance as an empty container, and subsequently allow the external
clients of the class to poke new members (methods or attributes) into different instances as they wish to. It
is a very dynamic approach, but perhaps not one that encourages thinking in abstractions, layers, contracts,
decoupling, etc. It might even be the kind of thing that one could write one of those “x,y,z . . . considered
harmful” papers about.
In our more conservative approach, we put an initializer into every class, we determine at object instantiation
time what members we want, and we initialize the instances from within the class. So we’ve moved closer in
philosophy to C# / Java on this one.
• We’re moving towards introducing more algorithms earlier into the course. Python is an efficient teaching
language — we can make fast progress. But the gains we make there we’d like to invest into deeper problem
solving, and more complex algorithms with the basics, rather than cover “more Python features”. Some of these
changes have started to find their way in this version, and I’m sure we’ll see more in future.
• We’re interested in issues around teaching and learning. Some research indicates that “intellectual playfulness”
is important. The study referenced in the Odds-and-ends workbook at the end just didn’t seem to have anywhere
sensible to go in the book, yet I wanted it included. It is quite likely that we’ll allow more issues like this to
creep into the book, to try to make it more than just about programming in Python.

169
Programming in Python, Release 1st Edition

170 Chapter 22. The Rhodes Local Edition (RLE) (Version of August, 2012)
CHAPTER

TWENTYTHREE

CONTRIBUTOR LIST

To paraphrase the philosophy of the Free Software Foundation, this book is free like free speech, but not necessarily
free like free pizza. It came about because of a collaboration that would not have been possible without the GNU Free
Documentation License. So we would like to thank the Free Software Foundation for developing this license and, of
course, making it available to us.
We would also like to thank the more than 100 sharp-eyed and thoughtful readers who have sent us suggestions and
corrections over the past few years. In the spirit of free software, we decided to express our gratitude in the form of
a contributor list. Unfortunately, this list is not complete, but we are doing our best to keep it up to date. It was also
getting too large to include everyone who sends in a typo or two. You have our gratitude, and you have the personal
satisfaction of making a book you found useful better for you and everyone else who uses it. New additions to the list
for the 2nd edition will be those who have made on-going contributions.
If you have a chance to look through the list, you should realize that each person here has spared you and all subsequent
readers from the confusion of a technical error or a less-than-transparent explanation, just by sending us a note.
Impossible as it may seem after so many corrections, there may still be errors in this book. If you should stumble
across one, we hope you will take a minute to contact us. The email address (for the Python 3 version of the book)
is [email protected] . Substantial changes made due to your suggestions will add you to the next version of the
contributor list (unless you ask to be omitted). Thank you!

23.1 Second Edition

• An email from Mike MacHenry set me straight on tail recursion. He not only pointed out an error in the
presentation, but suggested how to correct it.
• It wasn’t until 5th Grade student Owen Davies came to me in a Saturday morning Python enrichment class and
said he wanted to write the card game, Gin Rummy, in Python that I finally knew what I wanted to use as the
case study for the object oriented programming chapters.
• A special thanks to pioneering students in Jeff’s Python Programming class at GCTAA during the 2009-2010
school year: Safath Ahmed, Howard Batiste, Louis Elkner-Alfaro, and Rachel Hancock. Your continual and
thoughtfull feedback led to changes in most of the chapters of the book. You set the standard for the active and
engaged learners that will help make the new Governor’s Academy what it is to become. Thanks to you this is
truly a student tested text.
• Thanks in a similar vein to the students in Jeff’s Computer Science class at the HB-Woodlawn program during
the 2007-2008 school year: James Crowley, Joshua Eddy, Eric Larson, Brian McGrail, and Iliana Vazuka.
• Ammar Nabulsi sent in numerous corrections from Chapters 1 and 2.
• Aldric Giacomoni pointed out an error in our definition of the Fibonacci sequence in Chapter 5.
• Roger Sperberg sent in several spelling corrections and pointed out a twisted piece of logic in Chapter 3.

171
Programming in Python, Release 1st Edition

• Adele Goldberg sat down with Jeff at PyCon 2007 and gave him a list of suggestions and corrections from
throughout the book.
• Ben Bruno sent in corrections for chapters 4, 5, 6, and 7.
• Carl LaCombe pointed out that we incorrectly used the term commutative in chapter 6 where symmetric was the
correct term.
• Alessandro Montanile sent in corrections for errors in the code examples and text in chapters 3, 12, 15, 17, 18,
19, and 20.
• Emanuele Rusconi found errors in chapters 4, 8, and 15.
• Michael Vogt reported an indentation error in an example in chapter 6, and sent in a suggestion for improving
the clarity of the shell vs. script section in chapter 1.

23.2 First Edition

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


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

172 Chapter 23. Contributor List


Programming in Python, Release 1st Edition

• Robin Shaw pointed out an error in Section 13.1, where the printTime function was used in an example without
being defined.
• Paul Sleigh found an error in Chapter 7 and a bug in Jonah Cohen’s Perl script that generates HTML from
LaTeX.
• Craig T. Snydal is testing the text in a course at Drew University. He has contributed several valuable suggestions
and corrections.
• Ian Thomas and his students are using the text in a programming course. They are the first ones to test the
chapters in the latter half of the book, and they have make numerous corrections and suggestions.
• Keith Verheyden sent in a correction in Chapter 3.
• Peter Winstanley let us know about a longstanding error in our Latin in Chapter 3.
• Chris Wrobel made corrections to the code in the chapter on file I/O and exceptions.
• Moshe Zadka has made invaluable contributions to this project. In addition to writing the first draft of the chapter
on Dictionaries, he provided continual guidance in the early stages of the book.
• Christoph Zwerschke sent several corrections and pedagogic suggestions, and explained the difference between
gleich and selbe.
• James Mayer sent us a whole slew of spelling and typographical errors, including two in the contributor list.
• Hayden McAfee caught a potentially confusing inconsistency between two examples.
• Angel Arnal is part of an international team of translators working on the Spanish version of the text. He has
also found several errors in the English version.
• Tauhidul Hoque and Lex Berezhny created the illustrations in Chapter 1 and improved many of the other illus-
trations.
• Dr. Michele Alzetta caught an error in Chapter 8 and sent some interesting pedagogic comments and suggestions
about Fibonacci and Old Maid.
• Andy Mitchell caught a typo in Chapter 1 and a broken example in Chapter 2.
• Kalin Harvey suggested a clarification in Chapter 7 and caught some typos.
• Christopher P. Smith caught several typos and is helping us prepare to update the book for Python 2.2.
• David Hutchins caught a typo in the Foreword.
• Gregor Lingl is teaching Python at a high school in Vienna, Austria. He is working on a German translation of
the book, and he caught a couple of bad errors in Chapter 5.
• Julie Peters caught a typo in the Preface.
• search

23.2. First Edition 173


Programming in Python, Release 1st Edition

174 Chapter 23. Contributor List


INDEX

A composition of functions, 20
abbreviated assignment, 54 compound data type, 73, 82
abecedarian series, 74 compound statement, 41, 70
algorithm, 6 body, 41
deterministic, 132 header, 41
aliases, 96, 106, 125 computation pattern, 77
alternative execution, 42 concatenate, 25
ambiguity, 8 concatenation, 19, 74
argument, 70 condition, 47, 51
assignment, 12, 49 conditional
tuple, 120 chained, 43
assignment statement, 12, 25, 49 conditional branching, 41
assignment token, 25 conditional execution, 41
attribute, 27, 36 conditional statement, 41, 47
conditionals
B nested, 44
base case, 112, 113 continue statement, 57, 58
block, 41, 47 control flow, 31, 36, 146
body, 41, 47, 58, 70 counter, 58
Boolean algebra, 47
Boolean expression, 39, 47
D
Boolean function, 67 data structure, 112, 121
Boolean value, 39, 47 recursive, 112
branch, 42, 47 data type, 11, 25
break statement, 54 decrement, 58
breakpoint, 58 default value, 78, 82
bump, 58 definite iteration, 58
definition
C function, 27, 61
recursive, 112
call graph, 129
del statement, 94, 124
canvas, 27, 36
delimiter, 88, 106
chained conditional, 43, 47
deterministic algorithm, 132
character, 73
dictionary, 123, 129
chunking, 32
dir function, 80
class, 11
directory, 88, 89
clone, 96, 106
docstring, 70, 80, 82
Collatz 3n + 1 sequence, 51
dot notation, 80, 82
collection, 89
comment, 7 E
comparison of strings, 76
comparison operator, 39, 47 element, 89, 106
composition, 25 elif, 41

175
Programming in Python, Release 1st Edition

else, 41 input dialog, 20


enumerate, 97 instance, 29, 36
eureka traversal, 77 int, 11, 17, 25
evaluate, 25 integer, 11
event, 148 invoke, 27, 37
expression, 16, 25 is operator, 95
Boolean, 39 item, 89, 107
item assignment, 93
F iteration, 49, 51, 59
fibonacci numbers, 113
field width, 81 J
file, 85, 89 join, 101
text, 87 justification, 81
file handle, 85
file system, 89 K
float, 11, 17, 25 key, 123, 129
floor division, 16, 25 key:value pair, 123, 129
flow of execution, 31, 64, 70 keyword, 15, 25
for loop, 31, 36, 50, 74, 97
for loop traversal (for), 82 L
formatting len function, 74
strings, 81 length function (len), 82
frame, 70 lifetime, 66, 70
fruitful function, 70 list, 89, 107
function, 27, 58, 61, 70 append, 98
len, 74 nested, 91, 104
function call, 70 list index, 91
function composition, 20, 70 list traversal, 91, 107
function definition, 27, 61, 70 literalness, 8
function tips, 145 local variable, 66, 70
function type, 80 logical operator, 39, 40, 47
loop, 51, 59
H loop body, 36, 51
handle, 85, 89 loop variable, 36, 59
handler, 148
header line, 70 M
Make Way for Ducklings, 74
I mapping type, 123, 129
if, 41 matrix, 105, 126
if statement, 41 McCloskey, Robert, 74
immutable, 76, 93, 119 memo, 127, 129
immutable data value, 82, 106, 121, 129 method, 27, 36
import statement, 70, 134 middle-test loop, 59
in and not in operator (in, not in), 82 mode, 89
in operator, 77 modifier, 100, 107
increment, 58 module, 27, 37, 80
indefinite iteration, 58 modulus operator, 21, 25
index, 73, 82, 91, 107 mutable, 76, 93, 119
negative, 74 mutable data value, 82, 107, 122, 129
indexing ([]), 82
infinite loop, 51, 58 N
infinite recursion, 112, 114 negative index, 74
initialization (of a variable), 58 nested conditionals, 44
input, 20

176 Index
Programming in Python, Release 1st Edition

nested list, 91, 104, 107 short-circuit evaluation, 77, 82


nested loop, 59 shuffle, 131
nesting, 47 side effect, 100, 107
newline, 59 slice, 75, 82, 93
non-volatile memory, 89 slicing ([:]), 82
None, 145 split, 101
stack diagram, 71
O state, 27
object, 27, 37, 107 state snapshot, 12
objects and values, 95 state snapshot or state frame, 25
operand, 16, 25 statement, 16, 25
operations on strings, 81 assignment, 49
operator, 16, 25 continue, 57
comparison, 39 del, 94, 124
in, 77 if, 41
logical, 39, 40 import, 134
modulus, 21 pass, 41
optional parameter, 78, 82 statement block, 41
order of operations, 18 statement: break, 54
step size, 107
P str, 17, 25
parameter, 71, 97 string, 11, 148
optional, 78 string comparison, 76
pass statement, 41 string comparison (>, <, >=, <=, ==, !=), 82
path, 89 string formatting, 81
pattern, 107 string module, 80
pattern of computation, 77 string operations, 19
poetry, 8 string slice, 75
post-test loop, 59 strings and lists, 101
pre-test loop, 59 sublist, 75, 93
program, 6, 8 subscript operator, 73
promise, 107, 131 substring, 75
prompt, 47
prose, 8 T
pure function, 107 terminating condition, 37
text file, 87, 89
R traceback, 71
random numbers, 131 traversal, 74, 77
range, 37 traverse, 82
range function, 32 triple quoted string, 11
recursion, 112, 114 truncation, 17
infinite, 112 truth table, 47
recursive call, 112, 114 tuple, 119, 122
recursive data structure, 112 assignment, 120
recursive definition, 112, 114 return value, 120
redundancy, 8 tuple assignment, 122
return, 145 turtle module, 27
return a tuple, 120 type, 11
rules of precedence, 18, 25 type conversion, 47
runtime error, 74, 76 type converter functions, 17

S U
sequence, 89, 107 underscore character, 15
unit tests, 132

Index 177
Programming in Python, Release 1st Edition

V
value, 11, 25, 123
Boolean, 39
variable, 12, 25
local, 66
variable name, 26
variables local, 147
void function, 71
volatile memory, 89

W
while loop, 51
while statement, 51
whitespace, 82
wrapping code in a function, 42

178 Index

You might also like