Python - Coding The Matrix

Download as pdf or txt
Download as pdf or txt
You are on page 1of 20

python lab

Coding the Matrix, 2015

For auto-graded problems, edit the file python lab.py to include your solution.

1 Lab: Introduction to Python—sets, lists, dictionaries, and comprehen-


sions

Python http://xkcd.com/353/

We will be writing all our code in Python (Version 3.x). In writing Python code, we emphasize the use
of comprehensions, which allow one to express computations over the elements of a set, list, or dictionary
without a traditional for-loop. Use of comprehensions leads to more compact and more readable code, code
that more clearly expresses the mathematical idea behind the computation being expressed. Comprehensions

1
might be new to even some readers who are familiar with Python, and we encourage those readers to at least
skim the material on this topic.
To start Python, simply open a console (also called a shell or a terminal or, under Windows, a “Command
Prompt” or “MS-DOS Prompt”), and type python3 (or perhaps just python) to the console (or shell or
terminal or Command Prompt) and hit the Enter key. After a few lines telling you what version you are
using (e.g., Python 3.4.1), you should see >>> followed by a space. This is the prompt; it indicates that Python
is waiting for you to type something. When you type an expression and hit the Enter key, Python evaluates
the expression and prints the result, and then prints another prompt. To get out of this environment, type
quit() and Enter, or Control-D. To interrupt Python when it is running too long, type Control-C.
This environment is sometimes called a REPL, an acronym for “read-eval-print loop.” It reads what you
type, evaluates it, and prints the result if any. In this assignment, you will interact with Python primarily
through the REPL. In each task, you are asked to come up with an expression of a certain form.
There are two other ways to run Python code. You can import a module from within the REPL, and you
can run a Python script from the command line (outside the REPL). We will discuss modules and importing
in the next lab assignment. This will be an important part of your interaction with Python.
To submit your work on an assignment, you will have to edit the stencil for that assignment, in this case
python lab.py. You need to use a text editor for this purpose. If you are an experienced programmer (as
required for this course), you likely have a favorite text editor. You should use a text editor that supports
Python. As we will see, whitespace indentation (how many spaces or tabs appear in your file in each line)
is significant for Python. In this course, we follow this convention: no tabs, and each level of indentation is
represented by four spaces. A good text editor will have a Python mode that helps you manage indentation.
Your text editor should be configured to insert four spaces when you hit the tab key. Some text editors
handle this better than others. Some use Vim. I use ... Emacs.

Real Programmers http://xkcd.com/378/

2
1 Simple expressions
1.1 Arithmetic and numbers
You can use Python as a calculator for carrying out arithmetic computations. The binary operators +, *,
-, / work as you would expect. To take the negative of a number, use - as a unary operator (as in -9).
Exponentiation is represented by the binary operator **, and truncating integer division is //. Finding the
remainder when one integer is divided by another (modulo) is done using the % operator. As usual, **
has precedence over * and / and //, which have precedence over + and -, and parentheses can be used for
grouping.
To get Python to carry out a calculation, type the expression and press the Enter/Return key:

>>> 44+11*4-6/11.
87.454545454545454
>>>
Python prints the answer and then prints the prompt again.

Task 1: Use Python to find the number of minutes in a week.

Task 2: Use Python to find the remainder of 2304811 divided by 47 without using the modulo operator %.
(Hint: Use //.)

Python uses a traditional programming notation for scientific notation. The notation 6.022e23 denotes
the value 6.02 × 1023 , and 6.626e-34 denotes the value 6.626 × 10−34 . As we will discover, since Python
uses limited-precision arithmetic, there are round-off errors:
>>> 1e16 + 1
1e16

The value of the expression float(’infinity’) is infinity:


>>> min(6.02e23, float('infinity'))
6.02e+23

1.2 Strings
A string is a series of characters that starts and ends with a single-quote mark. Enter a string, and Python
will repeat it back to you:
>>> 'This sentence is false.'
'This sentence is false.'
You can also use double-quote marks; this is useful if your string itself contains a single-quote mark:

>>> "So's this one."


"So's this one."
Python is doing what it usually does: it evaluates (finds the value of) the expression it is given and prints
the value. The value of a string is just the string itself.

3
1.3 Comparisons and conditions and Booleans
You can compare values (strings and numbers, for example) using the operators ==, < , >, <=, >=, and
!=. (The operator != is inequality.)
>>> 5 == 4
False
>>> 4 == 4
True
The value of such a comparison is a Boolean value (True or False).

>>> type(5 == 4)
<class 'bool'>
An expression whose value is a boolean is called a Boolean expression.
Boolean operators such as and and or and not can be used to form more complicated Boolean expressions.

>> True and False


False
>>> True and not (5 == 4)
True

Task 3: Enter a Boolean expression to test whether the sum of 673 and 909 is divisible by 3.

2 Assignment statements
The following is a statement, not an expression. Python executes it but produces neither an error message
nor a value.

>>> mynum = 4+1


The result is that henceforth the variable mynum is bound to the value 5. Consequently, when Python
evaluates the expression consisting solely of mynum, the resulting value is 5. We say therefore that the value
of mynum is 5.
A bit of terminology: the variable being assigned to is called the left-hand side of an assignment, and the
expression whose value is assigned is called the right-hand side.
A variable name must start with a letter and must exclude certain special symbols such as the dot
(period). The underscore is allowed in a variable name. A variable can be bound to a value of any type.
You can rebind mynum to a string:
>>> mynum = 'Brown'

This binding lasts until you assign some other value to mynum or until you end your Python session. It is
called a top-level binding. We will encounter cases of binding variables to values where the bindings are
temporary.
It is important to remember (and second nature to most experienced programmers) that an assignment
statement binds a variable to the value of an expression, not to the expression itself. Python first evaluates
the right-hand side and only then assigns the resulting value to the left-hand side. This is the behavior of
most programming languages.
Consider the following assignments.

4
>>> x = 5+4
>>> y = 2 * x
>>> y
18
>>> x = 12
>>> y
18
In the second assignment, y is assigned the value of the expression 2 * x. The value of that expression is 9,
so y is bound to 18. In the third assignment, x is bound to 12. This does not change the fact that y is bound
to 18.

3 Conditional expressions
There is a syntax for conditional expressions:
hexpressioni if hconditioni else hexpressioni

The condition should be a Boolean expression. Python evaluates the condition; depending on whether it is
True or False, Python then evaluates either the first or second expression, and uses the result as the result
of the entire conditional expression.
For example, the value of the expression x if x>0 else -x is the absolute value of x.

Task 4: Assign the value -9 to x and 1/2 to y. Predict the value of the following expression, then enter it
to check your prediction:
2**(y+1/2) if x+10<0 else 2**(y-1/2)

4 Sets
Python provides some simple data structures for grouping together multiple values, and integrates them
with the rest of the language. These data structures are called collections. We start with sets.
A set is an unordered collection in which each value occurs at most once. You can use curly braces to
give an expression whose value is a set. Python prints sets using curly braces.

>>> {1+2, 3, "a"}


{'a', 3}
>>> {2, 1, 3}
{1, 2, 3}

Note that duplicates are eliminated and that the order in which the elements of the output are printed does
not necessarily match the order of the input elements.
The cardinality of a set S is the number of elements in the set. In Mathese we write |S| for the cardinality
of set S. In Python, the cardinality of a set is obtained using the procedure len(·).
>>> len({'a', 'b', 'c', 'a', 'a'})
3

4.1 Summing
The sum of elements of collection of values is obtained using the procedure sum(·).
>>> sum({1,2,3})
6

5
If for some reason (we’ll see one later) you want to start the sum not at zero but at some other value, supply
that value as a second argument to sum(·):
>>> sum({1,2,3}, 10)
16

4.2 Testing set membership


Membership in a set can be tested using the in operator and the not in operator. If S is a set, x in S is a
Boolean expression that evaluates to True if the value of x is a member of the set S, and False otherwise.
The value of a not in expression is just the opposite
>>> S={1,2,3}
>>> 2 in S
True
>>> 4 in S
False
>>> 4 not in S
True

4.3 Set union and intersection


The union of two sets S and T is a new set that contains every value that is a member of S or a member of
T (or both). Python uses the vertical bar | as the union operator:
>>> {1,2,3} | {2,3,4}
{1, 2, 3, 4}
The intersection of S and T is a new set that contains every value that is a member of both S and T . Python
uses the ampersand & as the intersection operator:
>>> {1,2,3} & {2,3,4}
{2, 3}

4.4 Mutating a set


A value that can be altered is a mutable value. Sets are mutable; elements can be added and removed using
the add and remove methods:

>>> S={1,2,3}
>>> S.add(4)
>>> S.remove(2)
>>> S
{1, 3, 4}

The syntax using the dot should be familiar to students of object-oriented programming languages such
as Java and C++. The operations add(·) and remove(·) are methods. You can think of a method as a
procedure that takes an extra argument, the value of the expression to the left of the dot.
Python provides a method update(...) to add to a set all the elements of another collection (e.g. a set
or a list):

>>> S.update({4, 5, 6})


>>> S
{1, 3, 4, 5, 6}

6
Similarly, one can intersect a set with another collection, removing from the set all elements not in the other
collection:
>>> S.intersection_update({5,6,7,8,9})
>>> S
{5, 6}
Suppose two variables are bound to the same value. A mutation to the value made through one variable
is seen by the other variable.
>>> T=S
>>> T.remove(5)
>>> S
{6}
This behavior reflects the fact that Python stores only one copy of the underlying data structure. After
Python executes the assignment statement T=S, both T and S point to the same data structure. This aspect
of Python will be important to us: many different variables can point to the same huge set without causing
a blow-up of storage requirements.
Python provides a method for copying a collection such as a set:
>>> U=S.copy()
>>> U.add(5)
>>> S
{6}
The assignment statement binds U not to the value of S but to a copy of that value, so mutations to the
value of U don’t affect the value of S.

4.5 Set comprehensions


Python provides for expressions called comprehensions that let you build collections out of other collections.
We will be using comprehensions a lot because they are useful in constructing an expression whose value is
a collection, and they mimic traditional mathematical notation. Here’s an example:

>>> {2*x for x in {1,2,3} }


{2, 4, 6}

This is said to be a set comprehension over the set {1,2,3}. It is called a set comprehension because its
value is a set. The notation is similar to the traditional mathematical notation for expressing sets in terms
of other sets, in this case {2x : x ∈ {1, 2, 3}}. To compute the value, Python iterates over the elements
of the set {1,2,3}, temporarily binding the control variable x to each element in turn and evaluating the
expression 2*x in the context of that binding. Each of the values obtained is an element of the final set. (The
bindings of x during the evaluation of the comprehension do not persist after the evaluation completes.)

Task 5: Write a comprehension over {1, 2, 3, 4, 5} whose value is the set consisting of the squares of the
first five positive integers.

Task 6: Write a comprehension over {0, 1, 2, 3, 4} whose value is the set consisting of the first five powers
of two, starting with 20 .

Using the union operator | or the intersection operator &, you can write set expressions for the union or
intersection of two sets, and use such expressions in a comprehension:

7
>>> {x*x for x in S | {5, 7}}
{1, 25, 49, 9}
By adding the phrase if hconditioni at the end of the comprehension (before the closing brace “}”), you
can skip some of the values in the set being iterated over:

>>> {x*x for x in S | {5, 7} if x > 2}


{9, 49, 25}
I call the conditional clause a filter.
You can write a comprehension that iterates over the Cartesian product of two sets:
>>>{x*y for x in {1,2,3} for y in {2,3,4}}
{2, 3, 4, 6, 8, 9, 12}
This comprehension constructs the set of the products of every combination of x and y. I call this a double
comprehension.

Task 7: The value of the previous comprehension,


{x*y for x in {1,2,3} for y in {2,3,4}}
is a seven-element set. Replace {1,2,3} and {2,3,4} with two other three-element sets so that the value
becomes a nine-element set.

Here is an example of a double comprehension with a filter:


>>> {x*y for x in {1,2,3} for y in {2,3,4} if x != y}
{2, 3, 4, 6, 8, 12}

Task 8: Replace {1,2,3} and {2,3,4} in the previous comprehension with two disjoint (i.e. non-overlapping)
three-element sets so that the value becomes a five-element set.

Task 9: Assume that S and T are assigned sets. Without using the intersection operator &, write a compre-
hension over S whose value is the intersection of S and T. Hint: Use a membership test in a filter at the end
of the comprehension.
Try out your comprehension with S = {1,2,3,4} and T = {3,4,5,6}.

4.6 Remarks
The empty set is represented by set(). You would think that {} would work but, as we will see, that
notation is used for something else.
You cannot make a set that has a set as element. This has nothing to do with Cantor’s Paradox—Python
imposes the restriction that the elements of a set must not be mutable, and sets are mutable. The reason for
this restriction will be clear to a student of data structures from the error message in the following example:
>>> {{1,2},3}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

There is a nonmutable version of set called frozenset. Frozensets can be elements of sets. However, we won’t
be using them.

8
5 Lists
Python represents sequences of values using lists. In a list, order is significant and repeated elements are
allowed. The notation for lists uses square brackets instead of curly braces. The empy list is represented by
[].

>>> [1,1+1,3,2,3]
[1, 2, 3, 2, 3]
There are no restrictions on the elements of lists. A list can contain a set or another list.
>>> [[1,1+1,4-1],{2*2,5,6}, "yo"]
[[1, 2, 3], {4, 5, 6}, 'yo']
However, a set cannot contain a list since lists are mutable.
The length of a list, obtained using the procedure len(·), is the number of elements in the list, even
though some of those elements may themselves be lists, and even though some elements might have the same
value:

>>> len([[1,1+1,4-1],{2*2,5,6}, "yo", "yo"])


4
As we saw in the section on sets, the sum of elements of a collection can be computed using sum(·)
>>> sum([1,1,0,1,0,1,0])
4
>>> sum([1,1,0,1,0,1,0], -9)
-5
In the second example, the second argument to sum(·) is the value to start with.

Task 10: Write an expression whose value is the average of the elements of the list [20, 10, 15, 75].

5.1 List concatenation


You can combine the elements in one list with the elements in another list to form a new list (without
changing the original lists) using the + operator.
>>> [1,2,3]+["my", "word"]
[1, 2, 3, 'my', 'word']
>>> mylist = [4,8,12]
>>> mylist + ["my", "word"]
[4, 8, 12, 'my', 'word']
>>> mylist
[4, 8, 12]

You can use sum(·) on a collection of lists, obtaining the concatenation of all the lists, by providing [] as
the second argument.
>>> sum([ [1,2,3], [4,5,6], [7,8,9] ])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>> sum([ [1,2,3], [4,5,6], [7,8,9] ], [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

9
5.2 List comprehensions
Next we discuss how to write a list comprehension (a comprehension whose value is a list). In the following
example, a list is constructed by iterating over the elements in a set.
>>> [2*x for x in {2,1,3,4,5} ]
[2, 4, 6, 8, 10]
Note that the order of elements in the resulting list might not correspond to the order of elements in the set
since the latter order is not significant.
You can also use a comprehension that constructs a list by iterating over the elements in a list:

>>> [ 2*x for x in [2,1,3,4,5] ]


[4, 2, 6, 8, 10]
Note that the list [2,1,3,4,5] specifies the order among its elements. In evaluating the comprehension
Python iterates through them in that order. Therefore the order of elements in the resulting list corresponds
to the order in the list iterated over.
You can also write list comprehensions that iterate over multiple collections using two control variables.
As I mentioned in the context of sets, I call these “double comprehensions”. Here is an example of a list
comprehension over two lists.
>>> [ x*y for x in [1,2,3] for y in [10,20,30] ]
[10, 20, 30, 20, 40, 60, 30, 60, 90]

The resulting list has an element for every combination of an element of [1,2,3] with an element of
[10,20,30].
We can use a comprehension over two sets to form the Cartesian product.

Task 11: Write a double list comprehension over the lists ['A','B','C'] and [1,2,3] whose value is the
list of all possible two-element lists [letter, number]. That is, the value is

[['A', 1], ['A', 2], ['A', 3], ['B', 1], ['B', 2],['B', 3],
['C', 1], ['C', 2], ['C', 3]]

Task 12: Suppose LofL has been assigned a list whose elements are themselves lists of numbers. Write an
expression that evaluates to the sum of all the numbers in all the lists. The expression has the form
sum([sum(...
and includes one comprehension. Test your expression after assigning [[.25, .75, .1], [-1, 0], [4,
4, 4, 4]] to LofL. Note that your expression should work for a list of any length.

10
5.3 Obtaining elements of a list by indexing

Donald Knuth http://xkcd.com/163/

There are two ways to obtain an individual element of a list. The first is by indexing. As in some other
languages (Java and C++, for example) indexing is done using square brackets around the index. Here is
an example. Note that the first element of the list has index 0.
>>> mylist[0]
4
>>> ['in','the','CIT'][1]
'the'

Slices: A slice of a list is a new list consisting of a consecutive subsequence of elements of the old list,
namely those indexed by a range of integers. The range is specified by a colon-separated pair i : j consisting
of the index i as the first element and j as one past the index of the last element. Thus mylist[1:3] is the
list consisting of elements 1 and 2 of mylist.

Prefixes: If the first element i of the pair is 0, it can be omitted, so mylist[:2] consists of the first 2
elements of mylist. This notation is useful for obtaining a prefix of a list.

Suffixes: If the second element j of the pair is the length of the list, it can be omitted, so mylist[1:]
consists of all elements of mylist except element 0.

>>> L = [0,10,20,30,40,50,60,70,80,90]
>>> L[:5]
[0, 10, 20, 30, 40]
>>> L[5:]
[50, 60, 70, 80, 90]

Slices that skip You can use a colon-separated triple a:b:c if you want the slice to include every cth
element. For example, here is how you can extract from L the list consisting of even-indexed elements and
the list consisting of odd-indexed elements:
>>> L[::2]
[0, 20, 40, 60, 80]

11
>>> L[1::2]
[10, 30, 50, 70, 90]

5.4 Obtaining elements of a list by unpacking


The second way to obtain individual elements is by unpacking. Instead of assigning a list to a single variable
as in mylist =[4,8,12], one can assign to a list of variables:

>>> [x,y,z] = [4*1, 4*2, 4*3]


>>> x
4
>>> y
8

I called the left-hand side of the assignment a “list of variables,” but beware: this is a notational fiction.
Python does not allow you to create a value that is a list of variables. The assignment is simply a convenient
way to assign to each of the variables appearing in the left-hand side.

Ungraded Task: Find out what happens if the length of the left-hand side list does not match the length
of the right-hand side list.

Unpacking can similarly be used in comprehensions:

>>> listoflists = [[1,1],[2,4],[3, 9]]


>>> [y for [x,y] in listoflists]
[1, 4, 9]
Here the two-element list [x,y] iterates over all elements of listoflists. This would result in an error
message if some element of listoflists were not a two-element list.

5.5 Mutating a list: indexing on the left-hand side of =


You can mutate a list, replacing its ith element, using indexing on the left-hand side of the =, analogous to
an assignment statement:
>>> mylist = [30, 20, 10]
>>> mylist[1] = 0
>>> mylist
[30, 0, 10]
Slices can also be used on the left-hand side but we will not use this.

6 Tuples
Like a list, a tuple is an ordered sequence of elements. However, tuples are immutable so they can be
elements of sets. The notation for tuples is the same as that for lists except that ordinary parentheses are
used instead of square brackets.
>>> (1,1+1,3)
(1, 2, 3)
>>> {0, (1,2)} | {(3,4,5)}
{(1, 2), 0, (3, 4, 5)}

12
6.1 Obtaining elements of a tuple by indexing and unpacking
You can use indexing to obtain an element of a tuple.
>>> mytuple = ("all", "my", "books")
>>> mytuple[1]
'my'
>>> (1, {"A", "B"}, 3.14)[2]
3.14
You can also use unpacking with tuples. Here is an example of top-level variable assignment:
>>> (a,b) = (1,5-3)
>>> a
1
In some contexts, you can get away without the parentheses, e.g.
>>> a,b = (1,5-3)
or even
>>> a,b = 1,5-3
You can use unpacking in a comprehension:
>>> [y for (x,y) in [(1,'A'),(2,'B'),(3,'C')] ]
['A', 'B', 'C']

Task 13: Suppose S is a set of integers, e.g. {−4, −2, 1, 2, 5, 0}. Write a triple comprehension whose value
is a list of all three-element tuples (i, j, k) such that i, j, k are elements of S whose sum is zero.

Task 14: Modify the comprehension of the previous task so that the resulting list does not include (0, 0, 0).
Hint: add a filter.

Task 15: Further modify the expression so that its value is not the list of all such tuples but is the first
such tuple.

The previous task provided a way to compute three elements i, j, k of S whose sum is zero—if there exist
three such elements. Suppose you wanted to determine if there were a hundred elements of S whose sum is
zero. What would go wrong if you used the approach used in the previous task? Can you think of a clever
way to quickly and reliably solve the problem, even if the integers making up S are very large? (If so, see
me immediately to collect your Ph.D.)

6.2 Obtaining a list or set from another collection


Python can compute a set from another collection (e.g. a list) using the constructor set(·). Similarly, the
constructor list(·) computes a list, and the constructor tuple(·) computes a tuple
>>> set([0,1,2,3,4,5,6,7,8,9])
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> set([1,2,3])
{1, 2, 3}

13
>>> list({1,2,3})
[1, 2, 3]
>>> set((1,2,3))
{1, 2, 3}

Task 16: Find an example of a list L such that len(L) and len(list(set(L))) are different.

7 Other things to iterate over


7.1 Tuple comprehensions—not! Generators
One would expect to be able to create a tuple using the usual comprehension syntax, e.g. (i for i in [1,2,3])
but the value of this expression is not a tuple. It is a generator. Generators are a very powerful feature of
Python but we don’t study them here. Note, however, that one can write a comprehension over a generator
instead of over a list or set or tuple. Alternatively, one can use set(·) or list(·) or tuple(·) to transform
a generator into a set or list or tuple.

7.2 Ranges
A range plays the role of a list consisting of the elements of an arithmetic progression. For any integer n,
range(n) represents the sequence of integers from 0 through n − 1. For example, range(10) represents the
integers from 0 through 9. Therefore, the value of the following comprehension is the sum of the squares of
these integers: sum({i*i for i in range(10)}).
Even though a range represents a sequence, it is not a list. Generally we will either iterate through the
elements of the range or use set(·) or list(·) to turn the range into a set or list.

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Task 17: Write a comprehension over a range of the form range(n) such that the value of the compre-
hension is the set of odd numbers from 1 to 99.

You can form a range with one, two, or three arguments. The expression range(a,b) represents the
sequence of integers a, a + 1, a + 2, . . . , b − 1. The expression range(a,b,c) represents a, a + c, a + 2c, . . .
(stopping just before b).

7.3 Zip
Another collection that can be iterated over is a zip. A zip is constructed from other collections all of the
same length. Each element of the zip is a tuple consisting of one element from each of the input collections.
>>> list(zip([1,3,5],[2,4,6]))
[(1, 2), (3, 4), (5, 6)]
>>> characters = ['Neo', 'Morpheus', 'Trinity']
>>> actors = ['Keanu', 'Laurence', 'Carrie-Anne']
>>> set(zip(characters, actors))
{('Trinity', 'Carrie-Anne'), ('Neo', 'Keanu'), ('Morpheus', 'Laurence')}
>>> [character+' is played by '+actor
... for (character,actor) in zip(characters,actors)]
['Neo is played by Keanu', 'Morpheus is played by Laurence',
'Trinity is played by Carrie-Anne']

14
Task 18: Assign to L the list consisting of the first five letters ['A','B','C','D','E']. Next, use L in
an expression whose value is
[(0, ’A’), (1, ’B’), (2, ’C’), (3, ’D’), (4, ’E’)]
Your expression should use a range and a zip, but should not use a comprehension.

Task 19: Starting from the lists [10, 25, 40] and [1, 15, 20], write a comprehension whose value is
the three-element list in which the first element is the sum of 10 and 1, the second is the sum of 25 and 15,
and the third is the sum of 40 and 20. Your expression should use zip but not list.

7.4 reversed
To iterate through the elements of a list L in reverse order, use reversed(L), which does not change the list
L:
>>> [x*x for x in reversed([4, 5, 10])]
[100, 25, 16]

8 Dictionaries
We will often have occasion to use functions with finite domains. Python provides collections, called dic-
tionaries, that are suitable for representing such functions. Conceptually, a dictionary is a set of key-value
pairs. The syntax for specifying a dictionary in terms of its key-value pairs therefore resembles the syntax
for sets—it uses curly braces—except that instead of listing the elements of the set, one lists the key-value
pairs. In this syntax, each key-value pair is written using colon notation: an expression for the key, followed
by the colon, followed by an expression for the value:

key : value

The function f that maps each letter in the alphabet to its rank in the alphabet could be written as

{'A':0, 'B':1, 'C':2, 'D':3, 'E':4, 'F':5, 'G':6, 'H':7, 'I':8,


'J':9, 'K':10, 'L':11, 'M':12, 'N':13, 'O':14, 'P':15, 'Q':16,
'R':17, 'S':18, 'T':19, 'U':20, 'V':21, 'W':22, 'X':23, 'Y':24,
'Z':25}
As in sets, the order of the key-value pairs is irrelevant, and the keys must be immutable (no sets or lists or
dictionaries). For us, the keys will mostly be integers, strings, or tuples of integers and strings.
The keys and values can be specified with expressions.
>>> {2+1:'thr'+'ee', 2*2:'fo'+'ur'}
{3: 'three', 4: 'four'}
To each key in a dictionary there corresponds only one value. If a dictionary is given multiple values for the
same key, only one value will be associated with that key.

>>> {0:'zero', 0:'nothing'}


{0: 'nothing'}

8.1 Indexing into a dictionary


Obtaining the value corresponding to a particular key uses the same syntax as indexing a list or tuple: right
after the dictionary expression, use square brackets around the key:

15
>>> {4:"four", 3:'three'}[4]
'four'
>>> mydict = {'Neo':'Keanu', 'Morpheus':'Laurence',
'Trinity':'Carrie-Anne'}
>>> mydict['Neo']
'Keanu'
If the key is not represented in the dictionary, Python considers it an error:
>>> mydict['Oracle']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Oracle'

8.2 Testing dictionary membership


You can check whether a key is in a dictionary using the in operator we earlier used for testing membership
in a set:
>>> 'Oracle' in mydict
False
>>> mydict['Oracle'] if 'Oracle' in mydict else 'NOT PRESENT'
'NOT PRESENT'
>>> mydict['Neo'] if 'Neo' in mydict else 'NOT PRESENT'
'Keanu'

8.3 Lists of dictionaries


Task 20: Suppose dlist is a list of dictionaries and k is a key that appears in all the dictionaries in dlist.
Write a comprehension that evaluates to the list whose ith element is the value corresponding to key k in
the ith dictionary in dlist.
Test your comprehension with some data. Here are some example data.
dlist = [{'James':'Sean', 'director':'Terence'}, {'James':'Roger',
'director':'Lewis'}, {'James':'Pierce', 'director':'Roger'}]
k = 'James'

For these data, the value corresponding to k in the first dictionary is ’Sean’, the value corresponding to k
in the second dictionary is ’Roger’, and the value corresponding to k in the third dictionary is ’Pierce’,
so the comprehension should evaluate to [’Sean’,’Roger’,’Pierce’].

Task 21: Modify the comprehension in Task 20 to handle the case in which k might not appear in all the
dictionaries. The comprehension evaluates to the list whose ith element is the value corresponding to key k
in the ith dictionary in dlist if that dictionary contains that key, and 'NOT PRESENT' otherwise. One way
to solve this is to use a conditional expression. Another way is to use the .get(key, default) method of
dictionaries.
Test your comprehension with k = 'Bilbo' and k = 'Frodo' and with the following list of dictionaries:

dlist = [{'Bilbo':'Ian','Frodo':'Elijah'},
{'Bilbo':'Martin','Thorin':'Richard'}]
For example, with k = Frodo', the first dictionary in the list maps ’Frodo’ to ’Elijah’, and the sec-
ond dictionary in the list does not map ’Frodo’ to anything, so the comprehension should evaluate to

16
[’Elijah’,’NOT PRESENT’].

8.4 Mutating a dictionary: indexing on the left-hand side of =


You can mutate a dictionary, mapping a (new or old) key to a given value, using the syntax used for assigning
a list element, namely using the index syntax on the left-hand side of an assignment:

>>> mydict['Agent Smith'] = 'Hugo'


>>> mydict['Neo'] = 'Philip'
>>> mydict
{'Neo': 'Philip', 'Agent Smith': 'Hugo', 'Trinity': 'Carrie-Anne',
'Morpheus': 'Laurence'}

8.5 Dictionary comprehensions


You can construct a dictionary using a comprehension.
>>> { k:v for (k,v) in [(3,2),(4,0),(100,1)] }
{3: 2, 4: 0, 100: 1}
>>> { (x,y):x*y for x in [1,2,3] for y in [1,2,3] }
{(1, 2): 2, (3, 2): 6, (1, 3): 3, (3, 3): 9, (3, 1): 3,
(2, 1): 2, (2, 3): 6, (2, 2): 4, (1, 1): 1}

Task 22: Using range, write a comprehension whose value is a dictionary. The keys should be the integers
from 0 to 99 and the value corresponding to a key should be the square of the key.

The identity function on a set D is the function with the following spec:
• input: an element x of D
• output: x
That is, the identity function simply outputs its input.

Task 23: Assign to the variable D the set {'red','white','blue'}. Now write a comprehension that
evaluates to a dictionary that represents the identity function on D.

Task 24: Our system for writing numbers uses decimal notation. For example, the digits (2, 1, 5) represent
the number 2 × 102 + 1 · 101 + 5 · 100 . We say for this system that the base is 10, and that the available
digits are 0, 1, 2, . . . , 9.
In binary, the digits (1, 0, 1) represent the number 1 × 22 + 0 · 21 + 1 · 20 . In this case, the base is 2, and
the available digits are 0, 1.
Write a dictionary comprehension using the variables base and digits that evaluates to a dictionary
that maps each three-digit number to the three digits that represent it. For example, if base = 10 then
digits should be the set {0,1,2,..., 9} and the comprehension should evaluate to

{0:(0,0,0), 1:(0,0,1), ..., 999:(9,9,9)}


If base = 2 then digits should be the set {0,1}, and the comprehension should evaluate to
{0:(0,0,0), 1:(0,0,1), 2:(0,1,0),3:(0,1,1), 4:(1,0,0), ...}

17
8.6 Comprehensions that iterate over dictionaries
You can write list comprehensions that iterate over the keys or the values of a dictionary, using keys() or
values():
>>> [2*x for x in {4:'a',3:'b'}.keys() ]
[6, 8]
>>> [x for x in {4:'a', 3:'b'}.values()]
['b', 'a']
Given two dictionaries A and B, you can write comprehensions that iterate over the union or intersection
of the keys, using the union operator | and intersection operator & we learned about in Section 4.3.
>>> [k for k in {'a':1, 'b':2}.keys() | {'b':3, 'c':4}.keys()]
['a', 'c', 'b']
>>> [k for k in {'a':1, 'b':2}.keys() & {'b':3, 'c':4}.keys()]
['b']
Often you’ll want a comprehension that iterates over the (key, value) pairs of a dictionary, using items().
Each pair is a tuple.
>>> [myitem for myitem in mydict.items()]
[('Neo', 'Philip'), ('Morpheus', 'Laurence'),
('Trinity', 'Carrie-Anne'), ('Agent Smith', 'Hugo')]
Since the items are tuples, you can access the key and value separately using unpacking:
>>> [k + " is played by " + v for (k,v) in mydict.items()]
['Neo is played by Philip, 'Agent Smith is played by Hugo',
'Trinity is played by Carrie-Anne', 'Morpheus is played by Laurence']
>>> [2*k+v for (k,v) in {4:0,3:2, 100:1}.items() ]
[8, 8, 201]

Task 25: Suppose id2salary is a dictionary that maps some employee IDs (a subset of the integers from
0 to n − 1) to salaries. Suppose L is an n-element list whose ith element is the name of employee number i.
Your goal is to write a comprehension whose value is a dictionary mapping employee names to salaries. You
can assume that employee names are distinct.
Test your comprehension with the following data:

id2salary = {0:1000.0, 3:990, 1:1200.50}


names = ['Larry', 'Curly', '', 'Moe']

9 Defining one-line procedures


The procedure twice : R −→ R that returns twice its input can be written in Python as follows:
def twice(z): return 2*z
The word def introduces a procedure definition. The name of the function being defined is twice. The
variable z is called the formal argument to the procedure. Once this procedure is defined, you can invoke it
using the usual notation: the name of the procedure followed by an expression in parenthesis, e.g. twice(1+2)
The value 3 of the expression 1+2 is the actual argument to the procedure. When the procedure is
invoked, the formal argument (the variable) is temporarily bound to the actual argument, and the body of
the procedure is executed. At the end, the binding of the actual argument is removed. (The binding was
temporary.)

18
Ungraded Task: Try entering the definition of twice(z). After you enter the definition, you will see the
ellipsis. Just press enter. Next, try invoking the procedure on some actual arguments. Just for fun, try
strings or lists. Finally, verify that the variable z is now not bound to any value by asking Python to evaluate
the expression consisting of z.

Task 26: Define a one-line procedure nextInts(L) specified as follows:


• input: list L of integers
• output: list of integers whose ith element is one more than the ith element of L

• example: input [1, 5, 7], output [2, 6, 8].

Task 27: Define a one-line procedure cubes(L) specified as follows:


• input: list L of numbers

• output: list of numbers whose ith element is the cube of the ith element of L
• example: input [1, 2, 3], output [1, 8, 27].

Task 28: Define a one-line procedure dict2list(dct,keylist) with this spec:

• input: dictionary dct, list keylist consisting of the keys of dct


• output: list L such that L[i] = dct[keylist[i]] for i = 0, 1, 2, . . . , len(keylist) − 1
• example: input dct={'a':'A', 'b':'B', 'c':'C'} and keylist=['b','c','a'],
output ['B', 'C', 'A']

Task 29: Define a one-line procedure list2dict(L, keylist) specified as follows:

• input: list L, list keylist of immutable items


• output: dictionary that maps keylist[i] to L[i] for i = 0, 1, 2, . . . , len(L) − 1
• example: input L=[’A’,’B’,’C’] and keylist=[’a’,’b’,’c’],
output {'a':'A', 'b':'B', 'c':'C'}

Hint: Use a comprehension that iterates over a zip or a range.

Ungraded Task: Write a procedure all 3 digit numbers(base, digits) with the following spec:
• input: a positive integer base and the set digits which should be {0, 1, 2, . . . , base − 1}.
• output: the set of all three-digit numbers where the base is base

For example,
>>> all_3_digit_numbers(2, {0,1})

19
{0, 1, 2, 3, 4, 5, 6, 7}
>>> all_3_digit_numbers(3, {0,1,2})
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26}
>>> all_3_digit_numbers(10, {0,1,2,3,4,5,6,7,8,9})
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
...
985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999}

20

You might also like