Skip to content

2029–2031 edition · for exams from June 2029. Students sitting exams up to November 2028 follow the current course.

2210 · 04782029–2031 editionPaper 2 · Algorithms and Programming§10.1, §10.2, §10.3, §10.4, §10.5

10. Further programming and testing

Local and global scope, procedures and functions with Python's built-ins, 1D and 2D lists, file handling, validation and verification in Python, test data, error types and maintainable code.

What you need to know521 learning objectives, as printed in the syllabus

  1. 10.1Variables and constantsNow in Python2026–2028 syllabus: §8.1

    Local and global variables and constants in Python (global keyword).

    Learning objectives (3)
    • 10.1.1Demonstrate and use local and global variables
    • 10.1.2Demonstrate and use local and global constants
    • 10.1.3Explain the advantages and disadvantages of local and global: (a) variables; (b) constants
  2. 10.2Procedures and functionsNow in Python2026–2028 syllabus: §8.1

    Procedures and functions with def; sum, count, max, min, statistics.mean, round, random.randrange and random.randint.

    Learning objectives (4)
    • 10.2.1Identify procedures and functions in a given context
    • 10.2.2Explain the purpose and concept of procedures and functions in a given context
    • 10.2.3Demonstrate and use functions, including: (a) sum() totalling; (b) .count() counting; (c) max() maximum; (d) min() minimum; (e) .mean() mean average; (f) round() rounds a number; (g) random.randrange() random number generation; (h) random.randint() random number generation
    • 10.2.4Demonstrate and use procedures and call functions with or without parameters
  3. 10.3ListsNow in Python2026–2028 syllabus: §8.2

    Arrays become Python lists from index 0, with append, insert, pop, sort and in / not in.

    Learning objectives (4)
    • 10.3.1Explain the purpose of a list including its features
    • 10.3.2Define, initialise and use one-dimensional (1D) and two-dimensional (2D) lists, including: (a) a variable for the index; (b) writing into, and reading from a list including the use of iteration; (c) iterating through each element in a list
    • 10.3.3Use Python's built-in functions and operators for working with lists, including: (a) sum(); (b) max(); (c) min(); (d) len(); (e) .append(); (f) .insert(); (g) .pop(); (h) .sort(); (i) in, not in (membership)
    • 10.3.4Write algorithms for a: (a) linear search; (b) bubble sort

    The first index is zero

  4. 10.4File handlingNow in Python2026–2028 syllabus: §8.3

    open / with open, modes r, w, a, read, readline, readlines and write.

    Learning objectives (2)
    • 10.4.1Explain the purpose of storing data in a file to be used by a program
    • 10.4.2Demonstrate and use: (a) open() open a file; (b) .close() close the file; (c) with open(); (d) "r" open in read mode; (e) "w" open in write mode; (f) "a" open in append mode; (g) .read(), .readline(), .readlines() read the file; (h) .write() write to file
  5. 10.5TestingChanged2026–2028 syllabus: Topic 7, 8.1

    Validation and double entry checks written in Python; test data is normal, boundary and erroneous; three error types.

    Learning objectives (8)
    • 10.5.1Describe the purpose of validation checks, limited to a: (a) range check; (b) length check; (c) type check; (d) presence check; (e) format check; (f) check digit
    • 10.5.2Write Python code to validate input data for a given scenario, (inclusive and exclusive)
    • 10.5.3Explain the purpose of verification, including the function of visual and double entry checks
    • 10.5.4Write Python code to do a double entry check
    • 10.5.5Suggest and apply suitable normal, boundary and erroneous test data
    • 10.5.6Identify and debug errors in given Python code
    • 10.5.7Identify and explain the different types of programming errors, limited to: (a) syntax error; (b) logic/logical errors; (c) run-time errors
    • 10.5.8Demonstrate and use techniques that make a program easier to maintain, including: (a) meaningful identifiers; (b) appropriate commenting; (c) procedures and functions; (d) relevant program layout

Objectives quoted from the 2029–2031 syllabus, Version 1, September 2026; © Cambridge University Press & Assessment.

Notes5every learning objective explained, with worked examples

10.1Variables and constants

Where a variable or constant is declared decides where it can be used — its scope. You must write local and global variables and constants in Python and weigh up when each is the better choice.

Local and global variables

  • A global variable is created in the main program (outside every function) and can be read anywhere in the program.
  • A local variable is created inside a procedure or function and exists only while that subroutine runs; nothing outside can use it.
  • To change a global variable inside a function, Python needs the global keyword.
VAT_RATE = 0.2          # global constant

total_sales = 0         # global variable

def add_sale(price):
    global total_sales
    with_vat = price + price * VAT_RATE   # local variable
    total_sales = total_sales + with_vat
    return with_vat

print(add_sale(10))
print(add_sale(5))
print(total_sales)

Output: 12.0, 6.0, 18.0. Using with_vat in the main program would cause a run-time error (NameError): it is local.

Local and global constants

  • A global constant (e.g. VAT_RATE) is declared once at the top and used by every subroutine — ideal for values the whole program shares.
  • A local constant is declared inside one subroutine because only that subroutine needs it.
def pass_mark(mark):
    PASS = 50             # local constant
    return mark >= PASS

Advantages and disadvantages

AdvantagesDisadvantages
Local variablesCannot be changed by mistake by other parts; the same name can be reused in different subroutines; memory freed when the subroutine ends; subroutines are self-contained and reusableValues are lost when the subroutine ends; must be passed as parameters/returned to share them
Global variablesAccessible everywhere, no need to pass them around; value kept for the whole runCan be changed accidentally anywhere — errors are hard to trace; use memory all the time; make subroutines depend on the main program, so less reusable
Local constantsKept close to where they are used; no clash with other namesCannot be shared; may be repeated in several places
Global constantsOne place to update a value used everywhere; cannot be changed by the programUse memory for the whole run; a name clash is possible in large programs

Exam tips

  • Define scope clearly: global = declared in the main program, accessible throughout; local = declared in a subroutine, accessible only there.
  • In code, show that the global keyword is needed to change a global variable inside a function.
  • Give advantages AND disadvantages when asked to explain — and link them to maintenance and errors.

Mistakes that lose marks

  • Assigning to a global variable inside a function without the global keyword (Python makes a new local instead).
  • Using a local variable outside its function.
  • Saying constants can be changed by the program while it runs.

10.2Procedures and functions

Procedures and functions are named blocks of code that do one job and can be called whenever needed. You must spot them, explain why they help, write your own with or without parameters, and use the built-in functions listed in the syllabus.

Procedures and functions

  • A subroutine is a named, self-contained block of code that performs a specific task; it runs when it is called.
  • A procedure performs a task and does not return a value (e.g. displays a menu).
  • A function performs a task and returns a value with return to where it was called (e.g. calculates an area), so it is used in an expression or assignment.
  • A parameter is the variable named in the definition that receives a value; the value passed in the call is the argument.

Why use them

  • Code is written once and called many times — shorter programs, no repetition.
  • Each one can be tested on its own, making errors easier to find.
  • The program is decomposed into clear parts that different programmers can write.
  • Easier to maintain: change the code in one place.
  • Can be reused in other programs (a library).

Writing and calling them, with or without parameters

def show_line(length):          # procedure: does a job, returns nothing
    print("-" * length)

def area(width, height):        # function: returns a value
    return width * height

def greeting():                 # function with no parameters
    return "Welcome!"

show_line(10)
print(greeting())
room = area(4, 3.5)
print("Area:", room)

Output: ----------, Welcome!, Area: 14.0. Arguments are matched to parameters in order.

The built-in functions you must know

FunctionPurpose
sum(list)Total of the values
list.count(value)How many times a value appears
max(list) / min(list)Largest / smallest value
statistics.mean(list)Mean average (needs import statistics)
round(number, places)Rounds to that many decimal places
random.randint(a, b)Random whole number from a to b inclusive
random.randrange(start, stop, step)Random value from the range — stop is excluded
import random
import statistics

marks = [56, 72, 91, 45, 72, 68]
print("Total:", sum(marks))
print("Highest:", max(marks), "Lowest:", min(marks))
print("How many 72s:", marks.count(72))
print("Mean:", round(statistics.mean(marks), 1))
dice = random.randint(1, 6)        # 1 to 6 inclusive
even = random.randrange(0, 10, 2)  # 0, 2, 4, 6 or 8 (10 excluded)

Output: Total: 404, Highest: 91 Lowest: 45, How many 72s: 2, Mean: 67.3.

Exam tips

  • The difference examiners want: a function returns a value, a procedure does not.
  • When identifying them in given code, quote the name and say whether it returns a value.
  • random.randint includes both ends; random.randrange excludes the stop value — say so in explanations.
  • Purpose answers: reuse without rewriting, easier testing, easier maintenance, splitting work between programmers.

Mistakes that lose marks

  • Printing inside a function instead of returning the value the question asked for.
  • Calling a function and ignoring its result (area(4, 3) on its own line does nothing useful).
  • Forgetting import random / import statistics.
  • Passing arguments in the wrong order.

10.3Lists

A list stores many related values under one name, so one loop can process them all. You need 1D and 2D lists, the list functions and methods in the syllabus, and to write a linear search and a bubble sort on a list.

Purpose and features of a list

A list is a data structure that stores a collection of values under one identifier.

  • Each item is accessed by its index; the first index is 0.
  • Items are kept in order; a list can grow and shrink.
  • Avoids needing many separate variables (mark1, mark2…), and lets a loop process every item.

1D lists: index variables and iteration

names = ["Ali", "Sara", "Omar"]
print(names[0])               # Ali
names[2] = "Bilal"            # write into index 2
for index in range(len(names)):
    print(index, names[index])  # using a variable for the index
for name in names:
    print(name)                 # iterating through each element
marks = []
for count in range(3):
    marks.append(int(input("Mark: ")))   # filling a list from input

2D lists

A 2D list is a list of lists — a table of rows and columns. Access an item with two indexes: [row][column], and process it with nested loops.

# 3 students x 4 test scores
scores = [[12, 15, 9, 18],
          [20, 17, 14, 16],
          [8, 11, 13, 10]]
print(scores[1][2])            # row 1, column 2
for row in range(len(scores)):
    total = 0
    for col in range(len(scores[row])):
        total = total + scores[row][col]
    print("Student", row, "total", total)
grid = [[0] * 3 for _ in range(2)]
grid[1][0] = 5
print(grid)

Output: 14, Student 0 total 54, Student 1 total 67, Student 2 total 42, [[0, 0, 0], [5, 0, 0]].

List functions, methods and membership

names = ["Ali", "Sara", "Omar"]
names.append("Zara")          # add to the end
names.insert(1, "Bilal")      # put at index 1
print(names)
removed = names.pop()         # remove the last item
print(removed, names)
names.sort()
print(names)
print("Omar" in names, "Hina" not in names)
print(len(names))

Output:

  • ['Ali', 'Bilal', 'Sara', 'Omar', 'Zara']
  • Zara ['Ali', 'Bilal', 'Sara', 'Omar']
  • ['Ali', 'Bilal', 'Omar', 'Sara']
  • True True
  • 4

sum(), max(), min() and len() work on number lists too. .pop(i) removes the item at index i; .sort() sorts the list itself (ascending).

Writing a linear search and a bubble sort

You must be able to write both without using in or .sort() — the examiner wants the algorithm. Use the code in §9.3: a loop with an index variable and a found flag for the search; nested loops, comparison of list[i] with list[i + 1], a temporary variable for the swap and a swapped flag for the sort.

names = ["Hina", "Ali", "Omar", "Bilal"]
search = input("Name to find: ")
found = False
for index in range(len(names)):
    if names[index] == search:
        print("Found at position", index)
        found = True
if not found:
    print("Not in the list")

Exam tips

  • Always use index 0 for the first item and len(list) - 1 for the last.
  • For 2D lists say [row][column] and use nested for loops.
  • When asked to write a search or sort, write the loops yourself — in and .sort() will not earn the algorithm marks.
  • Initialise empty lists with [] before appending.

Mistakes that lose marks

  • Off-by-one errors: list[len(list)] is out of range (IndexError).
  • Confusing .append() (adds to the end) with .insert(index, value).
  • Writing sorted = names.sort() — .sort() returns None; it changes the list itself.
  • Mixing up row and column order in a 2D list.

10.4File handling

Variables and lists vanish when a program ends. Files keep data permanently so the next run, or another program, can use it. You must open, read, write and close text files in Python.

Why store data in a file

  • Data is kept permanently (non-volatile) after the program closes or the computer is switched off.
  • Data can be used again the next time the program runs (high scores, settings, records).
  • Data can be shared with other programs or users, or moved to another computer.
  • Large amounts of data can be stored without typing them in each time.

Opening and closing, and the three modes

ModeMeaningIf the file existsIf not
"r"ReadReads from the startError
"w"WriteWipes it and starts againCreates it
"a"AppendAdds to the endCreates it

open(filename, mode) opens a file and .close() closes it (saving the data and releasing the file). with open(...) as file: closes the file automatically at the end of the indented block, even if an error happens.

Writing and reading

with open("scores.txt", "w") as file:
    file.write("Ali,72\n")
    file.write("Sara,88\n")
file = open("scores.txt", "a")
file.write("Omar,65\n")
file.close()
with open("scores.txt", "r") as file:
    first = file.readline()
    rest = file.readlines()
print(first.strip())
print(rest)
with open("scores.txt", "r") as file:
    everything = file.read()
print(everything.count("\n"), "lines")

Output: Ali,72, ['Sara,88\n', 'Omar,65\n'], 3 lines.

  • .write(text) writes a string (cast numbers with str() and add "\n" yourself for a new line).
  • .read() — the whole file as one string.
  • .readline() — the next line (including its "\n"; .strip() removes it).
  • .readlines() — every remaining line as a list of strings.

Exam tips

  • Say which mode and why: 'w' replaces the contents, 'a' keeps existing data and adds to the end.
  • Always close a file (or use with open) — examiners award a mark for it.
  • Remember write() needs a string: file.write(str(score) + '\n').
  • Purpose of files: data is stored permanently so it can be used again after the program ends.

Mistakes that lose marks

  • Opening with 'w' when data should be added — the old contents are lost.
  • Writing a number directly with write() (TypeError).
  • Forgetting the newline, so every record ends up on one line.
  • Reading a file in 'w' or 'a' mode.

10.5Testing

Testing makes sure a program accepts only sensible data and does what it should. This § covers validation and verification (and coding both), choosing test data, finding and classifying errors, and writing code that others can maintain.

Validation checks

Validation is an automatic check by the program that data entered is reasonable/sensible and follows the rules before it is accepted. It cannot tell if data is correct.

CheckWhat it doesExample
RangeValue lies between limitsMark 0–100
LengthNumber of characters is right (exact, or min/max)Password at least 8 characters; PIN exactly 4
TypeData is the right data typeAge is a whole number
PresenceSomething has been entered (not left blank)Surname must be filled in
FormatData matches a patternCode = two letters then four digits (AB1234); date DD/MM/YYYY
Check digitExtra digit calculated from the others and added to the end; recalculated on entry to detect typing/transposition errorsISBN, barcodes

Writing validation in Python (inclusive and exclusive)

Inclusive limits accept the boundary values (0 <= mark <= 100); exclusive limits reject them (11 <= age < 19 excludes 19).

# range check, inclusive: 0 to 100 allowed
mark = int(input("Mark (0-100): "))
while mark < 0 or mark > 100:
    print("Out of range")
    mark = int(input("Mark (0-100): "))
print("Accepted", mark)
# presence, length and format checks on a product code like AB1234
while True:
    code = input("Product code: ")
    if code == "":
        print("Presence check: you must enter something")
    elif len(code) != 6:
        print("Length check: must be 6 characters")
    elif not (code[0:2].isalpha() and code[0:2].isupper() and code[2:].isdigit()):
        print("Format check: two capital letters then four digits")
    else:
        break
print("Valid code", code)
# type check, then an exclusive range check
while True:
    text = input("Age: ")
    if text.isdigit():
        age = int(text)
        if 11 <= age < 19:          # exclusive upper limit: 19 is rejected
            break
        print("Range check: 11 to 18 only")
    else:
        print("Type check: whole number please")
print("Age accepted:", age)

With inputs ten, 19, 15 the last program prints the type message, the range message, then Age accepted: 15.

Check digits

def check_digit(code):
    weights = [4, 3, 2]
    total = 0
    for i in range(3):
        total = total + int(code[i]) * weights[i]
    remainder = total % 11
    digit = (11 - remainder) % 11
    return "X" if digit == 10 else str(digit)

print(check_digit("573"))

For 573: 5×4 + 7×3 + 3×2 = 47; 47 % 11 = 3; 11 − 3 = 8, so the full code is 5738. If someone types 5733, the recalculated check digit (8) does not match, so the error is detected.

Verification: visual check and double entry

Verification checks that data has been copied/entered accurately — that it matches the original source.

  • Visual check — the user reads the data on screen and compares it with the original document, then confirms.
  • Double entry — the data is entered twice and the computer compares the two; if they differ, it must be re-entered (e.g. new password, email address).

Validation = is it sensible? Verification = is it what was meant/the same as the source?

while True:
    password = input("New password: ")
    again = input("Type it again: ")
    if password == again:
        break
    print("They do not match - try again")
print("Password saved")

Test data: normal, boundary and erroneous

For a mark that must be 0 to 100 inclusive:

TypeMeaningExamplesExpected result
NormalSensible data that should be accepted45, 72Accepted
BoundaryThe largest and smallest acceptable values and the values just outside them0 and 100 (accepted); −1 and 101 (rejected)As stated
ErroneousData that should be rejected (out of range or wrong type)−20, 150, "ten"Rejected with an error message

Always state the expected result alongside each test value.

The three types of programming error

ErrorWhat it isExampleWhen found
SyntaxBreaks the rules of the language, so the program will not runprint("Hi" (missing bracket), missing colon after ifBefore running (the translator reports it)
LogicProgram runs but gives the wrong resultrange(1, 5) when 1–5 was needed; > instead of >=; average = total / 10 when 12 valuesOnly by testing with known expected results
Run-timeProgram starts but crashes while runningDividing by zero; scores[3] in a 3-item list (IndexError: list index out of range); int("ten")While running

Identifying and debugging errors

  • Read the error message: the line number and type (SyntaxError, NameError, IndexError, ValueError, ZeroDivisionError).
  • Use a trace table / dry run to find logic errors.
  • Add temporary print() statements to show variable values.
  • Test with data whose result you already know.
# Should output the average of the three numbers
total = 0
for count in range(3):
    total = total + int(input())
average = total / 2          # logic error: should be / 3
print(average)

Making programs easy to maintain

  • Meaningful identifiers — total_price not tp, so the purpose of each variable is clear.
  • Appropriate commenting — # comments explain what each part does and why (not every line).
  • Procedures and functions — split the code into named, reusable parts that can be tested and changed separately.
  • Relevant program layout — consistent indentation, blank lines between sections, constants at the top.

These make code easier for another programmer (or you, later) to understand, debug, update and extend.

Exam tips

  • Name the check precisely and link it to the data: 'a range check on the mark so it is between 0 and 100 inclusive'.
  • Validation code should loop until valid data is entered and output an error message — a single if loses marks.
  • Boundary test data includes both the limits and just outside them; always give expected results.
  • Know the difference: validation = sensible/reasonable; verification = accurately copied (visual check, double entry).
  • For error types, give a specific example from the code in the question.
  • Maintainability: name the technique AND say how it helps another programmer.

Mistakes that lose marks

  • Saying validation ensures data is correct — it only checks it is reasonable.
  • Calling double entry a validation check (it is verification).
  • Using and instead of or in an out-of-range loop condition (while mark < 0 and mark > 100 never runs).
  • Giving only one boundary value, or forgetting the expected outcome.
  • Calling a crash a logic error, or a wrong answer a run-time error.

Infographics3download any diagram as PNG or SVG

Choosing test dataRule under test: “enter a mark from 0 to 100”. Each type of test data has a job — name the type AND saywhy it is chosen.-1050100101accepted range 0 … 100rejectedrejectedTypePurposeExamples hereNormaltypical valid data the program should accept and process23, 50, 87Extremevalid data at the very limits of the accepted range0 and 100Boundaryeither side of each limit: last accepted, first rejected-1 / 0 and 100 / 101Abnormalinvalid (erroneous) data the program must reject-5, 150, “abc”, 3.7Validation: is the data sensible? range · type · length · format · presence · check digit checks.Verification: was it entered correctly? double entry, visual check.Trace table / dry run: one column per variable and the output, one row per change — the way to find logic errors.9618 adds: walkthrough (people read the code), alpha (in-house), beta (real users before release), acceptance (the client signs off).cswithzak.com

Choosing test data

O LevelAS
Python procedures, functions and scopeA procedure carries out a task; a function also returns a value. Variables made inside a subroutine arelocal to it.TAX = 0.2 # global constantdef show_line(n): # procedure print("-" * n)def with_tax(price): # function tax = price * TAX # tax is local return round(price + tax, 2)show_line(20) # call: 20 is the argumenttotal = with_tax(50)print(total) # 60.0Changing a globalcount = 0def add_one(): global count count = count + 1Built-in and library functionslen("abc") → 3 round(2.567, 1) → 2.6max([4, 9]) → 9 min([4, 9]) → 4sum([4, 9]) → 13import randomrandom.randint(1, 6) → 1 to 6from statistics import meanmean([4, 9]) → 6.5Parameter = the name in the def line (price); argument = the value passed in the call (50).cswithzak.com

Python procedures, functions and scope

O Level
Python lists and file handlingLists are indexed from 0. A 2D list is a list of lists: grid[row][column]. with open() closes the filefor you.1D and 2D listsmarks = [72, 45, 90]marks.append(66) # [72, 45, 90, 66]marks.insert(0, 50) # [50, 72, 45, 90, 66]marks.pop() # removes 66marks.sort() # [45, 50, 72, 90]print(90 in marks) # Trueprint(marks[0]) # 45grid = [[1, 2, 3], [4, 5, 6]]print(grid[1][2]) # 6 (row 1, col 2)for row in grid: print(sum(row)) # 6 then 15Fileswith open("scores.txt", "w") as f: f.write("Ali,72\n")with open("scores.txt", "a") as f: f.write("Sara,90\n")with open("scores.txt", "r") as f: for line in f.readlines(): print(line.strip())ModeMeaning"r"read — the file must already exist"w"write — creates or overwrites the file"a"append — adds to the end of the fileread() all · readline() one line · readlines() a listData written to a file stays after the program ends — variables and lists are lost when it stops.cswithzak.com

Python lists and file handling

O Level

Python for this topic17Python 3.10+, the only language on Paper 2 — runs in your browser

Key terms16use these exact words in the exam

local variableglobal variableprocedurefunctionparameterlist2D listappendfile modewith openvalidationdouble entryboundary datasyntax errorlogic errorrun-time error

Test yourself

Check you know the 2029–2031 content

Written for the new syllabus only: every card and question traces to a learning objective above. Rounds are random, and marks earn XP on your dashboard.

6 decks · 66 cards · 22 quiz questions.

From the current course

Most of this topic is taught in the 2026–2028 course today. Its notes and past-paper questions still help — skip anything the 2029–2031 syllabus removed (see the notes above), and remember those past papers answer in pseudocode: write Python instead.

Enroll nowOnline classes