Counting & Totals
Six programs that answer a question with a number. All of them are a collector above the loop and a test inside it — until the last one, which looks the same, is set constantly, and is wrong in almost every version you will be shown. The lists here are written into the program to keep the working half in view; putting a reading loop in front of any of them is Programs on a List You Read, in the submenu above.
1Program 1 — above and below the average
Work out the average of a list of marks, then count how many are above it and how many below.
# how many items are above the average, and how many below?
marks = [72, 65, 88, 91, 54, 40]
total = 0
for m in marks:
total = total + m
average = total / len(marks)
above = 0
below = 0
for m in marks:
if m > average:
above = above + 1
elif m < average:
below = below + 1
print('Average:', average)
print('Above:', above)
print('Below:', below)Average: 68.33333333333333 Above: 3 Below: 3
round(average, 2) gives 68.33 when you want something readable — but do the rounding when you print, never before the comparisons, or marks close to the average land on the wrong side.An elif rather than a second if, and no else: a mark exactly equal to the average is neither above nor below, so it is counted in neither. That is why 3 + 3 happens to be 6 here and would not be if a mark landed exactly on it.
2Program 2 — how many times does one value appear?
Count how often a given value occurs in a list, without count().
# count how many times one value appears, without count()
numbers = [4, 7, 4, 2, 4, 9]
wanted = 4
count = 0
for n in numbers:
if n == wanted:
count = count + 1
print(wanted, 'appears', count, 'times')4 appears 3 times
The shortest program on the page, and the one whose shape everything else is built from. numbers.count(4) does the same job in one call — write the loop when the question asks for it, and when the test is something no method knows about, like “how many are even and above the average”.
3Program 3 — the evens and the odds, kept apart
Add up the even numbers and the odd numbers separately.
# the totals of the even and the odd numbers, kept apart
numbers = [12, 7, 30, 45, 8, 21]
even_total = 0
odd_total = 0
for n in numbers:
if n % 2 == 0:
even_total = even_total + n
else:
odd_total = odd_total + n
print('Even numbers add up to', even_total)
print('Odd numbers add up to', odd_total)Even numbers add up to 50 Odd numbers add up to 73
n. Change both to + 1 and the program answers a different question — how many, not how much — and the answers (3 and 3) look every bit as plausible. The only way to tell them apart is to read what the variable is called and check the line agrees with it.4Program 4 — how many in each grade band?
Sort a list of marks into A (90+), B (75+), C (33+) and Failed.
# how many marks fall in each grade band?
marks = [92, 78, 45, 88, 30, 61, 75]
a_grade = 0
b_grade = 0
c_grade = 0
failed = 0
for m in marks:
if m >= 90:
a_grade = a_grade + 1
elif m >= 75:
b_grade = b_grade + 1
elif m >= 33:
c_grade = c_grade + 1
else:
failed = failed + 1
print('A:', a_grade)
print('B:', b_grade)
print('C:', c_grade)
print('Failed:', failed)A: 1 B: 3 C: 2 Failed: 1
m >= 75 is asked, the mark is already known to be under 90 — so there is no need to write m >= 75 and m < 90. Put the loosest rung first and every mark becomes a C: 45, 88 and 92 all pass m >= 33.Count the answers: 1 + 3 + 2 + 1 = 7, which is len(marks). Every mark landed in exactly one band, which is what the else guarantees.
5Program 5 — the second largest, and why it is hard
Find the second largest number in a list.
Here is the version that gets written first, everywhere. It keeps two champions and updates them together:
# the version everybody writes first
numbers = [20, 10]
largest = numbers[0]
second = numbers[0]
for n in numbers:
if n > largest:
second = largest
largest = n
elif n > second and n != largest:
second = n
print('Largest:', largest)
print('Second largest:', second)Largest: 20 Second largest: 20
[20, 10] is not 20. Both champions start at 20 — the first item — and 10 never beats either of them, so second is never touched. The program is right on every list whose largest value happens to arrive after something smaller, which is most of them, and that is exactly why the fault survives being tested.The fix is to stop pretending second has a value before one has been found. Two passes, and a starting value that means “nothing different yet”:
# the second largest number in a list
numbers = [45, 88, 12, 91, 67]
largest = numbers[0]
for n in numbers:
if n > largest:
largest = n
second = largest
for n in numbers:
if n != largest:
if second == largest or n > second:
second = n
if second == largest:
print('Every item in the list is the same')
else:
print('Largest:', largest)
print('Second largest:', second)Largest: 91 Second largest: 88
second = largestNot a real answer — a marker meaning 'no different value has been found yet'. It is a value we know is in the list, and one that can be recognised later.
if second == largest or n > second:Take this item if nothing has been taken yet, OR if it beats what has. The first half is what gets the search started without inventing a number.
if second == largest:Still the marker, so nothing different was ever found: every item is the same. Saying so is better than printing a second largest that does not exist.
Largest: 20 Second largest: 10
Every item in the list is the same
[45, 88, 12, 91, 67] has proved almost nothing.6Program 6 — the same answer, the short way
# the second largest, using sorted()
numbers = [45, 88, 12, 91, 67]
in_order = sorted(numbers)
print('Sorted:', in_order)
print('Largest:', in_order[-1])
print('Second largest:', in_order[-2])Sorted: [12, 45, 67, 88, 91] Largest: 91 Second largest: 88
sorted() hands back a new sorted list and leaves the original alone, so numbers is still in its original order afterwards. in_order[-1] is the last item and in_order[-2] the one before it.
[91, 91, 45] this prints 91 as the second largest, because the second largest item really is another 91 — while the loop version answers 45, the second largest value. Neither is wrong; read the question and know which one you have written.max(), take it out with remove(), and ask for the largest again — three lines, no champions to initialise. It is on The Built-in Way at the end of this submenu, along with the copy you have to make first.7Recap
The variable's name should tell you which. Getting it wrong gives a plausible number and no error at all.
The rungs lean on each other, so no rung needs an upper limit written into it. Loosest first and everything lands on the first rung.
round() in a comparison moves marks across the boundary. Keep the full value for the maths and shorten it for the reader.
second = largest means 'nothing different found yet'. Starting it at the first item quietly answers 20 for [20, 10].
- 1
Count how many numbers in a list are both even and above 50.
Hint · One
ifwith anand— no method answers this, which is the point. - 2
Find the smallest value, and how many times it appears.
Hint · Two passes: the champion first, then a tally of items equal to it.
- 3
Count how many marks are within 5 of the average, either side.
Hint · The two-pass shape, with
m >= average - 5 and m <= average + 5. - 4
Find the second smallest value in a list.
Hint · Program 5 turned round. Test it on a two-item list before you believe it.
- 5
Count the items in a list of names that begin with a vowel.
Hint ·
name[0].lower() in 'aeiou', and mind an empty name.
A grade ladder is written with if m >= 33 first, then elif m >= 75, then elif m >= 90. What grade does 92 get?
Why does second = numbers[0] fail on [20, 10]?
sorted(numbers) is used to find the second largest. What happens to numbers?