Data_Structures_and_Algorithms_in_Python_----_(1_Python_Primer)
Data_Structures_and_Algorithms_in_Python_----_(1_Python_Primer)
Contents
1 Python Primer
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
2 Chapter 1. Python Primer
file (e.g., demo.py) are executed by invoking the interpreter with the filename as
an argument (e.g., python demo.py), or using an additional -i flag in order to
execute a script and then enter interactive mode (e.g., python -i demo.py).
Many integrated development environments (IDEs) provide richer software
development platforms for Python, including one named IDLE that is included
with the standard Python distribution. IDLE provides an embedded text-editor with
support for displaying and editing Python code, and a basic debugger, allowing
step-by-step execution of a program while examining key variable values.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.1. Python Overview 3
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
4 Chapter 1. Python Primer
Figure 1.1: The identifier temperature references an instance of the float class
having value 98.6.
Identifiers
Identifiers in Python are case-sensitive, so temperature and Temperature are dis-
tinct names. Identifiers can be composed of almost any combination of letters,
numerals, and underscore characters (or more general Unicode characters). The
primary restrictions are that an identifier cannot begin with a numeral (thus 9lives
is an illegal name), and that there are 33 specially reserved words that cannot be
used as identifiers, as shown in Table 1.1.
Copyright © 2013. Wiley. All rights reserved.
Reserved Words
False as continue else from in not return yield
None assert def except global is or try
True break del finally if lambda pass while
and class elif for import nonlocal raise with
Table 1.1: A listing of the reserved words in Python. These names cannot be used
as identifiers.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.2. Objects in Python 5
For readers familiar with other programming languages, the semantics of a
Python identifier is most similar to a reference variable in Java or a pointer variable
in C++. Each identifier is implicitly associated with the memory address of the
object to which it refers. A Python identifier may be assigned to a special object
named None, serving a similar purpose to a null reference in Java or C++.
Unlike Java and C++, Python is a dynamically typed language, as there is no
advance declaration associating an identifier with a particular data type. An iden-
tifier can be associated with any type of object, and it can later be reassigned to
another object of the same (or different) type. Although an identifier has no de-
clared type, the object to which it refers has a definite type. In our first example,
the characters 98.6 are recognized as a floating-point literal, and thus the identifier
temperature is associated with an instance of the float class having that value.
A programmer can establish an alias by assigning a second identifier to an
existing object. Continuing with our earlier example, Figure 1.2 portrays the result
of a subsequent assignment, original = temperature.
float
temperature original
98.6
Figure 1.2: Identifiers temperature and original are aliases for the same object.
Once an alias has been established, either name can be used to access the under-
lying object. If that object supports behaviors that affect its state, changes enacted
through one alias will be apparent when using the other alias (because they refer to
the same object). However, if one of the names is reassigned to a new value using
a subsequent assignment statement, that does not affect the aliased object, rather it
breaks the alias. Continuing with our concrete example, we consider the command:
temperature = temperature + 5.0
The execution of this command begins with the evaluation of the expression on the
right-hand side of the = operator. That expression, temperature + 5.0, is eval-
uated based on the existing binding of the name temperature, and so the result
has value 103.6, that is, 98.6 + 5.0. That result is stored as a new floating-point
instance, and only then is the name on the left-hand side of the assignment state-
ment, temperature, (re)assigned to the result. The subsequent configuration is dia-
Copyright © 2013. Wiley. All rights reserved.
grammed in Figure 1.3. Of particular note, this last command had no effect on the
value of the existing float instance that identifier original continues to reference.
float float
temperature original
103.6 98.6
Figure 1.3: The temperature identifier has been assigned to a new value, while
original continues to refer to the previously existing value.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
6 Chapter 1. Python Primer
Calling Methods
Python supports traditional functions (see Section 1.5) that are invoked with a syn-
tax such as sorted(data), in which case data is a parameter sent to the function.
Python’s classes may also define one or more methods (also known as member
functions), which are invoked on a specific instance of a class using the dot (“.”)
operator. For example, Python’s list class has a method named sort that can be
invoked with a syntax such as data.sort( ). This particular method rearranges the
contents of the list so that they are sorted.
The expression to the left of the dot identifies the object upon which the method
is invoked. Often, this will be an identifier (e.g., data), but we can use the dot op-
erator to invoke a method upon the immediate result of some other operation. For
example, if response identifies a string instance (we will discuss strings later in this
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.2. Objects in Python 7
instances of that class are expressed as the literals True and False. The default
constructor, bool( ), returns False, but there is no reason to use that syntax rather
than the more direct literal form. Python allows the creation of a Boolean value
from a nonboolean type using the syntax bool(foo) for value foo. The interpretation
depends upon the type of the parameter. Numbers evaluate to False if zero, and
True if nonzero. Sequences and other container types, such as strings and lists,
evaluate to False if empty and True if nonempty. An important application of this
interpretation is the use of a nonboolean value as a condition in a control structure.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
8 Chapter 1. Python Primer
The int Class
The int and float classes are the primary numeric types in Python. The int class is
designed to represent integer values with arbitrary magnitude. Unlike Java and
C++, which support different integral types with different precisions (e.g., int,
short, long), Python automatically chooses the internal representation for an in-
teger based upon the magnitude of its value. Typical literals for integers include 0,
137, and −23. In some contexts, it is convenient to express an integral value using
binary, octal, or hexadecimal. That can be done by using a prefix of the number 0
and then a character to describe the base. Example of such literals are respectively
0b1011, 0o52, and 0x7f.
The integer constructor, int( ), returns value 0 by default. But this constructor
can be used to construct an integer value based upon an existing value of another
type. For example, if f represents a floating-point value, the syntax int(f) produces
the truncated value of f. For example, both int(3.14) and int(3.99) produce the
value 3, while int(−3.9) produces the value −3. The constructor can also be used
to parse a string that is presumed to represent an integral value (such as one en-
tered by a user). If s represents a string, then int(s) produces the integral value
that string represents. For example, the expression int( 137 ) produces the inte-
ger value 137. If an invalid string is given as a parameter, as in int( hello ), a
ValueError is raised (see Section 1.7 for discussion of Python’s exceptions). By de-
fault, the string must use base 10. If conversion from a different base is desired, that
base can be indicated as a second, optional, parameter. For example, the expression
int( 7f , 16) evaluates to the integer 127.
The float class is the sole floating-point type in Python, using a fixed-precision
representation. Its precision is more akin to a double in Java or C++, rather than
those languages’ float type. We have already discussed a typical literal form, 98.6.
We note that the floating-point equivalent of an integral number can be expressed
directly as 2.0. Technically, the trailing zero is optional, so some programmers
Copyright © 2013. Wiley. All rights reserved.
might use the expression 2. to designate this floating-point literal. One other form
of literal for floating-point values uses scientific notation. For example, the literal
6.022e23 represents the mathematical value 6.022 × 1023 .
The constructor form of float( ) returns 0.0. When given a parameter, the con-
structor attempts to return the equivalent floating-point value. For example, the call
float(2) returns the floating-point value 2.0. If the parameter to the constructor is
a string, as with float( 3.14 ), it attempts to parse that string as a floating-point
value, raising a ValueError as an exception.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.2. Objects in Python 9
Sequence Types: The list, tuple, and str Classes
The list, tuple, and str classes are sequence types in Python, representing a col-
lection of values in which the order is significant. The list class is the most general,
representing a sequence of arbitrary objects (akin to an “array” in other languages).
The tuple class is an immutable version of the list class, benefiting from a stream-
lined internal representation. The str class is specially designed for representing
an immutable sequence of text characters. We note that Python does not have a
separate class for characters; they are just strings with length one.
2 3 5 7 11 13 17 19 23 29 31
primes:
0 1 2 3 4 5 6 7 8 9 10
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
10 Chapter 1. Python Primer
The tuple Class
The tuple class provides an immutable version of a sequence, and therefore its
instances have an internal representation that may be more streamlined than that of
a list. While Python uses the [ ] characters to delimit a list, parentheses delimit a
tuple, with ( ) being an empty tuple. There is one important subtlety. To express
a tuple of length one as a literal, a comma must be placed after the element, but
within the parentheses. For example, (17,) is a one-element tuple. The reason for
this requirement is that, without the trailing comma, the expression (17) is viewed
as a simple parenthesized numeric expression.
S A M P L E
0 1 2 3 4 5
be embedded naturally (rather than escaped as \n). This can greatly improve the
readability of long, multiline strings in source code. For example, at the beginning
of Code Fragment 1.1, rather than use separate print statements for each line of
introductory output, we can use a single print statement, as follows:
print(”””Welcome to the GPA calculator.
Please enter all your letter grades, one per line.
Enter a blank line to designate the end.”””)
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.2. Objects in Python 11
The set and frozenset Classes
Python’s set class represents the mathematical notion of a set, namely a collection
of elements, without duplicates, and without an inherent order to those elements.
The major advantage of using a set, as opposed to a list, is that it has a highly
optimized method for checking whether a specific element is contained in the set.
This is based on a data structure known as a hash table (which will be the primary
topic of Chapter 10). However, there are two important restrictions due to the
algorithmic underpinnings. The first is that the set does not maintain the elements
in any particular order. The second is that only instances of immutable types can be
added to a Python set. Therefore, objects such as integers, floating-point numbers,
and character strings are eligible to be elements of a set. It is possible to maintain a
set of tuples, but not a set of lists or a set of sets, as lists and sets are mutable. The
frozenset class is an immutable form of the set type, so it is legal to have a set of
frozensets.
Python uses curly braces { and } as delimiters for a set, for example, as {17}
or { red , green , blue }. The exception to this rule is that { } does not
represent an empty set; for historical reasons, it represents an empty dictionary
(see next paragraph). Instead, the constructor syntax set( ) produces an empty set.
If an iterable parameter is sent to the constructor, then the set of distinct elements
is produced. For example, set( hello ) produces { h , e , l , o }.
Python’s dict class represents a dictionary, or mapping, from a set of distinct keys
to associated values. For example, a dictionary might map from unique student ID
numbers, to larger student records (such as the student’s name, address, and course
grades). Python implements a dict using an almost identical approach to that of a
set, but with storage of the associated values.
A dictionary literal also uses curly braces, and because dictionaries were intro-
Copyright © 2013. Wiley. All rights reserved.
duced in Python prior to sets, the literal form { } produces an empty dictionary.
A nonempty dictionary is expressed using a comma-separated series of key:value
pairs. For example, the dictionary { ga : Irish , de : German } maps
ga to Irish and de to German .
The constructor for the dict class accepts an existing mapping as a parameter,
in which case it creates a new dictionary with identical associations as the existing
one. Alternatively, the constructor accepts a sequence of key-value pairs as a pa-
rameter, as in dict(pairs) with pairs = [( ga , Irish ), ( de , German )].
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
12 Chapter 1. Python Primer
Logical Operators
Python supports the following keyword operators for Boolean values:
not unary negation
and conditional and
or conditional or
The and and or operators short-circuit, in that they do not evaluate the second
operand if the result can be determined based on the value of the first operand.
This feature is useful when constructing Boolean expressions in which we first test
that a certain condition holds (such as a reference not being None), and then test a
condition that could have otherwise generated an error condition had the prior test
not succeeded.
Equality Operators
Python supports the following operators to test two notions of equality:
Copyright © 2013. Wiley. All rights reserved.
is same identity
is not different identity
== equivalent
!= not equivalent
The expression a is b evaluates to True, precisely when identifiers a and b are
aliases for the same object. The expression a == b tests a more general notion of
equivalence. If identifiers a and b refer to the same object, then a == b should also
evaluate to True. Yet a == b also evaluates to True when the identifiers refer to
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.3. Expressions, Operators, and Precedence 13
different objects that happen to have values that are deemed equivalent. The precise
notion of equivalence depends on the data type. For example, two strings are con-
sidered equivalent if they match character for character. Two sets are equivalent if
they have the same contents, irrespective of order. In most programming situations,
the equivalence tests == and != are the appropriate operators; use of is and is not
should be reserved for situations in which it is necessary to detect true aliasing.
Comparison Operators
Data types may define a natural order via the following operators:
< less than
<= less than or equal to
> greater than
>= greater than or equal to
These operators have expected behavior for numeric types, and are defined lexi-
cographically, and case-sensitively, for strings. An exception is raised if operands
have incomparable types, as with 5 < hello .
Arithmetic Operators
Python supports the following arithmetic operators:
+ addition
− subtraction
multiplication
/ true division
// integer division
% the modulo operator
The use of addition, subtraction, and multiplication is straightforward, noting that if
both operands have type int, then the result is an int as well; if one or both operands
have type float, the result will be a float.
Python takes more care in its treatment of division. We first consider the case
in which both operands have type int, for example, the quantity 27 divided by
4. In mathematical notation, 27 ÷ 4 = 6 34 = 6.75. In Python, the / operator
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
14 Chapter 1. Python Primer
Python carefully extends the semantics of // and % to cases where one or both
operands are negative. For the sake of notation, let us assume that variables n
and m represent respectively the dividend and divisor of a quotient m n , and that
q = n // m and r = n % m. Python guarantees that q m + r will equal n. We
already saw an example of this identity with positive operands, as 6 ∗ 4 + 3 = 27.
When the divisor m is positive, Python further guarantees that 0 ≤ r < m. As
a consequence, we find that −27 // 4 evaluates to −7 and −27 % 4 evaluates
to 1, as (−7) ∗ 4 + 1 = −27. When the divisor is negative, Python guarantees that
m < r ≤ 0. As an example, 27 // −4 is −7 and 27 % −4 is −1, satisfying the
identity 27 = (−7) ∗ (−4) + (−1).
The conventions for the // and % operators are even extended to floating-
point operands, with the expression q = n // m being the integral floor of the
quotient, and r = n % m being the “remainder” to ensure that q m + r equals
n. For example, 8.2 // 3.14 evaluates to 2.0 and 8.2 % 3.14 evaluates to 1.92, as
2.0 ∗ 3.14 + 1.92 = 8.2.
Bitwise Operators
Python provides the following bitwise operators for integers:
∼ bitwise complement (prefix unary operator)
& bitwise and
| bitwise or
ˆ bitwise exclusive-or
<< shift bits left, filling in with zeros
>> shift bits right, filling in with sign bit
Sequence Operators
Each of Python’s built-in sequence types (str, tuple, and list) support the following
operator syntaxes:
s[j] element at index j
s[start:stop] slice including indices [start,stop)
s[start:stop:step] slice including indices start, start + step,
start + 2 step, . . . , up to but not equalling or stop
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.3. Expressions, Operators, and Precedence 15
notation to describe subsequences of a sequence. Slices are described as half-open
intervals, with a start index that is included, and a stop index that is excluded. For
example, the syntax data[3:8] denotes a subsequence including the five indices:
3, 4, 5, 6, 7. An optional “step” value, possibly negative, can be indicated as a third
parameter of the slice. If a start index or stop index is omitted in the slicing nota-
tion, it is presumed to designate the respective extreme of the original sequence.
Because lists are mutable, the syntax s[j] = val can be used to replace an ele-
ment at a given index. Lists also support a syntax, del s[j], that removes the desig-
nated element from the list. Slice notation can also be used to replace or delete a
sublist.
The notation val in s can be used for any of the sequences to see if there is an
element equivalent to val in the sequence. For strings, this syntax can be used to
check for a single character or for a larger substring, as with amp in example .
All sequences define comparison operations based on lexicographic order, per-
forming an element by element comparison until the first difference is found. For
example, [5, 6, 9] < [5, 7] because of the entries at index 1. Therefore, the follow-
ing operations are supported by sequence types:
s == t equivalent (element by element)
s != t not equivalent
s < t lexicographically less than
s <= t lexicographically less than or equal to
s > t lexicographically greater than
s >= t lexicographically greater than or equal to
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
16 Chapter 1. Python Primer
a partial order, but not a total order, as disjoint sets are neither “less than,” “equal
to,” or “greater than” each other. Sets also support many fundamental behaviors
through named methods (e.g., add, remove); we will explore their functionality
more fully in Chapter 10.
Dictionaries, like sets, do not maintain a well-defined order on their elements.
Furthermore, the concept of a subset is not typically meaningful for dictionaries, so
the dict class does not support operators such as <. Dictionaries support the notion
of equivalence, with d1 == d2 if the two dictionaries contain the same set of key-
value pairs. The most widely used behavior of dictionaries is accessing a value
associated with a particular key k with the indexing syntax, d[k]. The supported
operators are as follows:
Dictionaries also support many useful behaviors through named methods, which
we explore more fully in Chapter 10.
Python supports an extended assignment operator for most binary operators, for
example, allowing a syntax such as count += 5. By default, this is a shorthand for
the more verbose count = count + 5. For an immutable type, such as a number or
a string, one should not presume that this syntax changes the value of the existing
object, but instead that it will reassign the identifier to a newly constructed value.
(See discussion of Figure 1.3.) However, it is possible for a type to redefine such
semantics to mutate the object, as the list class does for the += operator.
Copyright © 2013. Wiley. All rights reserved.
alpha = [1, 2, 3]
beta = alpha # an alias for alpha
beta += [4, 5] # extends the original list with two more elements
beta = beta + [6, 7] # reassigns beta to a new list [1, 2, 3, 4, 5, 6, 7]
print(alpha) # will be [1, 2, 3, 4, 5]
This example demonstrates the subtle difference between the list semantics for the
syntax beta += foo versus beta = beta + foo.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.3. Expressions, Operators, and Precedence 17
Operator Precedence
Type Symbols
1 member access expr.member
function/method calls expr(...)
2
container subscripts/slices expr[...]
3 exponentiation
4 unary operators +expr, −expr, ˜expr
5 multiplication, division , /, //, %
6 addition, subtraction +, −
7 bitwise shifting <<, >>
8 bitwise-and &
9 bitwise-xor ˆ
10 bitwise-or |
comparisons is, is not, ==, !=, <, <=, >, >=
11
containment in, not in
12 logical-not not expr
13 logical-and and
Copyright © 2013. Wiley. All rights reserved.
14 logical-or or
15 conditional val1 if cond else val2
16 assignments =, +=, −=, =, etc.
Table 1.3: Operator precedence in Python, with categories ordered from highest
precedence to lowest precedence. When stated, we use expr to denote a literal,
identifier, or result of a previously evaluated expression. All operators without
explicit mention of expr are binary operators, with syntax expr1 operator expr2.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
18 Chapter 1. Python Primer
1.4.1 Conditionals
Conditional constructs (also known as if statements) provide a way to execute a
chosen block of code based on the run-time evaluation of one or more Boolean
expressions. In Python, the most general form of a conditional is written as follows:
if first condition:
first body
elif second condition:
second body
elif third condition:
third body
else:
fourth body
Each condition is a Boolean expression, and each body contains one or more com-
mands that are to be executed conditionally. If the first condition succeeds, the first
body will be executed; no other conditions or bodies are evaluated in that case.
If the first condition fails, then the process continues in similar manner with the
evaluation of the second condition. The execution of this overall construct will
cause precisely one of the bodies to be executed. There may be any number of
Copyright © 2013. Wiley. All rights reserved.
elif clauses (including zero), and the final else clause is optional. As described on
page 7, nonboolean types may be evaluated as Booleans with intuitive meanings.
For example, if response is a string that was entered by a user, and we want to
condition a behavior on this being a nonempty string, we may write
if response:
as a shorthand for the equivalent,
if response != :
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.4. Control Flow 19
As a simple example, a robot controller might have the following logic:
if door is closed:
open door( )
advance( )
Notice that the final command, advance( ), is not indented and therefore not part of
the conditional body. It will be executed unconditionally (although after opening a
closed door).
We may nest one control structure within another, relying on indentation to
make clear the extent of the various bodies. Revisiting our robot example, here is a
more complex control that accounts for unlocking a closed door.
if door is closed:
if door is locked:
unlock door( )
open door( )
advance( )
The logic expressed by this example can be diagrammed as a traditional flowchart,
as portrayed in Figure 1.6.
False True
door is closed
False True
door is locked
unlock door( )
open door( )
Copyright © 2013. Wiley. All rights reserved.
advance( )
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
20 Chapter 1. Python Primer
1.4.2 Loops
Python offers two distinct looping constructs. A while loop allows general repeti-
tion based upon the repeated testing of a Boolean condition. A for loop provides
convenient iteration of values from a defined series (such as characters of a string,
elements of a list, or numbers within a given range). We discuss both forms in this
section.
While Loops
The syntax for a while loop in Python is as follows:
while condition:
body
As with an if statement, condition can be an arbitrary Boolean expression, and
body can be an arbitrary block of code (including nested control structures). The
execution of a while loop begins with a test of the Boolean condition. If that condi-
tion evaluates to True, the body of the loop is performed. After each execution of
the body, the loop condition is retested, and if it evaluates to True, another iteration
of the body is performed. When the conditional test evaluates to False (assuming
it ever does), the loop is exited and the flow of control continues just beyond the
body of the loop.
As an example, here is a loop that advances an index through a sequence of
characters until finding an entry with value X or reaching the end of the sequence.
j=0
while j < len(data) and data[j] != X :
j += 1
The len function, which we will introduce in Section 1.5.2, returns the length of a
sequence such as a list or string. The correctness of this loop relies on the short-
circuiting behavior of the and operator, as described on page 12. We intention-
ally test j < len(data) to ensure that j is a valid index, prior to accessing element
data[j]. Had we written that compound condition with the opposite order, the eval-
Copyright © 2013. Wiley. All rights reserved.
uation of data[j] would eventually raise an IndexError when X is not found. (See
Section 1.7 for discussion of exceptions.)
As written, when this loop terminates, variable j’s value will be the index of
the leftmost occurrence of X , if found, or otherwise the length of the sequence
(which is recognizable as an invalid index to indicate failure of the search). It is
worth noting that this code behaves correctly, even in the special case when the list
is empty, as the condition j < len(data) will initially fail and the body of the loop
will never be executed.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.4. Control Flow 21
For Loops
For readers familiar with Java, the semantics of Python’s for loop is similar to the
“for each” loop style introduced in Java 1.5.
As an instructive example of such a loop, we consider the task of computing
the sum of a list of numbers. (Admittedly, Python has a built-in function, sum, for
this purpose.) We perform the calculation with a for loop as follows, assuming that
data identifies the list:
total = 0
for val in data:
total += val # note use of the loop variable, val
The loop body executes once for each element of the data sequence, with the iden-
tifier, val, from the for-loop syntax assigned at the beginning of each pass to a
respective element. It is worth noting that val is treated as a standard identifier. If
the element of the original data happens to be mutable, the val identifier can be
used to invoke its methods. But a reassignment of identifier val to a new value has
no affect on the original data, nor on the next iteration of the loop.
As a second classic example, we consider the task of finding the maximum
value in a list of elements (again, admitting that Python’s built-in max function
already provides this support). If we can assume that the list, data, has at least one
element, we could implement this task as follows:
biggest = data[0] # as we assume nonempty list
for val in data:
if val > biggest:
Copyright © 2013. Wiley. All rights reserved.
biggest = val
Although we could accomplish both of the above tasks with a while loop, the
for-loop syntax had an advantage of simplicity, as there is no need to manage an
explicit index into the list nor to author a Boolean loop condition. Furthermore, we
can use a for loop in cases for which a while loop does not apply, such as when
iterating through a collection, such as a set, that does not support any direct form
of indexing.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
22 Chapter 1. Python Primer
Index-Based For Loops
The simplicity of a standard for loop over the elements of a list is wonderful; how-
ever, one limitation of that form is that we do not know where an element resides
within the sequence. In some applications, we need knowledge of the index of an
element within the sequence. For example, suppose that we want to know where
the maximum element in a list resides.
Rather than directly looping over the elements of the list in that case, we prefer
to loop over all possible indices of the list. For this purpose, Python provides
a built-in class named range that generates integer sequences. (We will discuss
generators in Section 1.8.) In simplest form, the syntax range(n) generates the
series of n values from 0 to n − 1. Conveniently, these are precisely the series of
valid indices into a sequence of length n. Therefore, a standard Python idiom for
looping through the series of indices of a data sequence uses a syntax,
for j in range(len(data)):
In this case, identifier j is not an element of the data—it is an integer. But the
expression data[j] can be used to retrieve the respective element. For example, we
can find the index of the maximum element of a list as follows:
big index = 0
for j in range(len(data)):
if data[j] > data[big index]:
big index = j
if item == target:
found = True
break
Python also supports a continue statement that causes the current iteration of a
loop body to stop, but with subsequent passes of the loop proceeding as expected.
We recommend that the break and continue statements be used sparingly. Yet,
there are situations in which these commands can be effectively used to avoid in-
troducing overly complex logical conditions.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.5. Functions 23
1.5 Functions
In this section, we explore the creation of and use of functions in Python. As we
did in Section 1.2.2, we draw a distinction between functions and methods. We
use the general term function to describe a traditional, stateless function that is in-
voked without the context of a particular class or an instance of that class, such as
sorted(data). We use the more specific term method to describe a member function
that is invoked upon a specific object using an object-oriented message passing syn-
tax, such as data.sort( ). In this section, we only consider pure functions; methods
will be explored with more general object-oriented principles in Chapter 2.
We begin with an example to demonstrate the syntax for defining functions in
Python. The following function counts the number of occurrences of a given target
value within any form of iterable data set.
def count(data, target):
n=0
for item in data:
if item == target: # found a match
n += 1
return n
The first line, beginning with the keyword def, serves as the function’s signature.
This establishes a new identifier as the name of the function (count, in this exam-
ple), and it establishes the number of parameters that it expects, as well as names
identifying those parameters (data and target, in this example). Unlike Java and
C++, Python is a dynamically typed language, and therefore a Python signature
does not designate the types of those parameters, nor the type (if any) of a return
value. Those expectations should be stated in the function’s documentation (see
Section 2.2.3) and can be enforced within the body of the function, but misuse of a
function will only be detected at run-time.
The remainder of the function definition is known as the body of the func-
tion. As is the case with control structures in Python, the body of a function is
typically expressed as an indented block of code. Each time a function is called,
Python creates a dedicated activation record that stores information relevant to the
Copyright © 2013. Wiley. All rights reserved.
current call. This activation record includes what is known as a namespace (see
Section 1.10) to manage all identifiers that have local scope within the current call.
The namespace includes the function’s parameters and any other identifiers that are
defined locally within the body of the function. An identifier in the local scope
of the function caller has no relation to any identifier with the same name in the
caller’s scope (although identifiers in different scopes may be aliases to the same
object). In our first example, the identifier n has scope that is local to the function
call, as does the identifier item, which is established as the loop variable.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
24 Chapter 1. Python Primer
Return Statement
A return statement is used within the body of a function to indicate that the func-
tion should immediately cease execution, and that an expressed value should be
returned to the caller. If a return statement is executed without an explicit argu-
ment, the None value is automatically returned. Likewise, None will be returned if
the flow of control ever reaches the end of a function body without having executed
a return statement. Often, a return statement will be the final command within the
body of the function, as was the case in our earlier example of a count function.
However, there can be multiple return statements in the same function, with con-
ditional logic controlling which such command is executed, if any. As a further
example, consider the following function that tests if a value exists in a sequence.
def contains(data, target):
for item in target:
if item == target: # found a match
return True
return False
If the conditional within the loop body is ever satisfied, the return True statement is
executed and the function immediately ends, with True designating that the target
value was found. Conversely, if the for loop reaches its conclusion without ever
finding the match, the final return False statement will be executed.
For example, consider the following call to our count function from page 23:
prizes = count(grades, A )
Just before the function body is executed, the actual parameters, grades and A ,
are implicitly assigned to the formal parameters, data and target, as follows:
data = grades
target = A
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.5. Functions 25
These assignment statements establish identifier data as an alias for grades and
target as a name for the string literal A . (See Figure 1.7.)
grades data target
list str
... A
Figure 1.7: A portrayal of parameter passing in Python, for the function call
count(grades, A ). Identifiers data and target are formal parameters defined
within the local scope of the count function.
The communication of a return value from the function back to the caller is
similarly implemented as an assignment. Therefore, with our sample invocation of
prizes = count(grades, A ), the identifier prizes in the caller’s scope is assigned
to the object that is identified as n in the return statement within our function body.
An advantage to Python’s mechanism for passing information to and from a
function is that objects are not copied. This ensures that the invocation of a function
is efficient, even in a case where a parameter or return value is a complex object.
Mutable Parameters
Python’s parameter passing model has additional implications when a parameter is
a mutable object. Because the formal parameter is an alias for the actual parameter,
the body of the function may interact with the object in ways that change its state.
Considering again our sample invocation of the count function, if the body of the
function executes the command data.append( F ), the new entry is added to the
end of the list identified as data within the function, which is one and the same as
the list known to the caller as grades. As an aside, we note that reassigning a new
value to a formal parameter with a function body, such as by setting data = [ ],
does not alter the actual parameter; such a reassignment simply breaks the alias.
Our hypothetical example of a count method that appends a new element to a
list lacks common sense. There is no reason to expect such a behavior, and it would
be quite a poor design to have such an unexpected effect on the parameter. There
Copyright © 2013. Wiley. All rights reserved.
are, however, many legitimate cases in which a function may be designed (and
clearly documented) to modify the state of a parameter. As a concrete example,
we present the following implementation of a method named scale that’s primary
purpose is to multiply all entries of a numeric data set by a given factor.
def scale(data, factor):
for j in range(len(data)):
data[j] = factor
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
26 Chapter 1. Python Primer
Default Parameter Values
Python provides means for functions to support more than one possible calling
signature. Such a function is said to be polymorphic (which is Greek for “many
forms”). Most notably, functions can declare one or more default values for pa-
rameters, thereby allowing the caller to invoke a function with varying numbers of
actual parameters. As an artificial example, if a function is declared with signature
def foo(a, b=15, c=27):
there are three parameters, the last two of which offer default values. A caller is
welcome to send three actual parameters, as in foo(4, 12, 8), in which case the de-
fault values are not used. If, on the other hand, the caller only sends one parameter,
foo(4), the function will execute with parameters values a=4, b=15, c=27. If a
caller sends two parameters, they are assumed to be the first two, with the third be-
ing the default. Thus, foo(8, 20) executes with a=8, b=20, c=27. However, it is
illegal to define a function with a signature such as bar(a, b=15, c) with b having
a default value, yet not the subsequent c; if a default parameter value is present for
one parameter, it must be present for all further parameters.
As a more motivating example for the use of a default parameter, we revisit
the task of computing a student’s GPA (see Code Fragment 1.1). Rather than as-
sume direct input and output with the console, we prefer to design a function that
computes and returns a GPA. Our original implementation uses a fixed mapping
from each letter grade (such as a B−) to a corresponding point value (such as
2.67). While that point system is somewhat common, it may not agree with the
system used by all schools. (For example, some may assign an A+ grade a value
higher than 4.0.) Therefore, we design a compute gpa function, given in Code
Fragment 1.2, which allows the caller to specify a custom mapping from grades to
values, while offering the standard point system as a default.
for g in grades:
if g in points: # a recognizable grade
num courses += 1
total points += points[g]
return total points / num courses
Code Fragment 1.2: A function that computes a student’s GPA with a point value
system that can be customized as an optional parameter.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.5. Functions 27
As an additional example of an interesting polymorphic function, we consider
Python’s support for range. (Technically, this is a constructor for the range class,
but for the sake of this discussion, we can treat it as a pure function.) Three calling
syntaxes are supported. The one-parameter form, range(n), generates a sequence of
integers from 0 up to but not including n. A two-parameter form, range(start,stop)
generates integers from start up to, but not including, stop. A three-parameter
form, range(start, stop, step), generates a similar range as range(start, stop), but
with increments of size step rather than 1.
This combination of forms seems to violate the rules for default parameters.
In particular, when a single parameter is sent, as in range(n), it serves as the stop
value (which is the second parameter); the value of start is effectively 0 in that
case. However, this effect can be achieved with some sleight of hand, as follows:
def range(start, stop=None, step=1):
if stop is None:
stop = start
start = 0
...
From a technical perspective, when range(n) is invoked, the actual parameter n will
be assigned to formal parameter start. Within the body, if only one parameter is
received, the start and stop values are reassigned to provide the desired semantics.
Keyword Parameters
The traditional mechanism for matching the actual parameters sent by a caller, to
the formal parameters declared by the function signature is based on the concept
of positional arguments. For example, with signature foo(a=10, b=20, c=30),
parameters sent by the caller are matched, in the given order, to the formal param-
eters. An invocation of foo(5) indicates that a=5, while b and c are assigned their
default values.
Python supports an alternate mechanism for sending a parameter to a function
known as a keyword argument. A keyword argument is specified by explicitly
assigning an actual parameter to a formal parameter by name. For example, with
Copyright © 2013. Wiley. All rights reserved.
the above definition of function foo, a call foo(c=5) will invoke the function with
parameters a=10, b=20, c=5.
A function’s author can require that certain parameters be sent only through the
keyword-argument syntax. We never place such a restriction in our own function
definitions, but we will see several important uses of keyword-only parameters in
Python’s standard libraries. As an example, the built-in max function accepts a
keyword parameter, coincidentally named key, that can be used to vary the notion
of “maximum” that is used.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
28 Chapter 1. Python Primer
By default, max operates based upon the natural order of elements according
to the < operator for that type. But the maximum can be computed by comparing
some other aspect of the elements. This is done by providing an auxiliary function
that converts a natural element to some other value for the sake of comparison.
For example, if we are interested in finding a numeric value with magnitude that is
maximal (i.e., considering −35 to be larger than +20), we can use the calling syn-
tax max(a, b, key=abs). In this case, the built-in abs function is itself sent as the
value associated with the keyword parameter key. (Functions are first-class objects
in Python; see Section 1.10.) When max is called in this way, it will compare abs(a)
to abs(b), rather than a to b. The motivation for the keyword syntax as an alternate
to positional arguments is important in the case of max. This function is polymor-
phic in the number of arguments, allowing a call such as max(a,b,c,d); therefore,
it is not possible to designate a key function as a traditional positional element.
Sorting functions in Python also support a similar key parameter for indicating a
nonstandard order. (We explore this further in Section 9.4 and in Section 12.6.1,
when discussing sorting algorithms).
Ordering: max and min apply to any data type that supports a notion of compar-
ison, or to any collection of such values. Likewise, sorted can be used to produce
an ordered list of elements drawn from any existing collection.
Collections/Iterations: range generates a new sequence of numbers; len reports
the length of any existing collection; functions reversed, all, any, and map oper-
ate on arbitrary iterations as well; iter and next provide a general framework for
iteration through elements of a collection, and are discussed in Section 1.8.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.5. Functions 29
sum(iterable) Return the sum of the elements in the iterable (must be numeric).
type(obj) Return the class to which the instance obj belongs.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
30 Chapter 1. Python Primer
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.6. Simple Input and Output 31
When reading a numeric value from the user, a programmer must use the input
function to get the string of characters, and then use the int or float syntax to
construct the numeric value that character string represents. That is, if a call to
response = input( ) reports that the user entered the characters, 2013 , the syntax
int(response) could be used to produce the integer value 2013. It is quite common
to combine these operations with a syntax such as
year = int(input( In what year were you born? ))
if we assume that the user will enter an appropriate response. (In Section 1.7 we
discuss error handling in such a situation.)
Because input returns a string as its result, use of that function can be combined
with the existing functionality of the string class, as described in Appendix A. For
example, if the user enters multiple pieces of information on the same line, it is
common to call the split method on the result, as in
reply = input( Enter x and y, separated by spaces: )
pieces = reply.split( ) # returns a list of strings, as separated by spaces
x = float(pieces[0])
y = float(pieces[1])
A Sample Program
Here is a simple, but complete, program that demonstrates the use of the input
and print functions. The tools for formatting the final output is discussed in Ap-
pendix A.
age = int(input( Enter your age in years: ))
max heart rate = 206.9 − (0.67 age) # as per Med Sci Sports Exerc.
target = 0.65 max heart rate
print( Your target fat-burning heart rate is , target)
1.6.2 Files
Files are typically accessed in Python beginning with a call to a built-in function,
named open, that returns a proxy for interactions with the underlying file. For
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
32 Chapter 1. Python Primer
When processing a file, the proxy maintains a current position within the file as
an offset from the beginning, measured in number of bytes. When opening a file
with mode r or w , the position is initially 0; if opened in append mode, a ,
the position is initially at the end of the file. The syntax fp.close( ) closes the file
associated with proxy fp, ensuring that any written contents are saved. A summary
of methods for reading and writing a file is given in Table 1.5
Table 1.5: Behaviors for interacting with a text file via a file proxy (named fp).
Writing to a File
Copyright © 2013. Wiley. All rights reserved.
When a file proxy is writable, for example, if created with access mode w or
a , text can be written using methods write or writelines. For example, if we de-
fine fp = open( results.txt , w ), the syntax fp.write( Hello World.\n )
writes a single line to the file with the given string. Note well that write does not
explicitly add a trailing newline, so desired newline characters must be embedded
directly in the string parameter. Recall that the output of the print method can be
redirected to a file using a keyword parameter, as described in Section 1.6.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.7. Exception Handling 33
Class Description
Exception A base class for most error types
AttributeError Raised by syntax obj.foo, if obj has no member named foo
EOFError Raised if “end of file” reached for console or file input
IOError Raised upon failure of I/O operation (e.g., opening file)
IndexError Raised if index to sequence is out of bounds
KeyError Raised if nonexistent key requested for set or dictionary
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
34 Chapter 1. Python Primer
Sending the wrong number, type, or value of parameters to a function is another
common cause for an exception. For example, a call to abs( hello ) will raise a
TypeError because the parameter is not numeric, and a call to abs(3, 5) will raise
a TypeError because one parameter is expected. A ValueError is typically raised
when the correct number and type of parameters are sent, but a value is illegitimate
for the context of the function. For example, the int constructor accepts a string,
as with int( 137 ), but a ValueError is raised if that string does not represent an
integer, as with int( 3.14 ) or int( hello ).
Python’s sequence types (e.g., list, tuple, and str) raise an IndexError when
syntax such as data[k] is used with an integer k that is not a valid index for the given
sequence (as described in Section 1.2.3). Sets and dictionaries raise a KeyError
when an attempt is made to access a nonexistent element.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.7. Exception Handling 35
How much error-checking to perform within a function is a matter of debate.
Checking the type and value of each parameter demands additional execution time
and, if taken to an extreme, seems counter to the nature of Python. Consider the
built-in sum function, which computes a sum of a collection of numbers. An im-
plementation with rigorous error-checking might be written as follows:
def sum(values):
if not isinstance(values, collections.Iterable):
raise TypeError( parameter must be an iterable type )
total = 0
for v in values:
if not isinstance(v, (int, float)):
raise TypeError( elements must be numeric )
total = total+ v
return total
The abstract base class, collections.Iterable, includes all of Python’s iterable con-
tainers types that guarantee support for the for-loop syntax (e.g., list, tuple, set);
we discuss iterables in Section 1.8, and the use of modules, such as collections, in
Section 1.11. Within the body of the for loop, each element is verified as numeric
before being added to the total. A far more direct and clear implementation of this
function can be written as follows:
def sum(values):
total = 0
for v in values:
total = total + v
return total
Interestingly, this simple implementation performs exactly like Python’s built-in
version of the function. Even without the explicit checks, appropriate exceptions
are raised naturally by the code. In particular, if values is not an iterable type, the
attempt to use the for-loop syntax raises a TypeError reporting that the object is not
iterable. In the case when a user sends an iterable type that includes a nonnumer-
ical element, such as sum([3.14, oops ]), a TypeError is naturally raised by the
evaluation of expression total + v. The error message
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
36 Chapter 1. Python Primer
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.7. Exception Handling 37
Exception handling is particularly useful when working with user input, or
when reading from or writing to files, because such interactions are inherently less
predictable. In Section 1.6.2, we suggest the syntax, fp = open( sample.txt ),
for opening a file with read access. That command may raise an IOError for a vari-
ety of reasons, such as a non-existent file, or lack of sufficient privilege for opening
a file. It is significantly easier to attempt the command and catch the resulting error
than it is to accurately predict whether the command will succeed.
We continue by demonstrating a few other forms of the try-except syntax. Ex-
ceptions are objects that can be examined when caught. To do so, an identifier must
be established with a syntax as follows:
try:
fp = open( sample.txt )
except IOError as e:
print( Unable to open the file: , e)
In this case, the name, e, denotes the instance of the exception that was thrown, and
printing it causes a detailed error message to be displayed (e.g., “file not found”).
A try-statement may handle more than one type of exception. For example,
consider the following command from Section 1.6.1:
age = int(input( Enter your age in years: ))
This command could fail for a variety of reasons. The call to input will raise an
EOFError if the console input fails. If the call to input completes successfully, the
int constructor raises a ValueError if the user has not entered characters represent-
ing a valid integer. If we want to handle two or more types of errors in the same
way, we can use a single except-statement, as in the following example:
age = −1 # an initially invalid choice
while age <= 0:
try:
age = int(input( Enter your age in years: ))
if age <= 0:
print( Your age must be positive )
except (ValueError, EOFError):
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
38 Chapter 1. Python Primer
will be unchanged, the while loop will continue. If we preferred to have the while
loop continue without printing the Invalid response message, we could have
written the exception-clause as
except (ValueError, EOFError):
pass
The keyword, pass, is a statement that does nothing, yet it can serve syntactically
as a body of a control structure. In this way, we quietly catch the exception, thereby
allowing the surrounding while loop to continue.
In order to provide different responses to different types of errors, we may use
two or more except-clauses as part of a try-structure. In our previous example, an
EOFError suggests a more insurmountable error than simply an errant value being
entered. In that case, we might wish to provide a more specific error message, or
perhaps to allow the exception to interrupt the loop and be propagated to a higher
context. We could implement such behavior as follows:
age = −1 # an initially invalid choice
while age <= 0:
try:
age = int(input( Enter your age in years: ))
if age <= 0:
print( Your age must be positive )
except ValueError:
print( That is an invalid age specification )
except EOFError:
print( There was an unexpected error reading input. )
raise # let s re-raise this exception
In this implementation, we have separate except-clauses for the ValueError and
EOFError cases. The body of the clause for handling an EOFError relies on another
technique in Python. It uses the raise statement without any subsequent argument,
to re-raise the same exception that is currently being handled. This allows us to
provide our own response to the exception, and then to interrupt the while loop and
propagate the exception upward.
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.8. Iterators and Generators 39
ries of values, that is, without constructing a data structure to store all of its values
at once. For example, the call range(1000000) does not return a list of numbers; it
returns a range object that is iterable. This object generates the million values one
at a time, and only as needed. Such a lazy evaluation technique has great advan-
tage. In the case of range, it allows a loop of the form, for j in range(1000000):,
to execute without setting aside memory for storing one million values. Also, if
such a loop were to be interrupted in some fashion, no time will have been spent
computing unused values of the range.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
40 Chapter 1. Python Primer
We see lazy evaluation used in many of Python’s libraries. For example, the
dictionary class supports methods keys( ), values( ), and items( ), which respec-
tively produce a “view” of all keys, values, or (key,value) pairs within a dictionary.
None of these methods produces an explicit list of results. Instead, the views that
are produced are iterable objects based upon the actual contents of the dictionary.
An explicit list of values from such an iteration can be immediately constructed by
calling the list class constructor with the iteration as a parameter. For example, the
syntax list(range(1000)) produces a list instance with values from 0 to 999, while
the syntax list(d.values( )) produces a list that has elements based upon the current
values of dictionary d. We can similarly construct a tuple or set instance based
upon a given iterable.
Generators
In Section 2.3.4, we will explain how to define a class whose instances serve as
iterators. However, the most convenient technique for creating iterators in Python
is through the use of generators. A generator is implemented with a syntax that
is very similar to a function, but instead of returning values, a yield statement is
executed to indicate each element of the series. As an example, consider the goal
of determining all factors of a positive integer. For example, the number 100 has
factors 1, 2, 4, 5, 10, 20, 25, 50, 100. A traditional function might produce and
return a list containing all factors, implemented as:
def factors(n): # traditional function that computes factors
results = [ ] # store factors in a new list
for k in range(1,n+1):
if n % k == 0: # divides evenly, thus k is a factor
results.append(k) # add k to the list of factors
return results # return the entire list
In contrast, an implementation of a generator for computing those factors could be
implemented as follows:
def factors(n): # generator that computes factors
for k in range(1,n+1):
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.8. Iterators and Generators 41
until a yield statement indicates the next value. At that point, the procedure is tem-
porarily interrupted, only to be resumed when another value is requested. When
the flow of control naturally reaches the end of our procedure (or a zero-argument
return statement), a StopIteration exception is automatically raised. Although this
particular example uses a single yield statement in the source code, a generator can
rely on multiple yield statements in different constructs, with the generated series
determined by the natural flow of control. For example, we can greatly improve
the efficiency of our generator for computing factors of a number, n, by only test-
ing values up to the square root of that number, while reporting the factor n//k
that is associated with each k (unless n//k equals k). We might implement such a
generator as follows:
def factors(n): # generator that computes factors
k=1
while k k < n: # while k < sqrt(n)
if n % k == 0:
yield k
yield n // k
k += 1
if k k == n: # special case if n is perfect square
yield k
We should note that this generator differs from our first version in that the factors
are not generated in strictly increasing order. For example, factors(100) generates
the series 1, 100, 2, 50, 4, 25, 5, 20, 10.
In closing, we wish to emphasize the benefits of lazy evaluation when using a
generator rather than a traditional function. The results are only computed if re-
quested, and the entire series need not reside in memory at one time. In fact, a
generator can effectively produce an infinite series of values. As an example, the
Fibonacci numbers form a classic mathematical sequence, starting with value 0,
then value 1, and then each subsequent value being the sum of the two preceding
values. Hence, the Fibonacci series begins as: 0, 1, 1, 2, 3, 5, 8, 13, . . .. The follow-
ing generator produces this infinite series.
def fibonacci( ):
Copyright © 2013. Wiley. All rights reserved.
a=0
b=1
while True: # keep going...
yield a # report value, a, during this pass
future = a + b
a=b # this will be next value reported
b = future # and subsequently this
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
42 Chapter 1. Python Primer
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.9. Additional Python Conveniences 43
The generator syntax is particularly attractive when results do not need to be stored
in memory. For example, to compute the sum of the first n squares, the genera-
tor syntax, total = sum(k k for k in range(1, n+1)), is preferred to the use of an
explicitly instantiated list comprehension as the parameter.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
44 Chapter 1. Python Primer
Python provides two additional conveniences involving the treatment of tuples and
other sequence types. The first is rather cosmetic. If a series of comma-separated
expressions are given in a larger context, they will be treated as a single tuple, even
if no enclosing parentheses are provided. For example, the assignment
data = 2, 4, 6, 8
results in identifier, data, being assigned to the tuple (2, 4, 6, 8). This behavior
is called automatic packing of a tuple. One common use of packing in Python is
when returning multiple values from a function. If the body of a function executes
the command,
return x, y
it will be formally returning a single object that is the tuple (x, y).
As a dual to the packing behavior, Python can automatically unpack a se-
quence, allowing one to assign a series of individual identifiers to the elements
of sequence. As an example, we can write
a, b, c, d = range(7, 11)
which has the effect of assigning a=7, b=8, c=9, and d=10, as those are the four
values in the sequence returned by the call to range. For this syntax, the right-hand
side expression can be any iterable type, as long as the number of variables on the
left-hand side is the same as the number of elements in the iteration.
This technique can be used to unpack tuples returned by a function. For exam-
ple, the built-in function, divmod(a, b), returns the pair of values (a // b, a % b)
associated with an integer division. Although the caller can consider the return
value to be a single tuple, it is possible to write
quotient, remainder = divmod(a, b)
to separately identify the two entries of the returned tuple. This syntax can also be
Copyright © 2013. Wiley. All rights reserved.
used in the context of a for loop, when iterating over a sequence of iterables, as in
for x, y in [ (7, 2), (5, 8), (6, 4) ]:
In this example, there will be three iterations of the loop. During the first pass, x=7
and y=2, and so on. This style of loop is quite commonly used to iterate through
key-value pairs that are returned by the items( ) method of the dict class, as in:
for k, v in mapping.items( ):
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.9. Additional Python Conveniences 45
Simultaneous Assignments
with brief use of a third variable. With simultaneous assignments, that generator
can be implemented more directly as follows:
def fibonacci( ):
a, b = 0, 1
while True:
yield a
a, b = b, a+b
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
46 Chapter 1. Python Primer
When computing a sum with the syntax x + y in Python, the names x and y must
have been previously associated with objects that serve as values; a NameError
will be raised if no such definitions are found. The process of determining the
value associated with an identifier is known as name resolution.
Whenever an identifier is assigned to a value, that definition is made with a
specific scope. Top-level assignments are typically made in what is known as global
scope. Assignments made within the body of a function typically have scope that is
local to that function call. Therefore, an assignment, x = 5, within a function has
no effect on the identifier, x, in the broader scope.
Each distinct scope in Python is represented using an abstraction known as a
namespace. A namespace manages all identifiers that are currently defined in a
given scope. Figure 1.8 portrays two namespaces, one being that of a caller to our
count function from Section 1.5, and the other being the local namespace during
the execution of that function.
float str
n
3.56 gpa A int
target
grades 2
list data
str major item
CS
str str str
A- B+ A-
Figure 1.8: A portrayal of the two namespaces associated with a user’s call
count(grades, A ), as defined in Section 1.5. The left namespace is the caller’s
Copyright © 2013. Wiley. All rights reserved.
and the right namespace represents the local scope of the function.
Python implements a namespace with its own dictionary that maps each iden-
tifying string (e.g., n ) to its associated value. Python provides several ways to
examine a given namespace. The function, dir, reports the names of the identifiers
in a given namespace (i.e., the keys of the dictionary), while the function, vars,
returns the full dictionary. By default, calls to dir( ) and vars( ) report on the most
locally enclosing namespace in which they are executed.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.10. Scopes and Namespaces 47
When an identifier is indicated in a command, Python searches a series of
namespaces in the process of name resolution. First, the most locally enclosing
scope is searched for a given name. If not found there, the next outer scope is
searched, and so on. We will continue our examination of namespaces, in Sec-
tion 2.5, when discussing Python’s treatment of object-orientation. We will see
that each object has its own namespace to store its attributes, and that classes each
have a namespace as well.
First-Class Objects
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
48 Chapter 1. Python Primer
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.11. Modules and the Import Statement 49
It is worth noting that top-level commands with the module source code are
executed when the module is first imported, almost as if the module were its own
script. There is a special construct for embedding commands within the module
that will be executed if the module is directly invoked as a script, but not when
the module is imported from another script. Such commands should be placed in a
body of a conditional statement of the following form,
if name == __main__ :
Using our hypothetical utility.py module as an example, such commands will
be executed if the interpreter is started with a command python utility.py, but
not when the utility module is imported into another context. This approach is often
used to embed what are known as unit tests within the module; we will discuss unit
testing further in Section 2.2.4.
Existing Modules
Module Name Description
array Provides compact array storage for primitive types.
Defines additional data structures and abstract base classes
collections
involving collections of objects.
copy Defines general functions for making copies of objects.
heapq Provides heap-based priority queue functions (see Section 9.3.7).
math Defines common mathematical constants and functions.
os Provides support for interactions with the operating system.
random Provides random number generation.
re Provides support for processing regular expressions.
sys Provides additional level of interaction with the Python interpreter.
time Provides support for measuring time, or delaying a program.
Copyright © 2013. Wiley. All rights reserved.
Table 1.7: Some existing Python modules relevant to data structures and algorithms.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
50 Chapter 1. Python Primer
next number in a sequence based upon one or more past numbers that it has gen-
erated. Indeed, a simple yet popular pseudo-random number generator chooses its
next number based solely on the most recently chosen number and some additional
parameters using the following formula.
next = (a*current + b) % n;
where a, b, and n are appropriately chosen integers. Python uses a more advanced
technique known as a Mersenne twister. It turns out that the sequences generated
by these techniques can be proven to be statistically uniform, which is usually
good enough for most applications requiring random numbers, such as games. For
applications, such as computer security settings, where one needs unpredictable
random sequences, this kind of formula should not be used. Instead, one should
ideally sample from a source that is actually random, such as radio static coming
from outer space.
Since the next number in a pseudo-random generator is determined by the pre-
vious number(s), such a generator always needs a place to start, which is called its
seed. The sequence of numbers generated for a given seed will always be the same.
One common trick to get a different sequence each time a program is run is to use
a seed that will be different for each run. For example, we could use some timed
input from a user or the current system time in milliseconds.
Python’s random module provides support for pseudo-random number gener-
ation by defining a Random class; instances of that class serve as generators with
independent state. This allows different aspects of a program to rely on their own
pseudo-random number generator, so that calls to one generator do not affect the
sequence of numbers produced by another. For convenience, all of the methods
supported by the Random class are also supported as stand-alone functions of the
random module (essentially using a single generator instance for all top-level calls).
Syntax Description
Initializes the pseudo-random number generator
seed(hashable)
based upon the hash value of the parameter
Returns a pseudo-random floating-point
random( )
value in the interval [0.0, 1.0).
Returns a pseudo-random integer
randint(a,b)
in the closed interval [a, b].
Copyright © 2013. Wiley. All rights reserved.
Table 1.8: Methods supported by instances of the Random class, and as top-level
functions of the random module.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.12. Exercises 51
1.12 Exercises
For help with exercises, please visit the site, www.wiley.com/college/goodrich.
Reinforcement
R-1.1 Write a short Python function, is multiple(n, m), that takes two integer
values and returns True if n is a multiple of m, that is, n = mi for some
integer i, and False otherwise.
R-1.2 Write a short Python function, is even(k), that takes an integer value and
returns True if k is even, and False otherwise. However, your function
cannot use the multiplication, modulo, or division operators.
R-1.3 Write a short Python function, minmax(data), that takes a sequence of
one or more numbers, and returns the smallest and largest numbers, in the
form of a tuple of length two. Do not use the built-in functions min or
max in implementing your solution.
R-1.4 Write a short Python function that takes a positive integer n and returns
the sum of the squares of all the positive integers smaller than n.
R-1.5 Give a single command that computes the sum from Exercise R-1.4, rely-
ing on Python’s comprehension syntax and the built-in sum function.
R-1.6 Write a short Python function that takes a positive integer n and returns
the sum of the squares of all the odd positive integers smaller than n.
R-1.7 Give a single command that computes the sum from Exercise R-1.6, rely-
ing on Python’s comprehension syntax and the built-in sum function.
R-1.8 Python allows negative integers to be used as indices into a sequence,
such as a string. If string s has length n, and expression s[k] is used for in-
dex −n ≤ k < 0, what is the equivalent index j ≥ 0 such that s[j] references
the same element?
R-1.9 What parameters should be sent to the range constructor, to produce a
range with values 50, 60, 70, 80?
R-1.10 What parameters should be sent to the range constructor, to produce a
range with values 8, 6, 4, 2, 0, −2, −4, −6, −8?
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
52 Chapter 1. Python Primer
Creativity
C-1.13 Write a pseudo-code description of a function that reverses a list of n
integers, so that the numbers are listed in the opposite order than they
were before, and compare this method to an equivalent Python function
for doing the same thing.
C-1.14 Write a short Python function that takes a sequence of integer values and
determines if there is a distinct pair of numbers in the sequence whose
product is odd.
C-1.15 Write a Python function that takes a sequence of numbers and determines
if all the numbers are different from each other (that is, they are distinct).
C-1.16 In our implementation of the scale function (page 25), the body of the loop
executes the command data[j] = factor. We have discussed that numeric
types are immutable, and that use of the = operator in this context causes
the creation of a new instance (not the mutation of an existing instance).
How is it still possible, then, that our implementation of scale changes the
actual parameter sent by the caller?
C-1.17 Had we implemented the scale function (page 25) as follows, does it work
properly?
def scale(data, factor):
for val in data:
val = factor
Explain why or why not.
C-1.18 Demonstrate how to use Python’s list comprehension syntax to produce
the list [0, 2, 6, 12, 20, 30, 42, 56, 72, 90].
C-1.19 Demonstrate how to use Python’s list comprehension syntax to produce
the list [ a , b , c , ..., z ], but without having to type all 26 such
characters literally.
C-1.20 Python’s random module includes a function shuffle(data) that accepts a
Copyright © 2013. Wiley. All rights reserved.
list of elements and randomly reorders the elements so that each possi-
ble order occurs with equal probability. The random module includes a
more basic function randint(a, b) that returns a uniformly random integer
from a to b (including both endpoints). Using only the randint function,
implement your own version of the shuffle function.
C-1.21 Write a Python program that repeatedly reads lines from standard input
until an EOFError is raised, and then outputs those lines in reverse order
(a user can indicate end of input by typing ctrl-D).
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
1.12. Exercises 53
C-1.22 Write a short Python program that takes two arrays a and b of length n
storing int values, and returns the dot product of a and b. That is, it returns
an array c of length n such that c[i] = a[i] · b[i], for i = 0, . . . , n − 1.
C-1.23 Give an example of a Python code fragment that attempts to write an ele-
ment to a list based on an index that may be out of bounds. If that index
is out of bounds, the program should catch the exception that results, and
print the following error message:
“Don’t try buffer overflow attacks in Python!”
C-1.24 Write a short Python function that counts the number of vowels in a given
character string.
C-1.25 Write a short Python function that takes a string s, representing a sentence,
and returns a copy of the string with all punctuation removed. For exam-
ple, if given the string "Let s try, Mike.", this function would return
"Lets try Mike".
C-1.26 Write a short program that takes as input three integers, a, b, and c, from
the console and determines if they can be used in a correct arithmetic
formula (in the given order), like “a + b = c,” “a = b − c,” or “a ∗ b = c.”
tation of a function named norm such that norm(v, p) returns the p-norm
value of v and norm(v) returns the Euclidean norm of v. You may assume
that v is a list of numbers.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
54 Chapter 1. Python Primer
Projects
P-1.29 Write a Python program that outputs all possible strings formed by using
the characters c , a , t , d , o , and g exactly once.
P-1.30 Write a Python program that can take a positive integer greater than 2 as
input and write out the number of times one must repeatedly divide this
number by 2 before getting a value less than 2.
P-1.31 Write a Python program that can “make change.” Your program should
take two numbers as input, one that is a monetary amount charged and the
other that is a monetary amount given. It should then return the number
of each kind of bill and coin to give back as change for the difference
between the amount given and the amount charged. The values assigned
to the bills and coins can be based on the monetary system of any current
or former government. Try to design your program so that it returns as
few bills and coins as possible.
P-1.32 Write a Python program that can simulate a simple calculator, using the
console as the exclusive input and output device. That is, each input to the
calculator, be it a number, like 12.34 or 1034, or an operator, like + or =,
can be done on a separate line. After each such input, you should output
to the Python console what would be displayed on your calculator.
P-1.33 Write a Python program that simulates a handheld calculator. Your pro-
gram should process input from the Python console representing buttons
that are “pushed,” and then output the contents of the screen after each op-
eration is performed. Minimally, your calculator should be able to process
the basic arithmetic operations and a reset/clear operation.
P-1.34 A common punishment for school children is to write out a sentence mul-
tiple times. Write a Python stand-alone program that will write out the
following sentence one hundred times: “I will never spam my friends
again.” Your program should number each of the sentences and it should
make eight different random-looking typos.
P-1.35 The birthday paradox says that the probability that two people in a room
will have the same birthday is more than half, provided n, the number of
Copyright © 2013. Wiley. All rights reserved.
people in the room, is more than 23. This property is not really a paradox,
but many people find it surprising. Design a Python program that can test
this paradox by a series of experiments on randomly generated birthdays,
which test this paradox for n = 5, 10, 15, 20, . . . , 100.
P-1.36 Write a Python program that inputs a list of words, separated by white-
space, and outputs how many times each word appears in the list. You
need not worry about efficiency at this point, however, as this topic is
something that will be addressed later in this book.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.
Chapter Notes 55
Chapter Notes
The official Python Web site (http://www.python.org) has a wealth of information, in-
cluding a tutorial and full documentation of the built-in functions, classes, and standard
modules. The Python interpreter is itself a useful reference, as the interactive command
help(foo) provides documentation for any function, class, or module that foo identifies.
Books providing an introduction to programming in Python include titles authored by
Campbell et al. [22], Cedar [25], Dawson [32], Goldwasser and Letscher [43], Lutz [72],
Perkovic [82], and Zelle [105]. More complete reference books on Python include titles by
Beazley [12], and Summerfield [91].
Copyright © 2013. Wiley. All rights reserved.
Goodrich, Michael T., et al. Data Structures and Algorithms in Python, Wiley, 2013. ProQuest Ebook Central, http://ebookcentral.proquest.com/lib/unimelb/detail.action?docID=4946360.
Created from unimelb on 2025-03-02 00:34:28.