Python,
Week 4
Lists: creating them, indexing and slicing, adding and removing items, and the classic list-based programs that show up again and again.
Chapters
Lists: Creating, Indexing and Slicing
What a list is, how to reach its items, how to take slices, and how to measure its length.
1.1The Python list
A Python list is an ordered container. A list is created using square brackets [ ]. Objects are placed inside those brackets, separated by commas. A list can contain mixed data types.
l = [1, 2, 3]
print(l)[1, 2, 3]x = ['dog', 21, True]
print(x)['dog', 21, True]- Ordered means the items keep the order you put them in.
- Empty list (extra).
[]is a list with no items. You will start many programs with it and fill it byappend. - Mutable (extra). A list can be changed after it is made (Chapter 2): items can be replaced, added and removed.
e = []
print(e)
print(len(e))[]
01.2Indexing
Indexing is used to access the items of a list. It uses square brackets and numbers to access individual items. 0 refers to the first item, 1 to the second item, and so on.
l = [1, 2, 3]
print(l[0], l[1], l[2])1 2 3l[0], not l[1]. A list of 3 items has the indexes 0, 1, 2, so the last valid index is len(l) - 1.Going past the end (extra). Asking for an index that does not exist is an error:
l = [1, 2, 3]
print(l[3])IndexError: list index out of range1.3Negative indexing
Negative indexing accesses the items using negative numbers: -1 is the last item, -2 the second-to-last, and so on.
l = [2, 4, 6, 8]
print(l[-1])
print(l[-2])8
6| Index | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| Positive | 0 | 1 | 2 | 3 |
| Negative | -4 | -3 | -2 | -1 |
| Item of [2, 4, 6, 8] | 2 | 4 | 6 | 8 |
l[-1]is the quickest way to get the last item, whatever the length.l[len(l) - 1]means the same.- Extra. For a list of n items the valid negative indexes are
-1to-n;l[-n]is the first item. Anything beyond is anIndexError.
l = [2, 4, 6, 8]
print(l[-4], l[0], l[-1] == l[len(l) - 1])2 2 True1.4Range of indexes (slicing)
By using a colon : we can access a range of items at once. This is called slicing. Separate two indexes with the colon: the first index is the start of the range and the second is the end of the range, not included.
- If the first index is not specified, the range starts from index
0. - If the last index is not specified, the range ends with the last item of the list.
l = [1, 2, 3, 4, 5]
x = l[2:5]
y = l[:4]
z = l[2:]
print(x, y, z)[3, 4, 5] [1, 2, 3, 4] [3, 4, 5]| Slice of l = [1, 2, 3, 4, 5] | Result | Why |
|---|---|---|
| l[1:3] | [2, 3] | items at index 1 and 2 |
| l[:2] | [1, 2] | from the start, stop before index 2 |
| l[3:] | [4, 5] | from index 3 to the end |
| l[:] | [1, 2, 3, 4, 5] | both ends open: a copy of the whole list |
| l[-2:] | [4, 5] | the last two items |
| l[2:2] | [] | start equals stop: empty |
| l[::2] | [1, 3, 5] | every second item (extra: a step) |
| l[::-1] | [5, 4, 3, 2, 1] | reversed (extra: negative step) |
[1, 2, 3][1:10] gives [2, 3]. A slice is also a new list; slicing does not change the original.l = [1, 2, 3]
print(l[1:10])
m = l[:2]
m[0] = 99
print(m, l)[2, 3]
[99, 2] [1, 2, 3]1.5Getting the length
To get the length, or the number of items in a list, use the len() function.
l = [4, 8, 2, 1]
print(len(l))4- The last item is at index
len(l) - 1, and a loop over every index isrange(len(l)). len([])is0; a nested list counts as one item:len([1, [2, 3]])is2.
print(len([]), len([1, [2, 3]]))0 21.6Going deeper
- Strings index and slice the same way (extra).
"python"[0]isp,"python"[-1]isn,"python"[1:4]isyth. What you learn here carries over to strings. - Lists inside lists (extra). An item can itself be a list.
m = [[1, 2], [3, 4]]hasm[0]equal to[1, 2]andm[1][0]equal to3. You will use this for matrices in Chapter 6.
print("python"[0], "python"[-1], "python"[1:4])
m = [[1, 2], [3, 4]]
print(m[0], m[1][0])p n yth
[1, 2] 31.7Exam tips and common mistakes
- Write the indexes under the list (0, 1, 2, ... and -n, ..., -1) before answering any indexing or slicing question.
- For
l[a:b]the answer hasb - aitems (when both are inside the list).
- Thinking the first item is
l[1]. - Including the end index in a slice:
l[2:5]stops before index 5. - Using
l[len(l)]for the last item (usel[-1]orl[len(l) - 1]).
Practice questions (graded-assignment style)
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [10, 20, 30, 40, 50]
print(l[-2])Negative indexes count from the right: -1 is the last, -2 the second last.
l = [10, 20, 30, 40, 50]
print(l[-2])40- The list has 5 items;
l[-2]is the second item from the end, which is40. - Answer: option 3.
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [1, 2, 3, 4, 5]
print(l[1:4])The stop index is not included.
l = [1, 2, 3, 4, 5]
print(l[1:4])[2, 3, 4]l[1:4]takes the items at indexes 1, 2, 3, which are2, 3, 4.- Answer: option 2.
Numerical Answer Type (NAT).
How many items does l[2:7] contain when l = list(range(10))?
l = list(range(10))
print(len(l[2:7]))Stop minus start.
l = list(range(10))
print(len(l[2:7]))57 - 2 = 5items: 2, 3, 4, 5, 6.- Answer: 5
Numerical Answer Type (NAT).
What is the sum of the items printed by the program?
l = [10, 20, 30, 40, 50]
print(sum(l[-2:]))l[-2:] is the last two items.
l = [10, 20, 30, 40, 50]
print(sum(l[-2:]))90l[-2:]is[40, 50], whose sum is90.- Answer: 90
Multiple Choice Question (MCQ): choose the one correct option.
What happens when this program runs?
l = [1, 2, 3]
print(l[3])Valid indexes are 0 to len(l) - 1.
l = [1, 2, 3]
print(l[3])IndexError: list index out of range- The list has 3 items, so the valid indexes are 0, 1, 2. Index 3 does not exist.
- Answer: option 3.
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements about Python lists.
Indexing starts at 0.
- Statement 3 is false: the first item is at index 0.
- Answer: options 1, 2 and 4.
Ordered, mixed types
[] is empty
l[-1] last item
Out of range: IndexError
Omit a: from the start
Omit b: to the end
Last index: len(l) - 1
l[:] copies the list
Reverse: l[::-1]
Changing a List
Changing, adding, deleting and checking items, and joining lists with extend.
A list can be changed after it is created. This chapter covers changing an item, adding items (append, insert), deleting items (pop, remove, del), checking membership with in, and joining lists with extend.
2.1Changing an item's value
To change an item's value, access the index first and use the assignment operator.
l = [1, 2, 8, 6, 12, 14]
l[3] = 9
print(l)[1, 2, 8, 9, 12, 14]- The list keeps its length: one item is replaced, nothing is added or removed.
- Extra. A slice can be assigned too:
l[1:3] = [0, 0]replaces two items at once. - Extra. Strings cannot be changed like this: they are immutable.
l = [1, 2, 3, 4]
l[1:3] = [0, 0]
print(l)[1, 0, 0, 4]s = "cat"
s[0] = "b"TypeError: 'str' object does not support item assignment2.2Adding items: append() and insert()
The append() method adds an item to the end of the list.
l = [2, 4, 6]
l.append(8)
print(l)[2, 4, 6, 8]The insert() method inserts an item at a specific index. The item that was there, and all the items after it, move one place to the right.
l = [2, 4, 6]
l.insert(0, 0)
l.insert(4, 8)
print(l)[0, 2, 4, 6, 8]| Step | Statement | List after |
|---|---|---|
| 0 | l = [2, 4, 6] | [2, 4, 6] |
| 1 | l.insert(0, 0) | [0, 2, 4, 6] |
| 2 | l.insert(4, 8) | [0, 2, 4, 6, 8] |
append(x)is the same asinsert(len(l), x).- Extra. An index past the end does not fail:
insert(100, x)just addsxat the end. - Extra.
appendandinsertchange the list and returnNone. Writingl = l.append(5)destroys the list.
l = [1, 2]
l.insert(100, 9)
print(l)
l = l.append(5)
print(l)[1, 2, 9]
None2.3Deleting items: pop(), remove(), del
| Tool | What it removes | Result for l = [2, 3, 4, 5] |
|---|---|---|
| l.pop() | the last item | [2, 3, 4] |
| l.remove(3) | the item with the given value (the first one) | [2, 4, 5] |
| del l[2] | the item at the given index | [2, 3, 5] |
l = [2, 3, 4, 5]
l.pop()
print(l)[2, 3, 4]l = [2, 3, 4, 5]
l.remove(3)
print(l)[2, 4, 5]l = [2, 3, 4, 5]
del l[2]
print(l)[2, 3, 5]remove(3) removes the value 3 (wherever it is); del l[3] removes the item at index 3. They are different.
- Extra: pop gives the item back.
x = l.pop()stores the removed item;l.pop(i)removes the item at indexi. - Extra: remove deletes only the first match.
[1, 2, 1].remove(1)leaves[2, 1]. If the value is missing it raises aValueError. - Extra: del can remove a slice.
del l[1:3]removes the items at indexes 1 and 2.
l = [5, 6, 7]
x = l.pop()
print(x, l)
m = [1, 2, 1]
m.remove(1)
print(m)
n = [1, 2, 3, 4, 5]
del n[1:3]
print(n)7 [5, 6]
[2, 1]
[1, 4, 5]l = [1, 2, 3]
l.remove(9)ValueError: list.remove(x): x not in list2.4Checking if an item exists: in
To check if an item exists in a list, use the in operator. It returns True or False if the item is found or not found, respectively.
pets = ['dog', 'cat', 'rabbit']
print('dog' in pets)
print('python' in pets)True
Falsex not in lis the opposite (extra).- The check compares with every item, so a long list takes longer (Chapter 4 explains linear search).
- Use
inbeforeremoveto avoid theValueError(extra).
l = [1, 2, 3]
x = 9
if x in l:
l.remove(x)
else:
print(x, "is not in the list")
print(3 not in l)9 is not in the list
False2.5Extending a list: extend()
The extend() method adds all the items from one list to another list.
l1 = [1, 2, 3]
l2 = [4, 5, 6]
l1.extend(l2)
print(l1)[1, 2, 3, 4, 5, 6]l1 = [1, 2, 3]
l2 = [4, 5, 6]
l1.append(l2)
print(l1)
print(len(l1))
l3 = [1, 2] + [3, 4]
print(l3)[1, 2, 3, [4, 5, 6]]
4
[1, 2, 3, 4]append(x) always adds one item. If x is a list, that list becomes a single item inside the list. To add its items one by one use extend (or +, which makes a new list).2.6Two names for one list (extra)
b = a does not copy a list. Both names point to the same list, so a change made through one shows up through the other. To get a separate copy use a[:] (or a.copy()).
a = [1, 2, 3]
b = a
b.append(4)
print(a)
c = a[:]
c.append(5)
print(a, c)[1, 2, 3, 4]
[1, 2, 3, 4] [1, 2, 3, 4, 5]2.7Going deeper
- Which tool when (extra). You know the value:
remove. You know the position:del l[i]orpop(i). You need the removed item back:pop. The last item only:pop(). - Cost (extra). Adding at the end is cheap; adding or removing near the front shifts every later item, so it takes longer on very long lists.
2.8Exam tips and common mistakes
- Redraw the list after every statement (a trace table) and watch the indexes shift after an insert or a delete.
appendadds one item,extendadds many: check whether the argument is a list.
l = l.append(x): the method returnsNone.- Using
remove(i)when you meant to delete the item at indexi(usedel l[i]). b = aand thinking it is a copy.insert(i, x)with the arguments the wrong way round (index first, then the value).
Practice questions (graded-assignment style)
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [2, 4, 6]
l.insert(0, 0)
l.insert(4, 8)
print(l)insert(index, item) puts the item at the index.
l = [2, 4, 6]
l.insert(0, 0)
l.insert(4, 8)
print(l)[0, 2, 4, 6, 8]insert(0, 0)puts0at the front;insert(4, 8)puts8at index 4, which is the end of the 4-item list.- Answer: option 1.
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [10, 20, 30, 40]
l.remove(30)
del l[0]
print(l)remove(30) removes the value 30; del l[0] removes the item at index 0.
l = [10, 20, 30, 40]
l.remove(30)
del l[0]
print(l)[20, 40]- After
remove(30)the list is[10, 20, 40]. - Then
del l[0]deletes the first item,10, leaving[20, 40]. - Answer: option 1.
Numerical Answer Type (NAT).
What does the program print?
l = [1, 2, 3]
l.append(4)
l.pop()
l.extend([8, 9])
print(len(l))Count what each statement adds or removes.
l = [1, 2, 3]
l.append(4)
l.pop()
l.extend([8, 9])
print(len(l))5- It starts with 3 items.
append(4)makes 4,pop()makes 3, andextend([8, 9])adds two items to make 5. - Answer: 5
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [1, 2, 3]
l.append([4])
print(l)append adds the list as one item.
l = [1, 2, 3]
l.append([4])
print(l)[1, 2, 3, [4]]- The list
[4]becomes one item, so the result is[1, 2, 3, [4]]. - Answer: option 2.
Numerical Answer Type (NAT).
What does the program print?
a = [1, 2]
b = a
b.append(3)
print(len(a))b = a shares the list.
a = [1, 2]
b = a
b.append(3)
print(len(a))3bandaare the same list, so theappendthroughbis seen througha: the length is 3.- Answer: 3
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements.
remove works on values, del on positions.
- Statement 2 is false:
remove(x)removes the first item whose value isx. - Answer: options 1, 3 and 4.
Looping Through a List
Visiting every item, and the patterns that use it: filter, count, total, largest, new list.
Looping through a list basically means accessing all its items one by one. The for loop is used to loop through a list.
3.1The for loop over a list
l = [1, 2, 3]
for i in l:
print(i)1
2
3- The loop variable (
iabove) is not an index: it holds the item itself, one per round. - The loop ends by itself after the last item. No counter and no stop test are needed.
for loop reads a list the same way.3.2Looping with the index
Sometimes you need the position as well as the item. Loop over the indexes with range(len(l)), or use a while loop with a counter (the same two ways Week 3 gave for numbers).
l = [10, 20, 30]
for i in range(len(l)):
print(i, l[i])0 10
1 20
2 30l = [10, 20, 30]
i = 0
while i < len(l):
print(i, l[i])
i += 10 10
1 20
2 30| Style | Use when |
|---|---|
| for x in l | you only need the items |
| for i in range(len(l)) | you need the index too, or you change l[i] |
| while i < len(l) | the step is not simply +1, or you stop early |
| for i, x in enumerate(l) | index and item together (extra) |
l = [10, 20, 30]
for i, x in enumerate(l):
print(i, x)0 10
1 20
2 303.3Patterns you will use again and again
Print only some items (a filter)
nums = [3, 12, 7, 18, 9, 25]
for n in nums:
if n > 10:
print(n)12
18
25Add up and average (an accumulator)
marks = [70, 85, 90, 65]
total = 0
for m in marks:
total += m
print(total)
print(total / len(marks))310
77.5Find the largest item and where it is
l = [7, 42, 5, 42, 9]
big = l[0]
pos = 0
for i in range(len(l)):
if l[i] > big:
big = l[i]
pos = i
print(big, pos)42 1Build a new list
squares = []
for n in range(1, 6):
squares.append(n * n)
print(squares)[1, 4, 9, 16, 25]sum(l), max(l), min(l). The loops above show how they work, and exams usually ask for the loop.l = [7, 42, 5, 42, 9]
print(sum(l), max(l), min(l))105 42 53.4A trap: changing a list while looping over it (extra)
Removing items from the list that a for loop is walking over makes the loop skip items, because the later items shift left under the loop's feet.
l = [2, 4, 6, 1]
for x in l:
if x % 2 == 0:
l.remove(x)
print(l)[4, 1]The even numbers were meant to be removed, but 4 survived: after 2 was removed, 4 moved to index 0 and the loop went on to index 1. The safe way is to build a new list of the items you want to keep:
l = [2, 4, 6, 1]
odds = []
for x in l:
if x % 2 != 0:
odds.append(x)
print(odds)[1]3.5Going deeper
- Strings are loopable too (extra).
for ch in "hello":visits the characters. The same loop idea works for any sequence. - Changing items in place (extra). To double every item, loop over the indexes:
for i in range(len(l)): l[i] = l[i] * 2. Changing the loop variablexdoes not change the list.
l = [1, 2, 3]
for x in l:
x = x * 2
print(l)
for i in range(len(l)):
l[i] = l[i] * 2
print(l)[1, 2, 3]
[2, 4, 6]3.6Reading a list from input, list comprehensions, tuples and bubble sort
A. Reading a whole list from one line of input
Programs often receive numbers on one line, such as 3 1 4 1 5. input() gives that line as one string; split() cuts it into a list of strings; each piece must then be converted:
line = "3 1 4" # what input() would return
parts = line.split()
print(parts) # strings
nums = []
for p in parts:
nums.append(int(p))
print(nums) # integers
print(sum(nums))
print([int(p) for p in line.split()]) # the same, in one line['3', '1', '4']
[3, 1, 4]
8
[3, 1, 4]B. List comprehensions (extra)
A list comprehension builds a list in one line: [expression for item in sequence if condition]. The if part is optional. It is exactly a loop that appends:
squares = [x * x for x in range(5)]
print(squares)
evens_squared = [x * x for x in range(5) if x % 2 == 0]
print(evens_squared)
# the equivalent loop for the third list
multiples = []
for x in range(1, 10):
if x % 3 == 0:
multiples.append(x)
print(multiples)
print(len([x for x in range(20) if x % 3 == 0])) # counting with a comprehension[0, 1, 4, 9, 16]
[0, 4, 16]
[3, 6, 9]
7- Read it as: "for each
xin the sequence, if the condition holds, putexpressionin the list". - The
iffilters; the expression at the front transforms.[x * x for x in range(5) if x % 2 == 0]keeps only the evenx, then squares them. - The original sequence is not changed. Use a normal loop when the logic needs several statements.
C. Tuples and swapping
- A tuple is like a list written with round brackets, but it cannot be changed after it is created.
t = (1, 2, 3), thent[0] = 9raises aTypeError. A tuple with one item needs a comma:(5,). - Tuples unpack:
x, y = (3, 4). This is also how a swap works:a, b = b, abuilds the tuple(b, a)first, then assigns, so no temporary variable is needed. The same trick swaps list items:l[i], l[j] = l[j], l[i].
t = (1, 2, 3)
t[0] = 9TypeError: 'tuple' object does not support item assignmentx, y = (3, 4)
print(x, y)
x, y = y, x # swap
print(x, y)
l = [1, 2, 3]
l[0], l[2] = l[2], l[0] # swap two list items
print(l)
print((5,)) # a one-item tuple needs the comma3 4
4 3
[3, 2, 1]
(5,)D. sort() changes the list and returns None
l.sort() sorts the list in place and returns None. sorted(l) leaves l alone and returns a new sorted list. The slip l = l.sort() destroys the list:
l = [3, 1, 2]
print(sorted(l)) # a new list
print(l) # l itself is unchanged
print(l.sort()) # sort returns None
print(l) # ... but l is now sorted[1, 2, 3]
[3, 1, 2]
None
[1, 2, 3]E. Bubble sort
Selection sort picks the smallest item for each position. Bubble sort instead walks along the list and swaps any neighbouring pair that is out of order. After one full pass the largest item has "bubbled" to the end, so each pass can stop one place earlier.
l = [5, 1, 4, 2]
n = len(l)
# one pass only
for i in range(n - 1):
if l[i] > l[i + 1]:
l[i], l[i + 1] = l[i + 1], l[i]
print(l) # 5 has bubbled to the end
# the full algorithm
for p in range(n - 1):
for i in range(n - 1 - p):
if l[i] > l[i + 1]:
l[i], l[i + 1] = l[i + 1], l[i]
print(l)[1, 4, 2, 5]
[1, 2, 4, 5]Step (first pass on [5, 1, 4, 2]) | Compare | List after |
|---|---|---|
| i = 0 | 5 > 1, swap | [1, 5, 4, 2] |
| i = 1 | 5 > 4, swap | [1, 4, 5, 2] |
| i = 2 | 5 > 2, swap | [1, 4, 2, 5] |
- A list of items needs at most passes. Both bubble sort and selection sort make about comparisons, so both are slow for long lists. Python's own
sort()is much faster. - Index
i + 1is why the inner loop stops atn - 1 - p: asking forl[i + 1]past the last item would raiseIndexError.
3.7Exam tips and common mistakes
- Name the pattern first: filter, count, total, largest, or build a new list. The loop shape follows from it.
- Start accumulators (
total = 0,count = 0) before the loop, never inside it.
- Using the loop variable as if it were an index:
for i in l: print(l[i]). - Resetting
total = 0inside the loop. - Removing items from the list you are looping over.
Practice questions (graded-assignment style)
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
nums = [3, 12, 7, 18, 9, 25]
for n in nums:
if n > 10:
print(n)The if filters the items.
nums = [3, 12, 7, 18, 9, 25]
for n in nums:
if n > 10:
print(n)12
18
25- Only the items greater than 10 are printed, one per line:
12,18,25. - Answer: option 1.
Numerical Answer Type (NAT).
What does the program print?
marks = [70, 85, 90, 65]
total = 0
for m in marks:
total += m
print(total)Add each mark to total.
marks = [70, 85, 90, 65]
total = 0
for m in marks:
total += m
print(total)31070 + 85 + 90 + 65 = 310.- Answer: 310
Numerical Answer Type (NAT).
What index does the program print?
l = [7, 42, 5, 42, 9]
big = l[0]
pos = 0
for i in range(len(l)):
if l[i] > big:
big = l[i]
pos = i
print(pos)The test is >, so a later equal value does not replace it.
l = [7, 42, 5, 42, 9]
big = l[0]
pos = 0
for i in range(len(l)):
if l[i] > big:
big = l[i]
pos = i
print(pos)1- The first
42is at index 1; the second42is not greater than it, so the position stays 1. - Answer: 1
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [1, 2, 3]
for x in l:
x = x * 2
print(l)x = x * 2 rebinds the loop variable only.
l = [1, 2, 3]
for x in l:
x = x * 2
print(l)[1, 2, 3]- The loop variable
xis a copy of each item. Doubling it does not touch the list, which stays[1, 2, 3]. - Answer: option 2.
Numerical Answer Type (NAT).
How many items are left in the list when the program ends?
l = [2, 4, 6, 1]
for x in l:
if x % 2 == 0:
l.remove(x)
print(len(l))Removing during the loop makes it skip.
l = [2, 4, 6, 1]
for x in l:
if x % 2 == 0:
l.remove(x)
print(len(l))2- The program prints
[4, 1]: two items are left, because4was skipped after2was removed. - Answer: 2
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements.
The loop variable is a name for the current item.
- Statement 3 is false: only
l[i] = ...changes the list. - Answer: options 1, 2 and 4.
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
print([x * x for x in range(6) if x % 2 == 1])[1, 9, 25][0, 4, 16][1, 3, 5][1, 4, 9, 16, 25]Filter first (if), then transform (the expression at the front).
print([x * x for x in range(6) if x % 2 == 1])[1, 9, 25]range(6)is0..5. The odd values are1, 3, 5; squared they are1, 9, 25.- Option 3 forgets to square. Option 2 uses the even values.
- Answer: option 1.
Numerical Answer Type (NAT).
What does the program print?
print(len([x for x in range(20) if x % 3 == 0]))The comprehension collects the multiples of 3 below 20; len counts them.
print(len([x for x in range(20) if x % 3 == 0]))7- The list is
[0, 3, 6, 9, 12, 15, 18], which has 7 items (note that0counts). - Answer: 7
Multiple Select Question (MSQ): select all that apply.
With l = [3, 1, 2], select all the true statements.
print(sorted(l)) shows [1, 2, 3] and leaves l as [3, 1, 2].print(l.sort()) shows [1, 2, 3].l = l.sort() makes l equal to None.l.sort(), the list l is [1, 2, 3].In-place methods return None; sorted() returns a new list.
l = [3, 1, 2]
print(sorted(l), l)
print(l.sort())
print(l)
l = l.sort()
print(l)[1, 2, 3] [3, 1, 2]
None
[1, 2, 3]
None- 1 ✓. 2 ✗ it prints
None. 3 ✓. 4 ✓. - Answer: options 1, 3 and 4.
Multiple Choice Question (MCQ): choose the one correct option.
What happens when this program runs?
t = (4, 5, 6)
t[1] = 0
print(t)(4, 0, 6).(4, 5, 6).TypeError.IndexError.Tuples cannot be changed after they are created.
t = (4, 5, 6)
t[1] = 0
print(t)TypeError: 'tuple' object does not support item assignment- Index
1exists, so it is not anIndexError; the assignment itself is not allowed. With a list ([4, 5, 6]) the same code would print[4, 0, 6]. - Answer: option 3.
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [4, 3, 1, 2]
for i in range(len(l) - 1):
if l[i] > l[i + 1]:
l[i], l[i + 1] = l[i + 1], l[i]
print(l)[1, 2, 3, 4][3, 1, 2, 4][3, 4, 1, 2][1, 2, 4, 3]A single pass bubbles the largest item to the end but does not fully sort.
l = [4, 3, 1, 2]
for i in range(len(l) - 1):
if l[i] > l[i + 1]:
l[i], l[i + 1] = l[i + 1], l[i]
print(l)[3, 1, 2, 4]- : , swap, giving
[3, 4, 1, 2]. : , swap,[3, 1, 4, 2]. : , swap,[3, 1, 2, 4]. - The 4 is at the end, but the rest still needs more passes.
- Answer: option 2.
Ends by itself
x is the item
while with a counter
enumerate (extra)
Largest and its place
Build a new list
sum(l), max(l), min(l)
x = x * 2 does not change l
l[i] = ...
Birthday Paradox and Naive Search
Two classic programs: finding a repeat among random numbers, and a linear search.
Two classic programs that use lists of random numbers: the birthday paradox, and a search through a big list. Both show why lists and loops are so powerful.
4.1Practice: the birthday paradox
Generate 30 random "birthdays" (a number between 1 and 365), sort them, then check whether any two are the same.
import random
l = []
# create an empty list
for i in range(30):
l.append(random.randint(1, 365))
# append a random number from 1 to 365, 30 times
l.sort()
print(l)
i = 0
flag = 0
while i < len(l) - 1:
if l[i] == l[i + 1]:
print("Repeat", l[i])
flag = 1
break
i += 1
if flag == 0:
print("There is no repetition")l = []starts an empty list, and 30 rounds ofl.append(random.randint(1, 365))fill it with random numbers.l.sort()puts the numbers in order. Now equal numbers are neighbours, so we never have to compare every pair: onlyl[i]withl[i + 1].- The
whileloop stops atlen(l) - 1because it looks one place ahead. On the first repeat it prints it, setsflag = 1and leaves withbreak. - If
flagis still 0 after the loop, no repeat was found.
random.seed(...) line is added after the import. A seed fixes the "random" sequence; the logic is the same.A run with a repeat
import random
random.seed(2)
l = []
# create an empty list
for i in range(30):
l.append(random.randint(1, 365))
# append a random number from 1 to 365, 30 times
l.sort()
print(l)
i = 0
flag = 0
while i < len(l) - 1:
if l[i] == l[i + 1]:
print("Repeat", l[i])
flag = 1
break
i += 1
if flag == 0:
print("There is no repetition")[15, 19, 19, 29, 44, 47, 82, 87, 109, 129, 138, 158, 164, 185, 187, 191, 195, 202, 221, 228, 239, 258, 261, 279, 298, 311, 311, 327, 343, 349]
Repeat 19A run with no repeat
import random
random.seed(1)
l = []
# create an empty list
for i in range(30):
l.append(random.randint(1, 365))
# append a random number from 1 to 365, 30 times
l.sort()
print(l)
i = 0
flag = 0
while i < len(l) - 1:
if l[i] == l[i + 1]:
print("Repeat", l[i])
flag = 1
break
i += 1
if flag == 0:
print("There is no repetition")[2, 12, 14, 15, 16, 33, 49, 53, 61, 69, 108, 118, 131, 137, 163, 195, 200, 222, 229, 231, 242, 250, 254, 278, 292, 303, 312, 333, 334, 357]
There is no repetitionWhy "paradox"? With only 30 people, a repeat sounds unlikely out of 365 days, but the chance is about 70.6%. With just 23 people it is already above 50% (about 50.7%). The reason is that 30 people make 30 × 29 / 2 = 435 pairs, and any one pair could match.
Check by simulation (extra). Repeat the experiment 10,000 times and count how often a repeat appears:
import random
random.seed(1)
count = 0
for t in range(10000):
l = []
for i in range(30):
l.append(random.randint(1, 365))
if len(set(l)) < 30:
count += 1
print(count / 10000)0.7034len(set(l)) < 30 asks "are there fewer different numbers than items?", which is another way to detect a repeat without sorting.4.2Practice: naive search in a list
Build a list of 100,000 random numbers, then repeatedly ask the user for a number and search for it by scanning the whole list.
import random as r
l = []
for i in range(100000):
l.append(r.randint(1, 1000000))
n = 0
while n > -1:
print("Enter a no., type -1 to exit")
n = int(input())
flag = 0
for i in range(len(l)):
if n == l[i]:
print("Found")
flag = 1
break
if flag == 0:
print("Not found")This is a linear search: it checks every element one by one, so it can take up to 100,000 comparisons for a list of that size. When the number is found, break stops the scan early; when it is not found, the whole list has been checked.
A run with the seed fixed (the number typed first is an item that is in the list, then 0 which never is, then -1 to exit):
import random as r
r.seed(5)
l = []
for i in range(100000):
l.append(r.randint(1, 1000000))
n = 0
while n > -1:
print("Enter a no., type -1 to exit")
n = int(input())
flag = 0
for i in range(len(l)):
if n == l[i]:
print("Found")
flag = 1
break
if flag == 0:
print("Not found")Enter a no., type -1 to exit
400109
Found
Enter a no., type -1 to exit
0
Not found
Enter a no., type -1 to exit
-1
Not foundn > -1, so the -1 typed to exit is still searched first, and the program prints Not found once more before it stops. Any negative number does the same. It is harmless, but worth knowing.Search for 25 in [41, 7, 93, 25, 60, 18, 77, 3] and count how many items are looked at.
l = [41, 7, 93, 25, 60, 18, 77, 3]
n = 25
count = 0
for x in l:
count += 1
if x == n:
break
print("looks:", count)looks: 4Items 41, 7 and 93 do not match, and the fourth item is the match, so 4 looks. If the number were absent, all 8 items would be looked at. For a list of n items that is n looks in the worst case, and about n/2 on average when the number is present.
- The
inoperator searches too (extra).n in ldoes the same linear scan for you in one short line. - Faster search needs order (extra). If the list is sorted, much faster methods exist that skip most of the items. That is a topic for later; here the point is the cost of the plain scan.
4.3Exam tips and common mistakes
- Sorting first makes the duplicate check easy: only neighbours need comparing.
- Use a flag (0/1) when a loop must report whether something was found, and test it after the loop.
- Running the neighbour loop up to
len(l)instead oflen(l) - 1(l[i + 1]then goes out of range). - Printing "Not found" inside the scan loop instead of after it: it would print for every non-matching item.
- Forgetting
flag = 0before each new search.
Practice questions (graded-assignment style)
Multiple Choice Question (MCQ): choose the one correct option.
Why does the birthday program call l.sort() before checking for a repeat?
Think about which pairs you still have to compare.
- After sorting, equal values sit side by side, so only
l[i]againstl[i + 1]has to be checked. - Answer: option 1.
Numerical Answer Type (NAT).
What does the program print?
l = [233, 87, 14, 301, 87, 120]
l.sort()
i = 0
while i < len(l) - 1:
if l[i] == l[i + 1]:
print(l[i])
break
i += 1Find the first neighbouring equal pair.
l = [233, 87, 14, 301, 87, 120]
l.sort()
i = 0
while i < len(l) - 1:
if l[i] == l[i + 1]:
print(l[i])
break
i += 187- After sorting the list is
[14, 87, 87, 120, 233, 301]; the first equal neighbours are the two87s. - Answer: 87
Numerical Answer Type (NAT).
How many items does the loop look at before it stops?
l = [41, 7, 93, 25, 60, 18, 77, 3]
count = 0
for x in l:
count += 1
if x == 25:
break
print(count)The loop stops at the first match.
l = [41, 7, 93, 25, 60, 18, 77, 3]
count = 0
for x in l:
count += 1
if x == 25:
break
print(count)4- It looks at 41, 7, 93 and then 25, the match, so 4 items.
- Answer: 4
Multiple Choice Question (MCQ): choose the one correct option.
A linear search through a list of 100,000 items looks for a number that is not in the list. How many items are compared?
When the number is absent the scan never stops early.
- Every one of the 100,000 items is compared and none matches.
- Answer: option 3.
Numerical Answer Type (NAT).
How many different pairs can be formed from 30 people? (Use 30 × 29 / 2.)
Pairs count each two people once.
30 × 29 / 2 = 435.- Answer: 435
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements about the naive search.
It works on any list.
- Statement 3 is false: a linear search needs no order.
- Answer: options 1, 2 and 4.
sort, then compare neighbours
flag and break
23 people: about 50.7%
435 pairs
break when found
flag 0 means not found
100,000 for this list
so "Not found" prints once more
n in l
Sorting: Selection Sort
Sorting by repeatedly moving the smallest item, with a full trace.
Sorting means putting the items in order. Selection sort is the simplest idea: repeatedly find the smallest remaining item, move it into a new list, and remove it from the old one.
5.1The program
l = [12, 10, 7, 18, 6, 42, 8, 5, 35, 7]
x = []
while len(l) > 0:
minVal = l[0]
for i in range(len(l)):
if l[i] < minVal:
minVal = l[i]
x.append(minVal)
l.remove(minVal)
print(x)[5, 6, 7, 7, 8, 10, 12, 18, 35, 42]xis the new, empty list that will hold the sorted result. The outerwhileruns as long aslstill has items.minVal = l[0]assumes the first item is the smallest. Theforloop compares every item and keeps the smaller one.x.append(minVal)moves that smallest value to the result, andl.remove(minVal)takes one copy of it out ofl.- Each round
lgets one item shorter andxone item longer, untillis empty.
5.2Trace of the whole run
| Round | l at the start of the round | minVal | x after the round |
|---|---|---|---|
| 1 | [12, 10, 7, 18, 6, 42, 8, 5, 35, 7] | 5 | [5] |
| 2 | [12, 10, 7, 18, 6, 42, 8, 35, 7] | 6 | [5, 6] |
| 3 | [12, 10, 7, 18, 42, 8, 35, 7] | 7 | [5, 6, 7] |
| 4 | [12, 10, 18, 42, 8, 35, 7] | 7 | [5, 6, 7, 7] |
| 5 | [12, 10, 18, 42, 8, 35] | 8 | [5, 6, 7, 7, 8] |
| 6 | [12, 10, 18, 42, 35] | 10 | [5, 6, 7, 7, 8, 10] |
| 7 | [12, 18, 42, 35] | 12 | [5, 6, 7, 7, 8, 10, 12] |
| 8 | [18, 42, 35] | 18 | [5, 6, 7, 7, 8, 10, 12, 18] |
| 9 | [42, 35] | 35 | [5, 6, 7, 7, 8, 10, 12, 18, 35] |
| 10 | [42] | 42 | [5, 6, 7, 7, 8, 10, 12, 18, 35, 42] |
- Duplicates work. The value 7 appears twice.
remove(7)removes only one copy per round, so the second 7 is found in a later round and both reachx. - The original list is used up. At the end
lis empty. If you need the original later, work on a copy (l[:]).
How much work? Round 1 compares 10 items, round 2 compares 9, and so on: 10 + 9 + ... + 1 = 55 comparisons (counted in the code below). For n items it is about n²/2, so doubling the list roughly quadruples the work.
l = [12, 10, 7, 18, 6, 42, 8, 5, 35, 7]
comparisons = 0
x = []
while len(l) > 0:
minVal = l[0]
for i in range(len(l)):
comparisons += 1
if l[i] < minVal:
minVal = l[i]
x.append(minVal)
l.remove(minVal)
print(comparisons)555.3Variations
Largest first (descending)
l = [33, 7, 19, 2, 41, 15, 8, 27]
x = []
while len(l) > 0:
maxVal = l[0]
for i in range(len(l)):
if l[i] > maxVal:
maxVal = l[i]
x.append(maxVal)
l.remove(maxVal)
print(x)[41, 33, 27, 19, 15, 8, 7, 2]Only two things changed: min became max and the test < became >.
The built-in way (extra)
l = [12, 10, 7, 18, 6, 42, 8, 5, 35, 7]
print(sorted(l))
print(sorted(l, reverse=True))
l.sort()
print(l)
print(l.sort())[5, 6, 7, 7, 8, 10, 12, 18, 35, 42]
[42, 35, 18, 12, 10, 8, 7, 7, 6, 5]
[5, 6, 7, 7, 8, 10, 12, 18, 35, 42]
Nonesorted(l) returns a new sorted list and leaves l alone. l.sort() sorts l in place and returns None, so print(l.sort()) prints None. Writing the sort yourself, as above, shows how the idea works, and exams ask for it.Selection sort inside one list with swaps (extra)
l = [12, 10, 7, 18, 6]
for start in range(len(l)):
m = start
for i in range(start + 1, len(l)):
if l[i] < l[m]:
m = i
l[start], l[m] = l[m], l[start]
print(l)[6, 7, 10, 12, 18]This version keeps the sorted part at the front of the same list and swaps the smallest item into place, so no second list is needed. The idea (find the smallest, put it first) is exactly the same.
5.4Going deeper
- Why start with
l[0](extra). The starting guess must be a real item of the list. Starting with a fixed number like 0 would fail when every item is bigger. - Same shape as "find the maximum" (extra). The inner loop is the maximum-finding loop of Chapter 3 with the test flipped.
5.5Exam tips and common mistakes
- Trace with two columns: what is left in
l, and what has been collected inx. - For descending order flip both the name and the comparison.
- Forgetting
l.remove(minVal): the loop then never ends. - Setting
minVal = 0at the start. - Printing
lat the end: it is empty; the answer is inx.
Practice questions (graded-assignment style)
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [5, 9, 3, 1]
x = []
while len(l) > 0:
minVal = l[0]
for i in range(len(l)):
if l[i] < minVal:
minVal = l[i]
x.append(minVal)
l.remove(minVal)
print(x)Each round moves the smallest remaining item.
l = [5, 9, 3, 1]
x = []
while len(l) > 0:
minVal = l[0]
for i in range(len(l)):
if l[i] < minVal:
minVal = l[i]
x.append(minVal)
l.remove(minVal)
print(x)[1, 3, 5, 9]- The smallest values are moved one by one into
x: 1, 3, 5, 9. - Answer: option 1.
Numerical Answer Type (NAT).
What does the program print?
l = [5, 9, 3, 1]
x = []
while len(l) > 0:
minVal = l[0]
for i in range(len(l)):
if l[i] < minVal:
minVal = l[i]
x.append(minVal)
l.remove(minVal)
print(len(l))The loop runs while l has items.
l = [5, 9, 3, 1]
x = []
while len(l) > 0:
minVal = l[0]
for i in range(len(l)):
if l[i] < minVal:
minVal = l[i]
x.append(minVal)
l.remove(minVal)
print(len(l))0- The loop only stops when
lis empty, solen(l)is0. - Answer: 0
Numerical Answer Type (NAT).
For a list of 4 items this selection sort compares 4 + 3 + 2 + 1 items in total. What is the total?
Add the four numbers.
4 + 3 + 2 + 1 = 10.- Answer: 10
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [3, 1, 2]
print(l.sort())sort() sorts in place and returns None.
l = [3, 1, 2]
print(l.sort())Nonel.sort()returnsNone, and that is what gets printed.- Answer: option 2.
Multiple Choice Question (MCQ): choose the one correct option.
To sort in descending order with the same program, which two changes are needed?
Look at the descending program of this chapter.
- Track the largest value (
maxVal) and use>in the comparison. - Answer: option 1.
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements.
The program removes items from l.
- Statement 3 is false: the program empties the original list.
- Answer: options 1, 2 and 4.
move it to x
remove it from l
for loop compares
append then remove
l ends up empty
duplicates are fine
> instead of <
about n²/2
l.sort() in place, None
Dot Product and Matrices
A dot product, then matrix addition and multiplication with loops.
Lists of numbers become vectors, and lists of lists become matrices. This chapter does a dot product, then adds and multiplies matrices from first principles, using nothing but loops and indexing.
6.1Dot product
The dot product of two equal-length lists is the sum of the products of their corresponding elements.
x = [1, 7, 3, 4]
y = [8, 6, 3, 2]
if len(x) == len(y):
dot_product = 0
for i in range(len(x)):
dot_product += x[i] * y[i]
print(dot_product)
else:
print("Dot product can't be calculated")67| i | x[i] | y[i] | x[i] * y[i] | dot_product after |
|---|---|---|---|---|
| 0 | 1 | 8 | 8 | 8 |
| 1 | 7 | 6 | 42 | 50 |
| 2 | 3 | 3 | 9 | 59 |
| 3 | 4 | 2 | 8 | 67 |
The if len(x) == len(y) test checks that the lists are the same length; otherwise there is no dot product, and the else message prints:
x = [1, 7, 3, 4]
y = [8, 6, 3]
if len(x) == len(y):
dot_product = 0
for i in range(len(x)):
dot_product += x[i] * y[i]
print(dot_product)
else:
print("Dot product can't be calculated")Dot product can't be calculatedprint(dot_product) line has lost its indentation when copied. It belongs inside the if (and after the loop), as written above; otherwise the else would not match.x = [1, 7, 3, 4]
y = [8, 6, 3, 2]
print(sum(a * c for a, c in zip(x, y)))67The last line is a one-line version using zip and sum (extra). Learn the loop version first: it is what the exam asks for.
6.2A matrix as a list of lists
A matrix here is just a list of row-lists. M[i][j] is the item in row i, column j (both counted from 0).
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(A[0])
print(A[1][2])
print(A[2][0])
print(len(A), len(A[0]))[1, 2, 3]
6
7
3 3| Expression | Meaning | Value |
|---|---|---|
| A[0] | the whole first row | [1, 2, 3] |
| A[1][2] | row 1, column 2 | 6 |
| A[2][0] | row 2, column 0 | 7 |
6.3Matrix addition from first principles
Two same-sized matrices are added element by element: C[i][j] = A[i][j] + B[i][j]. Two nested loops visit every (i, j).
dim = 3
r1 = [1, 2, 3]
r2 = [4, 5, 6]
r3 = [7, 8, 9]
s1 = [1, 2, 1]
s2 = [6, 2, 3]
s3 = [4, 2, 1]
A = []
A.append(r1)
A.append(r2)
A.append(r3)
B = []
B.append(s1)
B.append(s2)
B.append(s3)
print(A)
print(B)
# we need to add A and B
C = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
for i in range(dim):
for j in range(dim):
C[i][j] = A[i][j] + B[i][j]
print(C)[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 2, 1], [6, 2, 3], [4, 2, 1]]
[[2, 4, 4], [10, 7, 9], [11, 10, 10]]C = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]makes a result matrix of zeros first, so thatC[i][j]exists to be assigned.dim = 3is the number of rows and of columns; both loops userange(dim).
6.4Matrix multiplication
C[i][j] is the dot product of row i of A and column j of B. That needs a third nested loop, over k, that walks along the row and down the column together.
r1 = [1, 2, 3]
r2 = [4, 5, 6]
r3 = [7, 8, 9]
s1 = [1, 2, 1]
s2 = [6, 2, 3]
s3 = [4, 2, 1]
A = []
B = []
A.append(r1)
A.append(r2)
A.append(r3)
B.append(s1)
B.append(s2)
B.append(s3)
C = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
dim = 3
for i in range(dim):
for j in range(dim):
for k in range(dim):
C[i][j] += A[i][k] * B[k][j]
print(C)[[25, 12, 10], [58, 30, 25], [91, 48, 40]]C[2][1] uses row index 2 of A (the third row, 7, 8, 9) and column index 1 of B (the second column, 2, 2, 2): 7·2 + 8·2 + 9·2 = 48, which is the value printed at C[2][1]. The 2nd row and first column of the comment would be C[1][0] = 4·1 + 5·6 + 6·4 = 58.| i | j | C[i][j] = sum over k of A[i][k] * B[k][j] | value |
|---|---|---|---|
| 0 | 0 | 1·1 + 2·6 + 3·4 | 25 |
| 0 | 1 | 1·2 + 2·2 + 3·2 | 12 |
| 0 | 2 | 1·1 + 2·3 + 3·1 | 10 |
| 2 | 1 | 7·2 + 8·2 + 9·2 | 48 |
How much work? The three nested loops multiply 3 × 3 × 3 = 27 pairs. For n × n matrices that is n³ multiplications, which is why multiplying big matrices is slow.
6.5The numpy shortcut
import numpy
X = numpy.mat(A)
Y = numpy.mat(B)
print(X * Y)The source shows this output:
print("[[25 12 10]\n [58 30 25]\n [91 48 40]]")[[25 12 10]
[58 30 25]
[91 48 40]]numpy.mat may be missing in newer numpy releases; if you get an error, use numpy.array with the @ operator (X @ Y) instead.A three-line numpy version replaces the whole triple-nested loop. It is worth remembering once you are comfortable with the first-principles version.
6.6Going deeper
- Addition needs equal sizes; multiplication needs matching inner sizes (extra). A (m × n) can be multiplied by B (n × p), giving m × p. The number of columns of A must equal the number of rows of B.
- A common trap (extra).
[[0] * 3] * 3builds three rows that are the same list, so changing one row changes all of them. Write the zero matrix out, or use a loop that builds each row separately.
bad = [[0] * 3] * 3
bad[0][0] = 5
print(bad)
good = []
for i in range(3):
good.append([0] * 3)
good[0][0] = 5
print(good)[[5, 0, 0], [5, 0, 0], [5, 0, 0]]
[[5, 0, 0], [0, 0, 0], [0, 0, 0]]6.7Exam tips and common mistakes
- Addition: two loops (i, j). Multiplication: three loops (i, j, k).
- Write the result matrix of zeros before the loops.
- Swapping the indexes:
B[j][k]instead ofB[k][j]. - Using
=instead of+=in the product loop, which keeps only the last term. - Forgetting the length check before a dot product.
Practice questions (graded-assignment style)
Numerical Answer Type (NAT).
What does the program print?
x = [1, 2, 3]
y = [4, 5, 6]
d = 0
for i in range(len(x)):
d += x[i] * y[i]
print(d)Multiply matching items and add.
x = [1, 2, 3]
y = [4, 5, 6]
d = 0
for i in range(len(x)):
d += x[i] * y[i]
print(d)321·4 + 2·5 + 3·6 = 4 + 10 + 18 = 32.- Answer: 32
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(A[1][2])A[1][2] is row 1, column 2.
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(A[1][2])6- Row 1 is
[4, 5, 6]and its item at index 2 is6. - Answer: option 4.
Numerical Answer Type (NAT).
What is the value printed?
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
B = [[1, 2, 1], [6, 2, 3], [4, 2, 1]]
print(sum(A[1][k] * B[k][0] for k in range(3)))Row 1 of A against column 0 of B.
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
B = [[1, 2, 1], [6, 2, 3], [4, 2, 1]]
print(sum(A[1][k] * B[k][0] for k in range(3)))584·1 + 5·6 + 6·4 = 4 + 30 + 24 = 58.- Answer: 58
Numerical Answer Type (NAT).
How many multiplications does the triple-loop program do for two 4 × 4 matrices?
n × n × n.
4 × 4 × 4 = 64.- Answer: 64
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
S = [[0, 0], [0, 0]]
for i in range(2):
for j in range(2):
S[i][j] = A[i][j] + B[i][j]
print(S)Add the items in the same position.
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
S = [[0, 0], [0, 0]]
for i in range(2):
for j in range(2):
S[i][j] = A[i][j] + B[i][j]
print(S)[[6, 8], [10, 12]]1+5, 2+6in row 0 and3+7, 4+8in row 1.- Answer: option 1.
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements.
A dot product needs equal lengths.
- Statement 4 is false: the two lists must have the same length.
- Answer: options 1, 2 and 3.
Equal lengths first
1·8 + 7·6 + 3·3 + 4·2 = 67
M[i][j]: row i, column j
Start the result with zeros
Multiply: 3 nested loops
C[i][j] += A[i][k] * B[k][j]
n cubed for n x n
value 48
(not run here)
Try It Yourself
The ten tasks of the source, each with a worked and executed solution.
Ten tasks from the source, to be done without looking back at the examples. Each has a worked, really executed solution. Try the task first, then compare.
7.1Tasks 1 to 5: creating, indexing, slicing, adding and deleting
Create a list of your 5 favourite movies and print it.
movies = ["Inception", "Interstellar", "Tenet", "Dunkirk", "Oppenheimer"]
print(movies)['Inception', 'Interstellar', 'Tenet', 'Dunkirk', 'Oppenheimer']Note that print shows the list with square brackets and single quotes, even though the code used double quotes.
Given l = [10, 20, 30, 40, 50], print the 1st, 3rd, and last item using indexing.
l = [10, 20, 30, 40, 50]
print(l[0], l[2], l[-1])10 30 50The 1st item is index 0, the 3rd item is index 2, and the last is -1.
From l = [1, 2, 3, 4, 5, 6, 7, 8], print the middle four items using a single slice.
l = [1, 2, 3, 4, 5, 6, 7, 8]
print(l[2:6])[3, 4, 5, 6]Start with l = [1, 2, 4, 5]. Insert 3 at the correct position so the list stays in order.
l = [1, 2, 4, 5]
l.insert(2, 3)
print(l)[1, 2, 3, 4, 5]Given l = [5, 10, 15, 20, 25], remove 15 by value, then delete the last item using del.
l = [5, 10, 15, 20, 25]
l.remove(15)
del l[-1]
print(l)[5, 10, 20]After remove(15) the list is [5, 10, 20, 25]; del l[-1] then deletes 25.
7.2Tasks 6 to 10: in, extend, looping, sorting and the combined task
Ask the user for a name and check whether it exists in a list of registered students.
students = ["Asha", "Ravi", "Meera"]
name = input("Enter a name: ")
print("Registered" if name in students else "Not registered")Enter a name: Ravi
Registeredstudents = ["Asha", "Ravi", "Meera"]
name = input("Enter a name: ")
print("Registered" if name in students else "Not registered")Enter a name: Kiran
Not registeredThe first run types Ravi (found), the second types Kiran (not found). The check is case-sensitive: "ravi" would not match "Ravi".
Merge two lists of even and odd numbers into one combined, sorted list.
evens = [2, 4, 6]
odds = [1, 3, 5]
evens.extend(odds)
evens.sort()
print(evens)[1, 2, 3, 4, 5, 6]extend adds the odd numbers to the end, then sort() puts the whole list in order.
Loop through a list of numbers and print only the ones greater than 10.
nums = [3, 12, 7, 18, 9, 25]
for n in nums:
if n > 10:
print(n)12
18
25Write your own selection-sort program to sort a list of 8 numbers in descending order.
l = [33, 7, 19, 2, 41, 15, 8, 27]
x = []
while len(l) > 0:
maxVal = l[0]
for i in range(len(l)):
if l[i] > maxVal:
maxVal = l[i]
x.append(maxVal)
l.remove(maxVal)
print(x)[41, 33, 27, 19, 15, 8, 7, 2]Write a program that removes duplicate numbers from a list of 20 random numbers between 1 and 10.
The source program has no seed, so its output changes every run. Here a random.seed(2) and an extra print of the original list are added so the result can be checked:
import random
random.seed(2)
l = []
for i in range(20):
l.append(random.randint(1, 10))
print(l)
unique = []
for n in l:
if n not in unique:
unique.append(n)
print(unique)[1, 2, 2, 6, 3, 5, 5, 10, 4, 10, 1, 10, 3, 7, 7, 9, 6, 9, 8, 9]
[1, 2, 6, 3, 5, 10, 4, 7, 9, 8]The idea: scan the original list and append a number to unique only if it is not already there (not in). The result keeps the order of first appearance; it is not sorted.
l = [3, 1, 3, 2, 1]
print(list(dict.fromkeys(l)))
print(sorted(set(l)))[3, 1, 2]
[1, 2, 3]The two extra lines are shorter ways: dict.fromkeys keeps the order, set loses it (extra).
7.3Going deeper
- Make your own checks. For each task write down the expected output before running. If the two differ, trace the code with a table until you find the line responsible.
- Variations to try (extra). Task 3 with a list of any even length; Task 8 printing the count instead; Task 9 ascending using the min version; Task 10 keeping the duplicates only.
7.4Exam tips and common mistakes
- Read the task for the output format: a list, one item per line, or a single value.
- Test every solution on a small list you can check by hand.
- Using
l.append(a, b):appendtakes exactly one item. - Comparing a number read with
input()to numbers in a list withoutint(...). - Printing inside the loop when the task asks for the whole list at the end.
Practice questions (graded-assignment style)
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
l = [5, 10, 15, 20, 25]
l.remove(15)
del l[-1]
print(l)Remove the value 15, then the last item.
l = [5, 10, 15, 20, 25]
l.remove(15)
del l[-1]
print(l)[5, 10, 20]remove(15)gives[5, 10, 20, 25];del l[-1]removes25.- Answer: option 1.
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
evens = [2, 4, 6]
odds = [1, 3, 5]
evens.extend(odds)
evens.sort()
print(evens)extend first, then sort.
evens = [2, 4, 6]
odds = [1, 3, 5]
evens.extend(odds)
evens.sort()
print(evens)[1, 2, 3, 4, 5, 6]- After
extendthe list is[2, 4, 6, 1, 3, 5], andsort()orders it. - Answer: option 2.
Numerical Answer Type (NAT).
How many numbers does unique hold at the end?
l = [4, 4, 9, 2, 9, 4]
unique = []
for n in l:
if n not in unique:
unique.append(n)
print(len(unique))A number is added only if it is not already there.
l = [4, 4, 9, 2, 9, 4]
unique = []
for n in l:
if n not in unique:
unique.append(n)
print(len(unique))3- The distinct numbers in
[4, 4, 9, 2, 9, 4]are 4, 9, 2: three. - Answer: 3
Multiple Choice Question (MCQ): choose the one correct option.
What does the program print?
students = ["Asha", "Ravi", "Meera"]
name = "ravi"
print("Registered" if name in students else "Not registered")The search is case-sensitive.
students = ["Asha", "Ravi", "Meera"]
name = "ravi"
print("Registered" if name in students else "Not registered")Not registered"ravi"is not equal to"Ravi", so it is not in the list.- Answer: option 2.
Multiple Select Question (MSQ): select all that apply.
Choose the correct statements.
sort changes the list in place.
- Statement 1 is false:
sort()returnsNone. - Answer: options 2, 3 and 4.
Week 4 Summary Sheet
One page to revise all of Week 4: methods, programs, the checklist and the common errors.
Everything from Week 4 on one page: the method table, how the methods differ, the programs of the week, the checklist, and the usual mistakes.
8.1Revision sheet
| Topic | Key point |
|---|---|
| [ ] | Creates a list, ordered and mutable |
| Indexing | l[0] = first item |
| Negative indexing | l[-1] = last item |
| Slicing l[a:b] | a = start, b = end (not included) |
| append(x) | Adds x to the end |
| insert(i, x) | Inserts x at index i |
| pop() | Removes the last item |
| remove(x) | Removes the first item equal to x |
| del l[i] | Deletes the item at index i |
| len(l) | Number of items in the list |
| l[i] = x | Changes the value at index i |
| x in l | True / False if x is present |
| extend(l2) | Appends all items of l2 to l |
| for i in l: | Loops through every item |
| Matrix as a list | A list of row-lists; M[i][j] = row i, col j |
| Matrix multiply | Needs 3 nested loops (i, j, k) |
8.2Which method, and what it returns
| Statement | Changes l? | Returns |
|---|---|---|
| l.append(x) | yes | None |
| l.insert(i, x) | yes | None |
| l.extend(l2) | yes | None |
| l.pop() | yes | the removed item |
| l.remove(x) | yes | None |
| del l[i] | yes | (a statement, not a function) |
| l.sort() | yes | None |
| sorted(l) | no | a new sorted list |
| l[a:b] | no | a new list |
| len(l), x in l | no | a number, True/False |
8.3The programs of the week
| Program | Idea | Result |
|---|---|---|
| Birthday paradox | sort, then compare neighbours | 30 people: about 70.6% chance of a repeat |
| Naive (linear) search | scan from the front, break on a match | up to 100,000 comparisons |
| Selection sort | find the smallest, move it, remove it | [5, 6, 7, 7, 8, 10, 12, 18, 35, 42] |
| Dot product | sum of x[i] * y[i] | 67 |
| Matrix addition | C[i][j] = A[i][j] + B[i][j] | [[2, 4, 4], [10, 7, 9], [11, 10, 10]] |
| Matrix multiplication | C[i][j] += A[i][k] * B[k][j] | [[25, 12, 10], [58, 30, 25], [91, 48, 40]] |
8.4Last-minute checklist
- Creating a list and mixing data types in one
- Positive and negative indexing
- Slicing with
[start:end], and omitting either side append()vsinsert()pop()vsremove()vsdellen()on a list- Changing an item by index assignment
- The
inoperator for membership checks extend()vsappend()with a list argument- Looping through a list with
for - Selection-sort logic (find min/max, move, remove)
- Linear search logic
- Representing a matrix as a list of lists
- Matrix addition vs matrix multiplication (2 vs 3 nested loops)
8.5Common errors to avoid
| Mistake | Correct idea |
|---|---|
| l[len(l)] | Out of range. The last item is l[len(l) - 1] or l[-1]. |
| l = l.append(x) | append returns None: use l.append(x) alone. |
| l1.append(l2) | Adds the list as one item. Use extend to add its items. |
| b = a | Not a copy: both names share one list. Use a[:]. |
| removing inside a for loop | Skips items. Build a new list instead. |
| l.sort() printed | Prints None. Use sorted(l) or sort first. |
| l[a:b] with b included | The end index is not included. |
Index 0.. and -1..
Slices l[a:b]
pop, remove, del
l[i] = x
for x in l
range(len(l))
Linear search
Selection sort
Dot product
2 vs 3 nested loops
append vs extend
b = a is not a copy