Programming in Python IAP
Programming in Python IAP
1 Preface (UHLE) 3
5 Conditionals 39
6 Iteration 49
7 Functions 61
8 Strings 73
9 Files 85
10 Lists 91
11 Recursion 109
13 Tuples 119
14 Dictionaries 123
15 Modules 131
16 Debugging 137
20 Foreword 161
21 Preface 163
i
23 Contributor List 171
Index 175
ii
Programming in Python, Release 1st Edition
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
TWO
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.
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.
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
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!
Natural languages are the languages that people speak, such as English, Spanish, and French. They were not designed
by people (although people try to impose some order on them); they evolved naturally.
Formal languages are languages that are designed by people for specific applications. For example, the notation
that mathematicians use is a formal language that is particularly good at denoting relationships among numbers and
symbols. Chemists use a formal language to represent the chemical structure of molecules. And most importantly:
Programming languages are formal languages that have been designed to express computations.
Formal languages tend to have strict rules about syntax. For example, 3+3=6 is a syntactically correct mathematical
statement, but 3=+6$ is not. 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.
We try to be explicit about syntax and semantics. Usually we employ boxes like the one below:
Semantics Prints the strings STRING1 and STRING2 separated by a space to the output and moves the
output to the next line.
THREE
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.
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 """)
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:
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.
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:
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
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!)
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:
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
day = 21
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
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;
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 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):
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.
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.
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
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:
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.
>>> 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:
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.
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)
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
>>> str(17)
'17'
>>> str(123.45)
'123.45'
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:
The immediate mode command prompt of Python is great for exploring and experimenting with expressions like this.
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):
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:
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.
The len function, when applied to a string, returns the number of characters in a string:
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"
3.10 Input
There is a built-in function in Python for getting input from the user:
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.
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
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.
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.
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.
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
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.
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)
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.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
produces as output
HellohiMr. Robot
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.
FOUR
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.
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).
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.
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
31 wn.mainloop()
Here is what happens when alex completes his rectangle, and tess completes her triangle:
• 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.
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 ...
• 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!
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.
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:
• 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]
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?
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:
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="")
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)
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!)
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
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).
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
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.
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:
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:
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'>
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.
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
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 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
not (not x) == x
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.
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.
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
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
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.
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
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
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:
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:
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:
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 <.
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:
it would probably be clearer to use the simplification laws, and to write instead:
Two powerful simplification laws (called de Morgan’s laws) that are often helpful when dealing with complicated
Boolean expressions are:
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:
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:
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:
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
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)
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.
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.
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:
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)
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.
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!
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)
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
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:
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:
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:
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
6.10 An example
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
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.
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
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.
while boolean_condition:
while_body
6.13 Glossary
58 Chapter 6. Iteration
Programming in Python, Release 1st Edition
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:
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
61
Programming in Python, Release 1st Edition
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!
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
23 wn.mainloop()
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.
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-
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.
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
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:
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:
1 def circle_area(r):
2 """
3 Apply the area formula for a circle of
4 radius r.
5 """
6
• 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 .
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
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 ""
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.
Functions can return Boolean values, which is often convenient for hiding complicated tests inside functions. For
example:
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:
>>> is_divisible(6, 4)
False
>>> is_divisible(6, 3)
True
1 if is_divisible(x, y):
2 ... # Do something ...
3 else:
4 ... # Do something else ...
1 if is_divisible(x, y) == True:
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)
1 def f(x):
2 x = 2
3 print(x)
4
5 x = 1
6 f(x)
7 print(x)
Semantics A function with name func_name and parameters param1 , param2, . . . is defined. Here,
param1, param2, . . . are called formal parameters.
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.
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
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.
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:
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'
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.
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)
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:
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:
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:
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:
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.
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:
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
The solution here is to concatenate a new first letter onto a slice of greeting. This operation has no effect on the
original string.
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.
Note that a string is a substring of itself, and the empty string is a substring of any other string.
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
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:
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:
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:
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.":
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).
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:
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.
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.
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.
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
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:
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
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.
Let’s begin with a simple program that writes three lines of text into a file:
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
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:
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:
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="")
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.
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 #:
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:
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.
There are several ways to create a new list; the simplest is to enclose the elements in square brackets ([ and ]):
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:
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
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
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:
3 for h in horsemen:
4 print(h)
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:
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:
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:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> c
[1, 2, 3, 4, 5, 6]
>>> [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.
The slice operations we saw previously with strings, also work with lists:
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:
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:
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:
Furthermore, we can add elements to a list by squeezing them into an empty slice at the desired location:
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:
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:
As usual, the sublist selected by slice contains all the elements up to, but not including, the second index.
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
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
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.
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:
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]
The for loop also works with lists, as we’ve already seen. The generalized syntax of a for loop is:
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:
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
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:
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
1 things = [2, 5, 9]
2 double_stuff(things)
3 print(things)
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.
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:
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.
Using lists as default arguments can lead to unexpected behavior. Consider the following program:
my_list = append_to(12)
print(my_list)
my_other_list = append_to(42)
print(my_other_list)
[12]
[42]
but instead it is
[12]
[12,42]
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
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:
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:
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
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:
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...']
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:
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)
[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)
[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]
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)
[1, 7]
Semantics For every value in list assigned to var the expression item_expr is evaluated to generate an item in
the list.
[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:
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.
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)
def filter_out_even_numbers(A):
return [x for x in A if x % 2 != 0]
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:
>>> print(nested[3])
[10, 20]
To extract an element from the nested list, we can proceed in two steps:
>>> 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)
10.22 Matrices
Nested lists are often used to represent matrices. For example, the matrix:
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:
>>> 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))]
10.23 Glossary
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.
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.
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:
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:
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:
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.
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.
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!
1 def fac_iter(n):
2 res = 1
3 i = 1
4 while i <= n:
5 res *= i
6 i+=1
7 return res
1 def fac_rec(n):
2 if n == 0 or n == 1:
3 return 1
4 else:
5 return n * fac_rec(n-1)
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.
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:
For our nested number list, however, sum will not work:
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
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.
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
1 def fib(n):
2 if n <= 1:
3 return n
4 t = fib(n-1) + fib(n-2)
5 return t
1 import time
2 t0 = time.clock()
3 n = 35
4 result = fib(n)
5 t1 = time.clock()
6
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.
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.
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):
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.
115
Programming in Python, Release 1st Edition
import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*
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
THIRTEEN
TUPLES
We can group together pairs of values by surrounding them with parentheses. Consider the example:
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:
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:
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:
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
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.
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:
In tuple unpacking, the values in a tuple on the right are ‘unpacked’ into the variables/names on the right:
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
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:
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)
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:
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.
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.
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 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}
The len function also works on dictionaries; it returns the number of key:value pairs:
>>> len(inventory)
4
4 ks = list(eng2sp.keys())
5 print(ks)
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:
This produces:
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'
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:
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:
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:
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:
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:
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:
The first argument is the key; the second argument is the value get should return if the key is not in the dictionary:
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
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
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:
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.
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.
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!
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
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:
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.)
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!)
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
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.
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
19 xs = make_random_ints_no_dups(5, 1, 10000000)
20 print(xs)
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)
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)
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.
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:
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:
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:
But hey, with nice editors that do auto-completion, and fast fingers, that’s a small price!
Finally, observe this case:
1 def area(radius):
2 import math
3 return math.pi * radius * radius
4
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.
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.
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. . .
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.
Once your program is syntactically correct, Python can import it and at least start running it. What could possibly go
wrong?
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.
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.
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.
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:
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.
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!
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.
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?
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.
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”.
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.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())
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()
count = self.hands[i].remove_matches()
return count
Now you have the opportunity to display or inspect the value of count before returning.
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.
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.
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.
SEVENTEEN
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.
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.
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.
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.
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:
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.
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.
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.
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
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.
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.
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
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.)
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.
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
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!
EIGHTEEN
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.
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.
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.
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.
• 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.
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”.
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.
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.
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.
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.
“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.
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:
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with . . . Texts.” line with this:
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
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
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
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.
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
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.
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 {
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.
I have received email from all over the globe from people using this book to learn or to teach programming. A user
community has begun to emerge, and many people have been contributing to the project by sending in materials for
the companion Website at http://openbookproject.net/pybiblio.
With the continued growth of Python, I expect the growth in the user community to continue and accelerate. The
emergence of this user community and the possibility it suggests for similar collaboration among educators have been
the most exciting parts of working on this project for me. By working together, we can increase the quality of materials
available for our use and save valuable time. I invite you to join our community and look forward to hearing from you.
Please write to me at [email protected].
Jeffrey Elkner
Governor’s Career and Technical Academy in Arlington
Arlington, Virginia
TWENTYTWO
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
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!
• 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.
• 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
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
176 Index
Programming in Python, Release 1st Edition
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