2029–2031 edition · for exams from June 2029. Students sitting exams up to November 2028 follow the current course.
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
- 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
- 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
- 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
- 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
- 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
globalkeyword.
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 >= PASSAdvantages and disadvantages
| Advantages | Disadvantages | |
|---|---|---|
| Local variables | Cannot 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 reusable | Values are lost when the subroutine ends; must be passed as parameters/returned to share them |
| Global variables | Accessible everywhere, no need to pass them around; value kept for the whole run | Can 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 constants | Kept close to where they are used; no clash with other names | Cannot be shared; may be repeated in several places |
| Global constants | One place to update a value used everywhere; cannot be changed by the program | Use 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
returnto 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
| Function | Purpose |
|---|---|
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 input2D 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 True4
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
| Mode | Meaning | If the file exists | If not |
|---|---|---|---|
"r" | Read | Reads from the start | Error |
"w" | Write | Wipes it and starts again | Creates it |
"a" | Append | Adds to the end | Creates 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 withstr()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.
| Check | What it does | Example |
|---|---|---|
| Range | Value lies between limits | Mark 0–100 |
| Length | Number of characters is right (exact, or min/max) | Password at least 8 characters; PIN exactly 4 |
| Type | Data is the right data type | Age is a whole number |
| Presence | Something has been entered (not left blank) | Surname must be filled in |
| Format | Data matches a pattern | Code = two letters then four digits (AB1234); date DD/MM/YYYY |
| Check digit | Extra digit calculated from the others and added to the end; recalculated on entry to detect typing/transposition errors | ISBN, 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:
| Type | Meaning | Examples | Expected result |
|---|---|---|---|
| Normal | Sensible data that should be accepted | 45, 72 | Accepted |
| Boundary | The largest and smallest acceptable values and the values just outside them | 0 and 100 (accepted); −1 and 101 (rejected) | As stated |
| Erroneous | Data 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
| Error | What it is | Example | When found |
|---|---|---|---|
| Syntax | Breaks the rules of the language, so the program will not run | print("Hi" (missing bracket), missing colon after if | Before running (the translator reports it) |
| Logic | Program runs but gives the wrong result | range(1, 5) when 1–5 was needed; > instead of >=; average = total / 10 when 12 values | Only by testing with known expected results |
| Run-time | Program starts but crashes while running | Dividing 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_pricenottp, 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 data
Python procedures, functions and scope
Python lists and file handling
Python for this topic17Python 3.10+, the only language on Paper 2 — runs in your browser
- Local and global variables and constants2029 styleVAT_RATE is a global constant and basket_total a global variable; DISCOUNT and vat only exist inside their functions. To change a global inside a function, write global first.
- Local and global scopeA variable assigned inside a function is local. global makes a function change a module-level variable — usually better to return a value instead.
- Functions and procedures — def and returnA procedure is a def with no return; a function returns a value. Parameters are passed by value for numbers/strings.
- sum, count, max, min, mean, round and random2029 styleThe built-in and library functions the syllabus lists. randint(1, 6) can give 6; randrange(1, 14) stops at 13 because the end value is not included.
- Lists — the Python arrayCreate, index, change, append, and loop. Remember index 0. len() gives the size.
- Lists — append, insert, pop, sort, in and 2D lists2029 styleList indexes start at 0. append adds to the end, insert(index, value) moves the rest up, pop() removes the last item, sort(reverse=True) sorts descending, in / not in test membership.
- 2D list — seating planA list of lists. seat[row][col]. Build it with a comprehension so each row is a separate list.
- Linear searchWalk the list until you find the target — the exam wants the loop, not list.index().
- Bubble sort with a swapped flagThe classic Paper 2 / Paper 4 sort. Open the Trace tab to watch the swaps.
- Files — w, a and r with read, readline and readlines2029 styleMode w overwrites, a adds to the end, r reads. Method 1 opens and closes the file yourself; method 2 (with open) closes it when the indented block ends.
- Write then read a text fileopen() with 'w', 'a' and 'r'; with closes the file for you. The file appears in the Files panel after the run.
- Read until end of file, count and totalreadline() returns '' at EOF — the Python version of WHILE NOT EOF. Also readlines().
- Validation checks in Python — range, length, presence, format, type2029 styleEach check as a function returning True or False, including an inclusive range (0 and 100 allowed) and an exclusive one (4 and 19 rejected); then a loop that keeps asking until the input passes.
- Validation loop — range and type checksKeep asking until the input is a whole number between 1 and 100. try/except catches the ValueError from int().
- Verification — a double entry check2029 styleVerification checks the data was entered as intended: the user types it twice and the program compares the two entries, asking again until they match.
- Spot the logic error — an average that is too low2029 styleThe first loop runs without crashing but gives the wrong answer (a logic error): range(1, …) skips index 0. Run it, find the error with the trace table, then compare with the corrected loop.
- Scenario task: sports day results2029 styleA Paper 2-style scenario in separate parts: a constant, two lists, a validating function, a presence check, a linear search for the winner, totalling and counting, and the results written to a file.
Key terms16use these exact words in the exam
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.
- 8. Programming2026–2028 topic · 324 past-paper questions
- 7. Algorithm Design and Problem-Solving2026–2028 topic · 304 past-paper questions

