The Built-in Way
Three pages of loops, and Python could have answered most of them in one line. That is not a joke at your expense — the loops are what the exam is testing, and they are what you need the moment the rule gets fussy. But a programmer who only knows the loop writes twelve lines where one would do, so this page is the other half: the same problems, solved with what is already there.
1Program 1 — four questions, five lines
Total, average, largest, smallest and how many — for a list of marks.
# everything the first programming page did, in five lines
marks = [72, 65, 88, 91, 54]
print('Total: ', sum(marks))
print('Average:', sum(marks) / len(marks))
print('Largest:', max(marks))
print('Smallest:', min(marks))
print('How many:', len(marks))Total: 370 Average: 74.0 Largest: 91 Smallest: 54 How many: 5
average() in Python. Writing average(marks) gives NameError: name 'average' is not defined. The average is sum(marks) / len(marks) — two built-ins and a division — and that is the whole of it. It catches people out because sum, max, min and len all exist, so a fifth one feels like it ought to.| the question | the loop | the built-in |
|---|---|---|
| add them up | a total, 3 lines | sum(marks) |
| the average | a total, then a divide | sum(marks) / len(marks) |
| the largest | a champion, 3 lines | max(marks) |
| where is it? | an index loop with a break | marks.index(max(marks)) |
| how many 4s? | a counter, 3 lines | numbers.count(4) |
| is 4 there? | a loop with for…else | 4 in numbers |
| put it in order | a sorting algorithm | marks.sort() |
| how many different? | a not-in loop | len(set(numbers)) |
2Program 2 — where is the largest mark?
Print the highest mark and the position it sits at.
# where is the largest mark?
marks = [72, 65, 88, 91, 54]
best = max(marks)
where = marks.index(best)
print('The highest mark is', best)
print('It is at position', where)The highest mark is 91 It is at position 3
max() gives the value; index() turns a value into a position. The two together are the built-in answer to “which is biggest and where”. Note index() finds the first occurrence: if 91 appeared twice, this reports the earlier one and says nothing about the other.index() raises when the value is not there. marks.index(50) gives ValueError: 50 is not in list — it does not answer −1 the way a string's find() does. Ask if 50 in marks: first, or use the for…else search that reports the miss itself.3Program 3 — the second largest, by removing the largest
The counting page spent a page and a half on this, because the loop version is genuinely fiddly. Here is the idea that makes it easy: find the largest, take it out, and ask for the largest again.
# the second largest, by removing the largest and asking again
numbers = [45, 88, 12, 91, 67]
working = list(numbers)
largest = max(working)
working.remove(largest)
second = max(working)
print('The list: ', numbers)
print('After removing', largest, ':', working)
print('Largest: ', largest)
print('Second largest:', second)
print('Second largest is at position', numbers.index(second), 'of the original')The list: [45, 88, 12, 91, 67] After removing 91 : [45, 88, 12, 67] Largest: 91 Second largest: 88 Second largest is at position 1 of the original
working = list(numbers)A COPY. list() builds a new list holding the same items, so everything that follows happens to the copy. Without this line the original loses its largest value permanently — remove() changes the list it is called on.
working.remove(largest)remove() takes a VALUE, not a position, and deletes the first item equal to it. That is exactly what is wanted here: the largest value, gone.
second = max(working)The largest of what is left is the second largest of what there was. One line, and no champions to initialise.
numbers.index(second)Asked of the ORIGINAL list, because the position in the copy would be wrong for anything after the removed item. Position 1 in the original; it would also be 1 here, but on a list where the largest came first it would not be.
numbers is changed for good. numbers.remove(max(numbers)) leaves [45, 88, 12, 67] in the original, so anything later in the program that expects all five values is now quietly wrong. If the question does not mind, drop the copy; if it prints the list afterwards, the copy is the difference between right and wrong.It answers a slightly different question, and you must know which. On a list where the largest value appears twice, removing one of them leaves the other — so the second largest comes out equal to the largest:
The list: [91, 91, 45] After removing 91 : [91, 45] Largest: 91 Second largest: 91 Second largest is at position 0 of the original
That is the second largest item, which is what sorted(numbers)[-2] also gives. The loop version on the counting page answers 45 — the second largest value. Both are defensible; read the question, and if it is ambiguous, say in a comment which one you have written.
max() is asked about an empty list: ValueError: max() iterable argument is empty. A guard — if len(numbers) < 2: — is the honest fix, and it is the same empty-list thinking as the page before this one.4Program 4 — remove the duplicates with set()
The changing page removed repeats with a loop and a not in test. There is a type whose whole purpose is that it cannot hold the same value twice, and handing a list to it does the work:
# the repeats removed, with a set
numbers = [4, 7, 4, 2, 7, 9, 4]
different = set(numbers)
as_a_list = sorted(different)
print('Original: ', numbers)
print('How many different values:', len(different))
print('Sorted, no repeats:', as_a_list)Original: [4, 7, 4, 2, 7, 9, 4] How many different values: 4 Sorted, no repeats: [2, 4, 7, 9]
different = set(numbers)set() builds a set from the list, and a set simply refuses a value it already holds. The duplicates are not deleted one by one — there was never anywhere to put them.
len(different)How many DIFFERENT values there were. One built-in call answers a question that took a loop and a growing list before.
sorted(different)Back to a list, in order. sorted() takes a set perfectly happily and always hands back a list.
list(set([4, 7, 4, 2, 7, 9, 4])) gives [9, 2, 4, 7] — not the order they were typed in, and not sorted either. That is why the program above says sorted() and not list(): sorting is a decision, and it is the only way to get a predictable answer out of a set. If the original order matters, the loop version is the one you want.set() when you are writing your own code, and know the not in loop for the paper, which is what “without using a set” is asking for.5Program 5 — sort() against sorted()
Put a list in order — twice, once each way — and see what happens to the original.
# the two ways of putting a list in order
marks = [45, 88, 12, 91, 67]
in_order = sorted(marks)
print('sorted() gave', in_order)
print('marks is still', marks)
marks.sort()
print('after marks.sort(), marks is', marks)
print('and sort() itself returned', [45, 88].sort())
print('descending:', sorted([45, 88, 12], reverse=True))sorted() gave [12, 45, 67, 88, 91] marks is still [45, 88, 12, 91, 67] after marks.sort(), marks is [12, 45, 67, 88, 91] and sort() itself returned None descending: [88, 45, 12]
sorted() answers; sort() changes. sorted(marks) hands back a new list and leaves the original alone. marks.sort() rearranges the original and hands back None — which is why marks = marks.sort() is the mistake that throws the list away, exactly like marks = marks.append(x).With sorting available, three of the earlier programs become one line each: the largest is sorted(marks)[-1], the smallest is sorted(marks)[0], and the middle value of an odd-length list is sorted(marks)[len(marks) // 2].
6So which do you write?
'Without using max()', 'using a loop', 'write an algorithm to…' — all of them mean the long version, and a one-line answer scores nothing however right it is.
Count the evens above the average. Find the first value that repeats. Split into two lists. There is no function for any of those.
sum(marks) cannot have an off-by-one, cannot start its collector at the wrong value, and says what it means at a glance.
Second largest item or second largest value? Order kept or order lost? The two versions differ, and the difference is where the marks are.
max() walks the list keeping a champion, exactly as your program did; sum() keeps a total; in is a linear search that stops when it finds something. Nothing on this page is magic — it is the same three pages of loops, packaged. That is worth knowing, because it is also why they fail on an empty list in the same way yours does.7Recap
The average is sum(marks) / len(marks). average() does not exist and gives a NameError.
The second largest in three lines. Copy the list first with list(numbers), or the original loses its largest value for good.
len(set(numbers)) is how many different values. sorted(set(numbers)) to get a predictable list back — never list(set(...)).
sort() returns None, so marks = marks.sort() destroys the list. Both take reverse=True for descending order.
- 1
Find the third largest value by removing the largest twice.
Hint · The same three lines, one more time — and think about what a list of two items would do.
- 2
Print the range of a list: the largest minus the smallest.
Hint ·
max(marks) - min(marks). One line, and worth writing the loop version once to compare. - 3
Report how many values appear more than once, using
count().Hint · Loop over
set(numbers)and testnumbers.count(v) > 1. - 4
Print the top three marks, in order, without changing the original list.
Hint ·
sorted(marks, reverse=True)and then a slice of the first three. - 5
Take a list from the user with
eval()and print it with the duplicates removed, in the order they were typed.Hint · This is the one
set()cannot do — the loop withnot inis the answer, and that is the point of the exercise.
What does average(marks) do in Python?
numbers.remove(max(numbers)) is used to find the second largest. What is the catch?
list(set([4, 7, 4, 2, 7, 9, 4])) gives [9, 2, 4, 7]. Why not [4, 7, 2, 9]?