The Remedial List That Leaked Between Sections
In a school practical, three sections' marks are processed by a Python script. The teacher runs it once per section without restarting the interpreter. Section A marks are [88, 0, 72]; Section B marks are [45, 51]; Section C marks are []. The script contains:
def analyse(marks, remedial=[]): total = 0 for m in marks: if m: total += m else: remedial.append('absent') print('average', total/len(marks), 'remedial', remedial)
After Section A, the output shows average 53.33 and remedial ['absent']. After Section B, the remedial list still contains the earlier entry, and Section C crashes with ZeroDivisionError. The teacher also notices that a student who genuinely scored 0 is reported absent, while a student who was absent is entered as 0 by the clerk.
1. Explain each symptom - the shared list, the zero treated as absent, and the crash - and give a corrected version of the function.
Read the worked answer
Three separate causes:
- The default argument
remedial=[]is evaluated once when the function is defined, not on each call. Section A appends to that one list object; Section B reuses the same object, so it sees 'absent' from Section A. Fix:def analyse(marks, remedial=None): if remedial is None: remedial = []. if m:is false for both 0 and None. A genuine zero is therefore treated as absence. Fix: use an explicit sentinel, e.g.Nonefor absent and0for scored zero, and testif m is not None:.- Section C has
len(marks) == 0, sototal/len(marks)raises ZeroDivisionError. Fix: guardif not marks: return Noneor print 'no students'.
Corrected sketch:
def analyse(marks, remedial=None): if remedial is None: remedial = [] if not marks: return None valid = [m for m in marks if m is not None] total = sum(valid) return total/len(valid)
The important design change is to make absence explicit rather than encoding it as zero.
2. Compute Section A's average under two policies: (i) zero counts as a mark, (ii) zero means absent and is excluded. Which policy should the teacher use if the board requires attendance in the result, and how should the data be stored to make the policy explicit?
Read the worked answer
Policy (i): zero counts as a mark: (88 + 0 + 72)/3 = 160/3 = 53.33. Policy (ii): zero is treated as absent and excluded: (88 + 72)/2 = 160/2 = 80.00. The same data gives a 26.67-mark difference, so the policy is not cosmetic.
If the board requires attendance to be reported, store absence as NULL/None or the string 'AB', not as 0, and compute the average over scored entries. If the school still wants a zero to pull the average down, keep 0 as a real mark and store absence separately. The original script's average happens to equal policy (i) because adding a zero does not change the total, but its remedial list labels the zero as absent. Average and attendance label are therefore using different definitions - that inconsistency is the real bug.
3. Section C has no students. What should the function print instead of crashing, and why is returning a numeric 0 average dangerous?
Read the worked answer
Return None or print 'no students'. A numeric 0.0 is indistinguishable from a real class average of zero and could be exported to a scholarship or remediation list as a genuine result. None forces the caller to handle 'no data' explicitly, for example by skipping Section C in a report or printing a dash. The function should also avoid mutating any list passed by the caller unless that side effect is documented, because a caller may reuse the same list for another section.