Spec checklist
Rate each point. Be honest: 'Secure' means you could answer an exam question on it now without notes.
| 2.1.1 | Define integer, float, string and Boolean and say when each is used |
| 2.2.1–2 | Define variables and constants, their purpose and when they are used |
| 2.2.3 | Explain why data type conversion functions are used |
| 2.2.4–6 | Define scope, explain global and local variables, and use scope in code |
| 2.2.7–9 | Declare and use variables and constants; use int(), float(), str(), bool() |
| 2.3.1 | Describe lists, arrays and dictionaries and when each is used |
| 2.3.2–4 | Interpret, develop and debug code that uses data structures |
| 2.4.1–3 | Explain arithmetic, relational and Boolean operators |
| 2.4.4–7 | Use, interpret, create and debug code using operators |
| 2.5.1–2 | Implement keyboard, screen and text file input and output |
| 2.5.3–5 | Interpret, create and debug input/output code |
| 2.6.1–4 | Explain sequence, selection (if, elif, else, match/case) and iteration (for, while) |
| 2.6.5 | Give benefits and drawbacks of loops |
| 2.6.6–8 | Interpret, develop and debug code using sequence, selection and iteration |
| 2.7.1–2 | Describe the characteristics of functions and procedures |
| 2.7.3–4 | Say where functions and procedures come from; benefits and drawbacks of pre-written code |
| 2.7.5–7 | Interpret, develop and debug user-written and pre-written functions and procedures |
| 2.8.1 | Define validation and explain presence, length, range, type, format and check digit checks |
| 2.8.2–4 | Interpret, develop and debug validation code |
| 2.9.1–4 | Decide the logical order of actions and operations; choose data structures for time and memory efficiency |
| 2.9.5–6 | Explain naming conventions (meaningful names, camelCase, snake_case) and code style |
| 2.9.7 | Judge the suitability of an algorithm |
| 2.10.1 | Describe the characteristics of robust code |
| 2.10.2–5 | Explain debugging; locate and correct errors in code |
| 2.11.1–2 | Explain how linear search, binary search, bubble sort, insertion sort and merge sort work |
| 2.11.3–5 | Compare algorithms using memory, execution time, comparisons, and best/worst/average case |
| 2.11.6–7 | Judge which searching or sorting algorithm suits a situation |
| 2.12.1 | Explain why components are tested separately before being combined |
| 2.12.2 | Define and use the testing methods (concept to open box) |
| 2.12.3 | Explain macros, scripts and functional testing tools for automation |
| 2.12.4 | Create valid, invalid, boundary and erroneous test data and write a test plan |
2.1 Standard data types
Every value in a program has a data type. The data type decides what the value can store and what you can do with it.
| Data type | Python | What it stores | Example values | Use it for |
|---|---|---|---|---|
| Integer | int | Whole numbers | 42, -7, 0 | Counting, ages, quantities, scores, loop counters |
| Float | float | Numbers with a decimal part | 3.99, -0.5, 2.0 | Prices, measurements, averages, temperatures |
| String | str | Text: letters, digits, symbols | "Leeds", "07700 900123" | Names, addresses, postcodes, phone numbers |
| Boolean | bool | One of two values | True, False | Flags and yes/no states: logged_in, in_stock |
quantity = 3 # int price = 4.75 # float product = "Meal deal" # str in_stock = True # bool print(type(quantity), type(price), type(product), type(in_stock))
<class 'int'> <class 'float'> <class 'str'> <class 'bool'>
✗ COMMON MISTAKE
- Phone numbers and postcodes are strings, not integers. You never do maths on them, and an integer would drop a leading zero (07700 becomes 7700).
"42"in quotes is a string.42without quotes is an integer."4" + "2"gives"42", not 6.- Money is often stored as a float in exam answers, but floats can give tiny rounding errors:
0.1 + 0.2gives0.30000000000000004. Real payment systems often store pence as an integer.
? QUICK CHECK
- Which data type would you use for: a student's date of birth day number; a bank balance; an email address; whether a user has accepted cookies?
- Why is a mobile number stored as a string?
- Integer
- A whole number, positive, negative or zero. Python type int.
- Float
- A number with a decimal (fractional) part. Python type float.
- String
- A sequence of characters, written in quotes. Python type str.
- Boolean
- A value that is either True or False. Python type bool.
2.2 Variables, constants, conversion and scope
Variables and constants
| Variable | Constant | |
|---|---|---|
| Definition | A named memory location whose value can change while the program runs. | A named value that does not change while the program runs. |
| Purpose | Store data the program needs to work with and update, e.g. a running total. | Store a fixed value once, so it is easy to read and to update in one place. |
| Python example | score = 0 then score = score + 10 | VAT_RATE = 0.2 |
| When used | User input, totals, counters, results of calculations. | Tax rates, maximum login attempts, pi, file names. |
Python has no built-in way to lock a constant. By convention, a name in CAPITALS tells other programmers not to change it.
MAX_ATTEMPTS = 3 # constant
attempts = 0 # variable
attempts = attempts + 1
print(f"Attempt {attempts} of {MAX_ATTEMPTS}")
Attempt 1 of 3
★ EXAM TIP
Benefit of constants: if the VAT rate changes, you change it on one line and every calculation updates. This avoids missing one copy of 0.2 buried in the code and makes the program easier to maintain.
Data type conversion (casting)
input() always returns a string. To do maths with it, convert it first. Conversion is also needed to join numbers into strings.
| Function | Converts to | Example | Result |
|---|---|---|---|
int() | Integer | int("25") / int(7.9) | 25 / 7 (decimal part is cut off, not rounded) |
float() | Float | float("3.5") / float(4) | 3.5 / 4.0 |
str() | String | str(99) | "99" |
bool() | Boolean | bool(0) / bool("hello") / bool("") | False / True / False |
age = int(input("Enter your age: "))
print("Next year you will be " + str(age + 1))
Enter your age: 17 Next year you will be 18
✗ COMMON MISTAKE
int("3.5")crashes with a ValueError. Convert to float first:int(float("3.5"))gives 3.bool("False")is True. Any non-empty string converts to True.print("Total: " + 10)crashes with a TypeError. Usestr(10)or an f-string.
Scope: global and local variables
Scope is the part of the program where a variable can be used.
- A global variable is created outside any function. It can be read anywhere in the program.
- A local variable is created inside a function. It only exists while the function is running and cannot be used outside it.
- To change a global variable inside a function, Python needs the
globalkeyword.
basket_total = 0 # global
def add_item(price):
global basket_total
discount = 0.1 # local: only exists inside add_item
basket_total = basket_total + price * (1 - discount)
add_item(20)
add_item(10)
print(basket_total)
27.0
| Global variables | Local variables | |
|---|---|---|
| Benefits | Available everywhere, so useful for values many parts of the program need (e.g. settings). | Can't be changed by accident elsewhere. The same name can be reused in different functions. Memory is freed when the function ends. Functions are self-contained and easier to test and reuse. |
| Drawbacks | Any function can change them, so bugs are hard to trace. Use memory for the whole run. Make functions depend on outside values. | Values are lost when the function ends unless returned. |
★ EXAM TIP
Good practice is to use local variables and pass values in as parameters and back out with return. If asked why, say: it reduces side effects, makes code easier to debug, and makes functions reusable.
- Variable
- A named location in memory that stores a value which can change while the program runs.
- Constant
- A named value that does not change while the program runs. In Python, written in CAPITALS by convention.
- Casting / type conversion
- Changing a value from one data type to another, e.g. int("42").
- Scope
- The part of a program where a variable can be accessed.
- Global variable
- A variable declared outside any function, which can be read anywhere in the program.
- Local variable
- A variable declared inside a function. It only exists while that function runs.
2.3 Data structures
| Structure | Description | Python | Use it when |
|---|---|---|---|
| List | Ordered, changeable, can hold mixed types, grows and shrinks. | orders = ["latte", "tea", 3] | You have a collection that changes size, e.g. a shopping basket. |
| Array | Ordered collection where every item is the same data type. More memory efficient. | import arraytemps = array.array("f", [12.5, 13.0]) | You store lots of values of one type, e.g. sensor readings. In exams, a list of one type is often used as an array. |
| Dictionary | Unordered by position; stores key: value pairs. Keys must be unique. | stock = {"cola": 12, "water": 30} | You need to look something up by a label, e.g. product code to price, username to password hash. |
List and dictionary operations (Appendix 2)
queue = ["Asha", "Ben", "Chloe"]
queue.append("Dev") # add to end
queue.insert(0, "Ellie") # add at index 0
queue.remove("Ben") # remove by value
first = queue.pop(0) # remove and return item at index 0
print(first, queue, len(queue))
print(queue.index("Chloe"), "Dev" in queue, "Ben" not in queue)
queue.sort()
print(queue)
Ellie ['Asha', 'Chloe', 'Dev'] 3 1 True True ['Asha', 'Chloe', 'Dev']
prices = {"latte": 3.20, "tea": 1.80}
prices["mocha"] = 3.50 # add a new key
prices["tea"] = 1.95 # update an existing key
for drink, price in prices.items():
print(f"{drink}: £{price:.2f}")
print("cocoa" in prices)
latte: £3.20 tea: £1.95 mocha: £3.50 False
2D lists
A list can hold other lists, which works like a table. Use two indexes: [row][column].
seats = [["A1", "A2", "A3"],
["B1", "B2", "B3"]]
print(seats[1][2])
for row in seats:
print(len(row), row[0])
B3 3 A1 3 B1
✗ COMMON MISTAKE
- Indexes start at 0. In a list of 5 items, the last index is 4.
items[5]raises an IndexError. - Looking up a key that does not exist in a dictionary raises a KeyError. Check with
if key in my_dict:first.
Worked example: debugging a data structure
This code should print the total of all marks. It crashes. Find and fix the error.
marks = [45, 67, 89, 72]
total = 0
for i in range(1, len(marks) + 1):
total = total + marks[i]
print(total)
Found it? Click to check.
Error: range(1, len(marks) + 1) produces 1, 2, 3, 4. Index 4 does not exist, so an IndexError occurs on the last loop. It also skips index 0. Fix: use range(len(marks)) or loop directly over the list:
marks = [45, 67, 89, 72]
total = 0
for mark in marks:
total = total + mark
print(total)
273
- List
- An ordered, changeable collection of items that can hold mixed data types and grow or shrink.
- Array
- An ordered collection of items that are all the same data type.
- Dictionary
- A collection of key–value pairs. Values are looked up by key rather than position.
- Index
- The position of an item in a list, array or string. Python starts counting at 0.
2.4 Operators
Arithmetic operators
| Operator | Name | Example | Result | Typical use |
|---|---|---|---|---|
+ | Add | 7 + 2 | 9 | Totals |
- | Subtract | 7 - 2 | 5 | Change given, stock left |
* | Multiply | 7 * 2 | 14 | Price × quantity |
/ | Divide | 7 / 2 | 3.5 (always a float) | Averages |
** | Exponentiation | 7 ** 2 | 49 | Powers, compound interest |
// | Integer division | 7 // 2 | 3 | How many full boxes, whole hours from minutes |
% | Modulus (remainder) | 7 % 2 | 1 | Odd/even, items left over, wrapping around |
Worked example: // and %
minutes = 135
hours = minutes // 60
left_over = minutes % 60
print(f"{hours} h {left_over} min")
eggs = 26
print("Full boxes:", eggs // 6, "Spare eggs:", eggs % 6)
2 h 15 min Full boxes: 4 Spare eggs: 2
Relational operators
Relational operators compare two values and always give a Boolean result.
| Operator | Meaning | Example | Result |
|---|---|---|---|
== | Equal to (equivalence) | 5 == 5 | True |
!= | Not equal to | 5 != 3 | True |
< | Less than | 3 < 5 | True |
> | Greater than | 3 > 5 | False |
<= | Less than or equal to | 5 <= 5 | True |
>= | Greater than or equal to | 4 >= 5 | False |
✗ COMMON MISTAKE
= assigns a value. == compares. if age = 18: is a syntax error.
Boolean operators
| Operator | True when... | Example |
|---|---|---|
and | both conditions are True | age >= 18 and has_id |
or | at least one condition is True | day == "Sat" or day == "Sun" |
not | the condition is False (it reverses it) | not logged_in |
| A | B | A and B | A or B | not A |
|---|---|---|---|---|
| True | True | True | True | False |
| True | False | False | True | False |
| False | True | False | True | True |
| False | False | False | False | True |
Order of operations
Python follows BIDMAS: brackets, then **, then * / // %, then + -. Then relational operators, then not, and, or.
print(2 + 3 * 4) print((2 + 3) * 4) print(10 - 4 / 2) mark_a, mark_b = 60, 80 print(mark_a + mark_b / 2) # logic error print((mark_a + mark_b) / 2) # correct average
14 20 8.0 100.0 70.0
▲ STRETCH
Explain why (mark_a + mark_b) / 2 must use brackets, and why the wrong version does not produce an error message. (It is a logic error: the code is valid Python, so it runs, but the output is wrong.)
2.5 Input and output
Keyboard and screen
input("prompt")displays the prompt, waits for the user to press Enter and returns what they typed as a string.print()outputs to the screen. Separate items with commas, join strings with+, or use an f-string..format()and f-strings can format numbers:{price:.2f}shows 2 decimal places.
name = input("Name: ")
hours = float(input("Hours worked: "))
rate = 11.44
pay = hours * rate
print(f"{name} earned £{pay:.2f}")
print("{} earned £{:.2f}".format(name, pay))
Name: Sam Hours worked: 16 Sam earned £183.04 Sam earned £183.04
Text files
| Step | Code | Notes |
|---|---|---|
| Open for writing | f = open("log.txt", "w") | "w" creates the file or overwrites it. "a" appends to the end. |
| Write lines | f.write("Login OK\n") | write() does not add a new line. Add \n yourself. Only strings can be written. |
| Close | f.close() | Saves the data and frees the file. Data may be lost if you forget. |
| Open for reading | f = open("log.txt", "r") | Raises FileNotFoundError if the file does not exist. |
| Read | f.read() / f.readline() / f.readlines() | Whole file as one string / one line / list of lines. |
scores = {"Asha": 18, "Ben": 12, "Chloe": 20}
f = open("scores.txt", "w")
for name, score in scores.items():
f.write(name + "," + str(score) + "\n")
f.close()
f = open("scores.txt", "r")
for line in f.readlines():
parts = line.strip().split(",")
print(parts[0], "scored", int(parts[1]))
f.close()
Asha scored 18 Ben scored 12 Chloe scored 20
line.strip() removes the \n and spaces from each end. line.split(",") breaks the line into a list at every comma.
★ EXAM TIP
You may also see with open("scores.txt", "r") as f:. This closes the file automatically when the indented block ends, even if an error happens. It is good practice, and worth mentioning in a 'robust code' answer.
✗ COMMON MISTAKE
- Opening an existing file in
"w"mode wipes it. Use"a"to add to it. f.write(score)crashes if score is an integer. Usestr(score).- Forgetting
.strip()leaves\non the end, so"Ben\n" == "Ben"is False.
2.6 Actions: sequence, selection and iteration
| Construct | What it does | When it is used |
|---|---|---|
| Sequence | Runs instructions one after another, in order. | Always. The order matters: you must get input before you process it. |
| Selection | Chooses which block of code to run based on a condition. | When the program needs to make a decision: grade boundaries, menu choices, login checks. |
| Iteration | Repeats a block of code. | When something needs doing more than once: processing every item in a list, re-asking for valid input. |
Selection: if / elif / else
mark = 64
if mark >= 70:
grade = "Distinction"
elif mark >= 55:
grade = "Merit"
elif mark >= 40:
grade = "Pass"
else:
grade = "Fail"
print(grade)
Merit
Conditions are checked from the top. The first True one runs and the rest are skipped. That is why the highest boundary goes first: if mark >= 40 came first, 64 would get a Pass.
Selection: match / case (Python 3.10+)
match compares one value against several possible patterns. It is tidier than a long elif chain when you are checking a single value for exact matches, such as a menu choice.
choice = "2"
match choice:
case "1":
print("View basket")
case "2":
print("Checkout")
case "3" | "q":
print("Quit")
case _:
print("Invalid option")
Checkout
case _: is the default and works like else. | means 'or'. match cannot do range checks like mark >= 70 without extra syntax, so use if for those.
Iteration
| Loop | Type | Python | Use when |
|---|---|---|---|
| Count-controlled | Repeats a fixed number of times | for i in range(5):for item in basket: | You know how many times before the loop starts: every item in a list, 10 questions. |
| Condition-controlled | Repeats while a condition is True ('while do') | while password != "admin123": | You don't know how many times: until valid input, until the user quits. |
for i in range(1, 11, 3):
print(i, end=" ")
print()
total = 0
count = 0
while total < 20:
total = total + 7
count = count + 1
print(total, count)
1 4 7 10 21 3
range(start, stop, step): stop is not included. range(5) gives 0, 1, 2, 3, 4.
Benefits and drawbacks of loops (2.6.5)
| Benefits | Drawbacks |
|---|---|
|
|
Worked example: trace table
A trace table records the value of each variable as the code runs, line by line. It is the best way to answer 'what is the output' questions. Draw your own before you check.
n = 5
result = 1
while n > 1:
result = result * n
n = n - 2
print(result)
15
Show the trace table
| n | result | n > 1? | Output |
|---|---|---|---|
| 5 | 1 | True | |
| 3 | 5 | True | |
| 1 | 15 | False | |
| 15 |
? QUICK CHECK
- Rewrite the loop above as a
forloop usingrange()with a step. - Why is a
whileloop the right choice for asking a user to re-enter a PIN until it is correct?
- Sequence
- Instructions run one after another in the order written.
- Selection
- Choosing which code to run based on a condition (if, elif, else, match/case).
- Iteration
- Repeating a block of code (a loop).
- Count-controlled loop
- A loop that repeats a set number of times (for).
- Condition-controlled loop
- A loop that repeats while a condition is True (while).
2.7 Functions and procedures
| Function | Procedure | |
|---|---|---|
| Parameters | May or may not take parameters | May or may not take parameters |
| Returns a value? | Must return a result using return | Must not return a result |
| Example use | Calculate VAT and return it; validate an email and return True/False | Display a menu; write a line to a log file |
def calculate_vat(price, rate=0.2): # function: returns a value
return round(price * rate, 2)
def print_receipt(item, price): # procedure: no return value
vat = calculate_vat(price)
print(f"{item:<10} £{price:>6.2f} VAT £{vat:.2f}")
print_receipt("Keyboard", 24.99)
print_receipt("Mouse", 9.5)
Keyboard £ 24.99 VAT £5.00 Mouse £ 9.50 VAT £1.90
price and rate are parameters. 24.99 is an argument: the actual value passed in. rate=0.2 is a default value used if no argument is given.
Where functions and procedures come from (2.7.3)
| Source | Example |
|---|---|
| User-written | calculate_vat() above |
| Pre-written, built into the language | print(), len(), round(), max(), min(), range(), ord(), chr() |
| Pre-written, in libraries supplied with the language (standard library) | math.sqrt(), math.floor(), math.ceil(), random.randint(), datetime.now() |
| Pre-written, in third-party libraries | pandas, matplotlib, requests (installed with pip) |
import math
import random
print(math.floor(7.8), math.ceil(7.2), math.trunc(-7.8))
print(math.sqrt(81), math.pow(2, 5), round(math.pi, 3))
roll = random.randint(1, 6)
print(1 <= roll <= 6)
print(ord("A"), chr(66), max([4, 9, 2]), min(4, 9, 2))
7 8 -7 9.0 32.0 3.142 True 65 B 9 2
Benefits and drawbacks of pre-written code (2.7.4)
| Benefits | Drawbacks |
|---|---|
|
|
★ EXAM TIP
Benefits of writing your own subroutines (user-written): reusable, easier to test each part on its own (unit testing), easier to read, and a team can work on different subroutines at the same time.
String handling methods (Appendix 2)
email = " Sam.Jones@Example.co.uk "
email = email.strip().lower()
print(email)
print(email.find("@"), email.index("."), email.count("o"))
user, domain = email.split("@")
print(user, domain, len(user))
print("abc123".isalnum(), "abc".isalpha())
print("2024".isdigit(), "ABC".isupper())
print(email.replace("example", "sample"))
sam.jones@example.co.uk 9 3 2 sam.jones example.co.uk 9 True True True True sam.jones@sample.co.uk
- Function
- A named subroutine that may take parameters and must return a value.
- Procedure
- A named subroutine that may take parameters and does not return a value.
- Parameter
- A variable listed in a subroutine definition that receives a value when the subroutine is called.
- Argument
- The actual value passed to a subroutine when it is called.
- Library
- A collection of pre-written code that can be imported, e.g. math, random.
2.8 Validation
Validation is an automatic check that input data is sensible and follows set rules before it is accepted. It reduces errors and stops a program crashing on bad data. It cannot check that data is correct: a valid date of birth may still be the wrong one.
| Check | What it checks | Example |
|---|---|---|
| Presence | Something has been entered | Surname cannot be left blank |
| Length | Number of characters is within limits | Password at least 8 characters; mobile number exactly 11 digits |
| Range | A value is between a lower and upper limit | Age 16–99; quantity 1–10 |
| Type | Data is the right data type | Quantity must be a whole number |
| Format | Data matches a pattern | Postcode like LS1 4AP; email contains @ and a dot after it |
| Check digit | A calculated digit at the end matches the rest of the number | ISBN-13 on books, barcodes, bank card numbers |
def get_quantity():
while True:
entry = input("Quantity (1-10): ")
if entry == "": # presence check
print("Error: quantity cannot be blank.")
elif not entry.isdigit(): # type check
print("Error: enter a whole number.")
elif not 1 <= int(entry) <= 10: # range check
print("Error: quantity must be between 1 and 10.")
else:
return int(entry)
qty = get_quantity()
print("Accepted:", qty)
Quantity (1-10): Error: quantity cannot be blank. Quantity (1-10): two Error: enter a whole number. Quantity (1-10): 15 Error: quantity must be between 1 and 10. Quantity (1-10): 4 Accepted: 4
def valid_password(pw):
return len(pw) >= 8 # length check
def valid_email(email): # simple format check
if email.count("@") != 1:
return False
user, domain = email.split("@")
return len(user) > 0 and "." in domain
print(valid_password("letmein"))
print(valid_email("ali@wakefield.ac.uk"))
print(valid_email("ali.wakefield"))
False True False
Check digit worked example: ISBN-13
Multiply the 13 digits alternately by 1 and 3 and add the results. If the total divides exactly by 10, the number is valid. A single mistyped digit or two swapped digits will usually fail.
def valid_isbn13(isbn):
if len(isbn) != 13 or not isbn.isdigit():
return False
total = 0
for i in range(13):
weight = 1 if i % 2 == 0 else 3
total = total + int(isbn[i]) * weight
return total % 10 == 0
print(valid_isbn13("9780306406157"))
print(valid_isbn13("9780306406158"))
True False
Try it: ISBN-13 checker
Try changing one digit, or swapping two neighbouring digits.
✗ COMMON MISTAKE
Range checks: if 1 < qty < 10 rejects 1 and 10. If the limits are allowed, use <=. Boundary test data catches exactly this error.
- Validation
- Automatic checks that input data is sensible and follows rules before it is accepted. It does not check data is correct.
- Check digit
- An extra digit calculated from the other digits in a number, used to detect input errors.
2.9 Design considerations and programming practices
Order of actions and operations (2.9.1–2.9.2, 2.9.4)
- Actions must happen in a logical order: get input, validate it, process it, output the result. You cannot calculate a total before reading the prices.
- Initialise variables before a loop. Setting
total = 0inside the loop resets it every time. - Use brackets so calculations follow the order you intend. This keeps outputs accurate and avoids logic errors.
- Check for zero before dividing:
if count > 0: average = total / count. This avoids a ZeroDivisionError. - For efficiency, do work once rather than inside a loop. Calculate
len(items)once before the loop; put the most likely condition first in anifchain; stop a search as soon as the item is found.
Choosing data structures for efficiency (2.9.3)
| Situation | Better choice | Why |
|---|---|---|
| Look up a price by product code thousands of times | Dictionary | Looking up a key is direct; a list would need searching item by item (slower execution time). |
| Store 1 million temperature readings | Array of floats | All the same type, stored compactly, so less memory than a list of mixed objects. |
| A basket that grows and shrinks | List | Easy to append and remove items. |
| Fixed values that never change | Constants (or a tuple) | Clear and protected from accidental change. |
Naming conventions (2.9.5)
| Convention | Example | Notes |
|---|---|---|
| Meaningful names | total_price not tp or x | Code explains itself; fewer comments needed; easier to maintain. |
| camelCase | totalPrice, getUserName() | Used in Java, C# and JavaScript. |
| snake_case | total_price, get_user_name() | Python's style guide (PEP 8) recommends this for variables and functions. |
| CAPITALS | MAX_SIZE | Constants in Python. |
Code style and readability (2.9.6)
- Naming conventions: consistent names let any developer guess what a variable holds.
- White space: blank lines between functions, spaces around operators (
x = a + b), and consistent indentation make structure obvious. In Python, indentation is required, not optional. - Maximum line length: PEP 8 suggests 79 characters. Short lines can be read without scrolling sideways and are easier to compare side by side.
Hard to read:
def a(b,c):
d=b*c
if d>100:d=d*0.9
return d
print(a(12,10))
108.0
Same logic, readable:
BULK_DISCOUNT = 0.9
BULK_THRESHOLD = 100
def order_cost(unit_price, quantity):
cost = unit_price * quantity
if cost > BULK_THRESHOLD:
cost = cost * BULK_DISCOUNT
return cost
print(order_cost(12, 10))
108.0
Judging an algorithm (2.9.7)
When asked to judge the suitability of an algorithm or program, comment on each of these:
| Criterion | Questions to ask |
|---|---|
| Meets requirements | Does it do everything the brief asks? Does it handle all cases, including edge cases? |
| Efficiency: storage | Does it keep unnecessary copies of data? Is the data structure memory efficient? |
| Efficiency: execution time | Does it repeat work? Does it stop early when it can? Are there unnecessary nested loops? |
| Data structures and types | Is a dictionary used where lookups are needed? Are prices floats and counts integers? |
| Variables and constants | Are fixed values constants? Are variables local where possible? |
| Presentation | Is output clear and formatted? Are prompts and error messages helpful? |
| Maintainability | Meaningful names, comments, subroutines, consistent style, constants instead of magic numbers. |
- camelCase
- Naming style where words are joined and each word after the first starts with a capital: totalPrice.
- snake_case
- Naming style where words are lower case and joined with underscores: total_price. Recommended for Python variables.
2.10 Robust code
Characteristics of robust code (2.10.1)
- Handles unexpected inputs: validates input and re-prompts instead of crashing. Letters typed into a number field are rejected.
- Handles unexpected terminations: uses
try/exceptto catch runtime errors (missing file, network down, divide by zero) and closes files or saves data safely. - Produces specific and meaningful error messages: 'Quantity must be between 1 and 10' tells the user what to fix. 'Error' does not.
def read_total(filename):
try:
with open(filename, "r") as f:
values = [float(line.strip()) for line in f]
return sum(values)
except FileNotFoundError:
print(f"Error: '{filename}' was not found. Check the file name.")
except ValueError:
print(f"Error: '{filename}' contains a line that is not a number.")
return None
with open("sales.txt", "w") as f:
f.write("12.50\n8.25\nten\n")
print(read_total("sale.txt"))
print(read_total("sales.txt"))
Error: 'sale.txt' was not found. Check the file name. None Error: 'sales.txt' contains a line that is not a number. None
Types of error
| Error type | What happens | Example |
|---|---|---|
| Syntax | Code breaks the language rules. It will not run at all. | Missing colon: if x > 5; missing bracket; pritn(); bad indentation |
| Runtime | Program starts but crashes when the line runs. | 10 / 0 (ZeroDivisionError); int("abc") (ValueError); items[10] (IndexError); missing file (FileNotFoundError) |
| Logic | Program runs but gives the wrong result. No error message. | > instead of >=; wrong order of operations; total reset inside loop |
Debugging (2.10.2–2.10.5)
Debugging is locating errors and then correcting them. It is used during development, after a test fails and when users report faults. Debugging makes code robust because each error found and fixed is one less way for the program to crash or give wrong results.
| Technique | How it helps |
|---|---|
| Read the error message | Python gives the error type and line number. |
| Trace tables / dry runs | Follow the code by hand to find where values go wrong. Essential for logic errors. |
| Print statements | Print variable values at key points to see what is really happening. |
| IDE breakpoints | Pause the program at a chosen line and inspect variables. |
| Stepping | Run one line at a time and watch the flow through selection and loops. |
| Test with planned data | Valid, invalid, boundary and erroneous data show which cases fail. |
Worked example: locate and correct
This program should count how many students passed (40 or more) and print the pass rate. It contains three errors. Find them before you check.
marks = [38, 40, 72, 55, 19]
for mark in marks:
passed = 0
if mark > 40:
passed = passed + 1
rate = passed / len(marks) * 100
print("Pass rate: " + rate + "%")
Show the three errors and the corrected code
| Line | Error | Type | Correction |
|---|---|---|---|
| 3 | passed = 0 inside the loop resets the count each time | Logic | Move it above the for line |
| 4 | > 40 excludes a mark of exactly 40 | Logic | if mark >= 40: |
| 7 | Cannot join a float to a string with + | Runtime (TypeError) | print("Pass rate: " + str(rate) + "%") |
Corrected:
marks = [38, 40, 72, 55, 19]
passed = 0
for mark in marks:
if mark >= 40:
passed = passed + 1
rate = passed / len(marks) * 100
print("Pass rate: " + str(rate) + "%")
Pass rate: 60.0%
- Robust code
- Code that handles unexpected inputs and terminations and gives specific, meaningful error messages.
- Debugging
- Locating and correcting errors in code.
- Syntax error
- Code that breaks the rules of the language, so it cannot run.
- Runtime error
- An error that happens while the program is running, causing it to crash, e.g. dividing by zero.
- Logic error
- The program runs but gives the wrong result.
- Exception
- An error object raised at runtime. Can be caught with try/except.
2.11 Common algorithms
You need to know how each algorithm works, be able to trace it, and judge when to use it. Big O notation is not required: compare using logical reasoning about comparisons, time and memory.
Try it: algorithm stepper
Pick an algorithm, type a list, then press Step to watch it work one comparison at a time.
Yellow = being compared · Green = in its final place / found · Faded = discarded
Linear search
- Start at the first item.
- Compare it with the target.
- If it matches, stop and return the position.
- Otherwise move to the next item.
- If you reach the end without a match, the item is not there.
def linear_search(items, target):
for i in range(len(items)):
if items[i] == target:
return i
return -1
print(linear_search(["Leeds", "York", "Hull", "Wakefield"], "Hull"))
print(linear_search(["Leeds", "York"], "Bradford"))
2 -1
Binary search
The list must be sorted first.
- Find the middle item.
- If it is the target, stop.
- If the target is smaller, discard the middle item and everything above it.
- If the target is larger, discard the middle item and everything below it.
- Repeat on the half that is left until the target is found or nothing is left.
def binary_search(items, target):
low = 0
high = len(items) - 1
while low <= high:
mid = (low + high) // 2
if items[mid] == target:
return mid
elif target < items[mid]:
high = mid - 1
else:
low = mid + 1
return -1
numbers = [3, 8, 12, 19, 25, 31, 40, 47, 52]
print(binary_search(numbers, 40))
6
| Step | low | high | mid | items[mid] | Action |
|---|---|---|---|---|---|
| 1 | 0 | 8 | 4 | 25 | 40 > 25, so low = 5 |
| 2 | 5 | 8 | 6 | 40 | Found at index 6 |
Linear search would have needed 7 comparisons to find 40. Binary search needed 2.
Bubble sort
- Compare the first two items. If they are in the wrong order, swap them.
- Move along one place and compare the next pair. Repeat to the end of the list: this is one pass.
- After each pass the largest unsorted item has 'bubbled' to its correct place at the end.
- Repeat passes until a whole pass makes no swaps.
def bubble_sort(items):
n = len(items)
swapped = True
while swapped:
swapped = False
for i in range(n - 1):
if items[i] > items[i + 1]:
items[i], items[i + 1] = items[i + 1], items[i]
swapped = True
n = n - 1
return items
print(bubble_sort([5, 1, 4, 2, 8]))
[1, 2, 4, 5, 8]
| After pass | List | Swaps made |
|---|---|---|
| Start | 5 1 4 2 8 | |
| 1 | 1 4 2 5 8 | 3 (5↔1, 5↔4, 5↔2) |
| 2 | 1 2 4 5 8 | 1 (4↔2) |
| 3 | 1 2 4 5 8 | 0, so stop |
Insertion sort
- Treat the first item as a sorted list of one.
- Take the next item.
- Compare it with items in the sorted part, moving larger items one place right, until you find where it belongs.
- Insert it. Repeat until every item has been inserted.
def insertion_sort(items):
for i in range(1, len(items)):
current = items[i]
j = i - 1
while j >= 0 and items[j] > current:
items[j + 1] = items[j]
j = j - 1
items[j + 1] = current
return items
print(insertion_sort([7, 3, 9, 1, 5]))
[1, 3, 5, 7, 9]
| Insert | List afterwards (sorted part in bold) |
|---|---|
| Start | 7 3 9 1 5 |
| 3 | 3 7 9 1 5 |
| 9 | 3 7 9 1 5 |
| 1 | 1 3 7 9 5 |
| 5 | 1 3 5 7 9 |
Merge sort
- Split the list in half, then split each half again, until every list has one item (a list of one is sorted).
- Merge pairs of lists back together: compare the front item of each list and take the smaller one each time.
- Keep merging until there is one sorted list.
def merge_sort(items):
if len(items) <= 1:
return items
mid = len(items) // 2
left = merge_sort(items[:mid])
right = merge_sort(items[mid:])
merged = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
merged.append(left[i])
i = i + 1
else:
merged.append(right[j])
j = j + 1
merged = merged + left[i:] + right[j:]
return merged
print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
[3, 9, 10, 27, 38, 43, 82]
| Stage | Lists |
|---|---|
| Split | [38 27 43] [3 9 82 10] |
| Split | [38] [27 43] [3 9] [82 10] |
| Split | [38] [27] [43] [3] [9] [82] [10] |
| Merge | [38] [27 43] [3 9] [10 82] |
| Merge | [27 38 43] [3 9 10 82] |
| Merge | [3 9 10 27 38 43 82] |
Comparing algorithms (2.11.3–2.11.5)
Metrics: memory space used, execution time, and number of comparisons. The table assumes a list of n items.
| Algorithm | Best case | Worst case | Average case | Memory |
|---|---|---|---|---|
| Linear search | Target is first: 1 comparison | Target is last or missing: n comparisons | About n ÷ 2 comparisons | No extra memory |
| Binary search | Target is the middle item: 1 comparison | Keeps halving until 1 item left: about 10 comparisons for 1,000 items, 20 for 1,000,000 | Close to the worst case | No extra memory (but list must already be sorted) |
| Bubble sort | Already sorted: one pass, n − 1 comparisons, no swaps | Reverse order: every pair compared and swapped on every pass | Many passes; slow on large lists | Sorts in place, no extra memory |
| Insertion sort | Already sorted: n − 1 comparisons | Reverse order: each item moves all the way to the front | Slow on large lists; good on small or nearly sorted lists | Sorts in place, no extra memory |
| Merge sort | Same as worst: always splits and merges fully | Much faster than bubble or insertion on large lists | Consistent performance | Needs extra memory for the sub-lists |
| Algorithm | Benefits | Drawbacks |
|---|---|---|
| Linear search | Simple. Works on unsorted data. Fine for short lists. | Slow on long lists. |
| Binary search | Very fast on large lists. | Data must be sorted first, which takes time. More complex to code. |
| Bubble sort | Simple to understand and code. In place. Can detect a sorted list in one pass. | Very slow on large or reversed lists. |
| Insertion sort | Simple. In place. Fast for small or nearly sorted lists. Can sort data as it arrives. | Slow on large, unsorted lists. |
| Merge sort | Fast and consistent on large lists. | Uses more memory. More complex. Overkill for tiny lists. |
★ EXAM TIP: JUDGING SUITABILITY (2.11.6–2.11.7)
- Always link your choice to the scenario: size of the data, whether it is already sorted, how often it is searched, and memory limits.
- A phone contact list searched many times a day: keep it sorted and use binary search; the sorting cost is paid once.
- A list of 12 items searched once: linear search; sorting first would waste time.
- A leaderboard where one new score is added to an already sorted list: insertion sort.
- Sorting 2 million transaction records on a server with plenty of RAM: merge sort.
- An embedded device with very little memory: an in-place sort (insertion or bubble).
- Linear search
- Checks each item in turn from the start until the target is found or the list ends.
- Binary search
- Repeatedly checks the middle item of a sorted list and discards the half that cannot contain the target.
- Bubble sort
- Compares adjacent pairs and swaps them if they are in the wrong order, repeating passes until no swaps are made.
- Insertion sort
- Builds a sorted section one item at a time by inserting each item into its correct position.
- Merge sort
- Splits a list into single items, then merges them back together in order.
2.12 Testing
Why test components separately (2.12.1)
Each part of a solution is tested on its own before everything is put together. Faults are easier and cheaper to find and fix in a small part than in the finished system, where it is hard to tell which part caused the problem.
| Component | Why test it first | Example |
|---|---|---|
| Software | Each module must work before it is combined, so faults can be traced to one place. | Test the login function on its own. |
| Hardware | Confirms devices work and meet requirements before software depends on them. | Check a barcode scanner reads codes reliably. |
| Data | Confirms data is accurate, complete and in the right format. | Check imported product prices have no blanks or text. |
| Interfaces | Confirms components pass data correctly between each other and to users. | Check the app sends the right data to the payment API. |
| Resulting service (final product) | Confirms the whole system meets the requirements for real users. | Full test of an online order from basket to confirmation email. |
Testing methods (2.12.2)
| Method | What it is | Benefits | Drawbacks | When |
|---|---|---|---|---|
| Concept | Testing an idea or prototype with users before full development. | Finds bad ideas early, cheaply. | Prototype may not reflect the real product. | Start of a project |
| Unit | Testing one component, such as a single function, on its own. | Errors easy to locate. Can be automated. | Does not show if parts work together. | During development |
| Boundary | Testing values at and just beyond the edges of valid ranges. | Finds off-by-one errors in conditions. | Only covers limits, not general logic. | Whenever there are ranges |
| Integration | Testing that combined components work together. | Finds interface and data-passing faults. | Harder to locate which part failed. | After unit testing |
| Performance | Measuring speed and responsiveness under normal use. | Confirms the system is fast enough. | Needs realistic environment and tools. | Before release |
| System | Testing the complete system against the requirements. | Checks everything works end to end. | Time-consuming. Faults found late cost more. | After integration |
| Acceptance | The client or end users check the system meets their needs. | Confirms the client is happy before sign-off. | Users may find issues that need major changes. | Before handover |
| Usability | Watching real users try tasks to see how easy the system is to use. | Finds confusing design and accessibility issues. | Subjective. Needs representative users. | Prototype and before release |
| Regression | Re-running earlier tests after a change. | Shows a fix has not broken something else. | Repetitive; best automated. | After every change or update |
| Load / stress | Load: expected peak users. Stress: pushing beyond limits to find the breaking point. | Shows how the system copes on busy days and how it fails. | Needs special tools to simulate users. | Before launch, e.g. ticket sites |
| Closed box | Testing inputs and outputs without seeing the code (black box). | Tests from the user's view. Tester needs no code knowledge. | Some code paths may never be tested. | System and acceptance testing |
| Open box | Testing with knowledge of the code, checking every path (white box). | Can test every branch and loop. | Needs programming knowledge. Time-consuming. | Unit testing |
Automation (2.12.3)
| Method | Purpose | When used |
|---|---|---|
| Macros | Record and replay a sequence of actions, e.g. keystrokes and clicks in an application. | Repetitive tasks in applications such as entering test data into a form or spreadsheet. |
| Scripts | Code written to run tests or tasks automatically, e.g. a Python script that calls a function with 100 sets of test data and checks results. | Unit and regression testing; running the same tests after every change. |
| Functional testing tools | Software that tests whether features work as specified, often by driving the user interface automatically (e.g. Selenium, pytest). | Regression, system and load testing on larger projects. |
A simple test script:
def is_valid_age(age):
return 16 <= age <= 99
tests = [(15, False), (16, True), (50, True), (99, True), (100, False)]
for value, expected in tests:
actual = is_valid_age(value)
result = "PASS" if actual == expected else "FAIL"
print(value, "expected", expected, "got", actual, result)
15 expected False got False PASS 16 expected True got True PASS 50 expected True got True PASS 99 expected True got True PASS 100 expected False got False PASS
Test data (2.12.4.1–2.12.4.2)
Example: a cinema app accepts a number of tickets from 1 to 8.
| Type | Definition | Example data | Expected result |
|---|---|---|---|
| Valid | Data that should be accepted | 4 | Accepted |
| Invalid | Correct data type but breaks a rule, so should be rejected | 12, -3 | Rejected with error message |
| Boundary | Values at the limits and just outside them | 1 and 8 (accepted); 0 and 9 (rejected) | 1, 8 accepted; 0, 9 rejected |
| Erroneous | Wrong data type, should be rejected | "four", 2.5, blank | Rejected with error message |
Quick sort: what type of test data is it?
The cinema app accepts 1 to 8 tickets. Choose the type for each item.
Test plan (2.12.4.3)
A test plan is written before testing and completed during testing. Its steps are: identify the tests; describe the purpose of each; identify the test data; describe the expected results; record the actual results.
| No. | Purpose of test | Test data | Type | Expected result | Actual result |
|---|---|---|---|---|---|
| 1 | Check a normal number of tickets is accepted | 4 | Valid | Accepted, total shown | |
| 2 | Check upper limit is accepted | 8 | Boundary | Accepted | |
| 3 | Check just above upper limit is rejected | 9 | Boundary | Error: 'Choose 1 to 8 tickets' | |
| 4 | Check text is rejected | four | Erroneous | Error: 'Enter a whole number' | |
| 5 | Check blank entry is rejected | (blank) | Erroneous | Error: 'Enter a number of tickets' |
★ EXAM TIP
The 'actual result' column is filled in when the test is run. If actual ≠ expected, the test has failed: record what happened, fix the code, then re-test (and regression test).
- Test plan
- A document listing the tests to run, their purpose, the test data, expected results and actual results.
- Valid test data
- Data that should be accepted and processed normally.
- Invalid test data
- Data of the correct type that should be rejected because it breaks a rule, e.g. age = 150.
- Boundary test data
- Data at the edge of what is allowed, and just outside it, e.g. 0, 1, 100, 101 for a 1–100 range.
- Erroneous test data
- Data of the wrong type that should be rejected, e.g. "ten" when a number is expected.
- Unit testing
- Testing one component (e.g. a function) on its own.
- Integration testing
- Testing that components work correctly together.
- Regression testing
- Re-running earlier tests after a change to check nothing that worked before is now broken.
- Open box testing
- Testing with knowledge of the code, checking every path runs correctly (also called white box).
- Closed box testing
- Testing inputs and outputs without looking at the code (also called black box).
Knowledge organiser
DATA TYPES
- int: whole numbers
- float: decimals
- str: text (phone numbers too)
- bool: True / False
- input() always returns a string
VARIABLES AND SCOPE
- Variable changes; CONSTANT doesn't
- int() float() str() bool() convert types
- Local: inside a function only
- Global: whole program; needs
globalto change inside a function
DATA STRUCTURES
- List: ordered, changeable, mixed types
- Array: one data type, memory efficient
- Dictionary: key → value lookup
- append, insert, remove, pop, index, sort, in
OPERATORS
- + - * / ** // %
- // integer division, % remainder
- == != < > <= >=
- and, or, not
- BIDMAS: use brackets
ACTIONS
- Sequence, selection, iteration
- if / elif / else; match / case
- for = count-controlled; while = condition-controlled
- range(start, stop, step): stop not included
SUBROUTINES AND VALIDATION
- Function returns a value; procedure doesn't
- Built-in, standard library, third-party, user-written
- Checks: presence, length, range, type, format, check digit
- Validation ≠ correct data
ROBUST CODE AND DEBUGGING
- Handles bad input and unexpected termination
- Specific error messages
- try / except
- Errors: syntax, runtime, logic
- Trace tables, breakpoints, print statements
ALGORITHMS AND TESTING
- Linear: any list, slow; binary: sorted list, halves each time
- Bubble: swap pairs; insertion: insert into sorted part; merge: split then merge (extra memory)
- Test data: valid, invalid, boundary, erroneous
- Unit → integration → system → acceptance; regression after changes
Flashcards
Every Quick recall definition on this page. Click the card to flip it; use the arrow keys to move.
Quiz me
Random questions on CA2 from the course question bank. Pick a topic or question type, answer in your head or on paper, then check. Your score is saved on this device.
Exam-style questions
Allow about 1 hour 45 minutes. Questions get harder as you go.
State the most suitable data type for each of these: (i) the number of items in a basket; (ii) a customer's postcode; (iii) whether an order has been dispatched.
- (i) Integer
- (ii) String
- (iii) Boolean
Explain why a constant should be used to store the VAT rate in a till program.
- The value is stored once and cannot be changed by accident during the program (1)…
- …so if the VAT rate changes, it only needs updating in one place / every calculation updates / fewer errors (1).
- Accept: makes code easier to read because the name explains the value (1).
A program uses age = input("Enter age: ") then if age >= 18:. Explain why this produces an error and how to fix it.
input()returns a string, which cannot be compared with an integer using >= / causes a TypeError (1).- Convert it:
age = int(input("Enter age: "))(1).
State the output of each line: (a) print(17 // 5) (b) print(17 % 5) (c) print(2 ** 3 + 1) (d) print(not (5 > 3 and 2 > 4)).
- (a) 3
- (b) 2
- (c) 9
- (d) True
Describe the difference between a local variable and a global variable.
- A local variable is declared inside a subroutine and can only be used within it / only exists while it runs (1).
- A global variable is declared outside subroutines and can be accessed anywhere in the program (1).
Give one similarity and one difference between a function and a procedure.
- Similarity: both are named blocks of code / both may take parameters / both can be called many times (1).
- Difference: a function must return a value; a procedure does not (1).
A gym app stores member IDs and their membership type, e.g. M104 is "Off-peak". Explain why a dictionary is more suitable than a list for this data.
- A dictionary stores key–value pairs, so the member ID can be the key and the membership type the value (1)…
- …so a type can be looked up directly by ID without searching through the list / faster lookup / IDs are kept unique (1).
Describe how a binary search would find the value 31 in this list: [4, 9, 15, 22, 31, 40, 58].
- Find the middle item: index 3 = 22 (1).
- 31 is greater than 22, so discard 22 and everything below it, leaving [31, 40, 58] (1).
- Middle item is 40; 31 is less, so discard 40 and above; check 31: found (1). Accept a correct alternative midpoint convention applied consistently.
Complete a trace table for this code and state the output.
total = 0
for i in range(2, 9, 3):
total = total + i
print(i, total)
- i takes the values 2, 5, 8 (1)
- total takes the values 2, 7, 15 (1)
- Output lines:
2 2,5 7,8 15(2: one mark for first two lines, one for the last)
Write a Python program that asks the user for a password and keeps asking until it is at least 8 characters long and contains at least one digit. It should print a specific error message each time and print "Password set" when a valid one is entered.
- Gets input from the user (1)
- Uses a condition-controlled loop that repeats until valid (1)
- Length check using
len() >= 8(1) - Checks for a digit, e.g. loop with
isdigit()orany()(1) - Specific error message for each failed rule (1)
- Prints "Password set" only when valid (1)
Example answer:
while True:
password = input("Enter a password: ")
has_digit = False
for ch in password:
if ch.isdigit():
has_digit = True
if len(password) < 8:
print("Error: password must be at least 8 characters.")
elif not has_digit:
print("Error: password must contain at least one digit.")
else:
print("Password set")
break
Enter a password: cat Error: password must be at least 8 characters. Enter a password: elephants Error: password must contain at least one digit. Enter a password: elephant5 Password set
The code below should read a file of temperatures (one per line) and print the average. It contains three errors. Identify each error and state the correction.
f = open("temps.txt", "w")
total = 0
count = 0
for line in f.readlines():
total = total + line.strip()
count = count + 1
f.close()
print("Average:", total // count)
- Line 1: file opened in write mode, which wipes it / cannot read (1). Correction:
open("temps.txt", "r")(1). - Line 5: adding a string to a number causes a TypeError (1). Correction:
float(line.strip())(1). - Line 8: integer division
//gives a whole number, losing accuracy (1). Correction:total / count(1). - Accept: no check for
count > 0before dividing, if justified, in place of one error.
A cinema's booking form accepts an age from 3 to 120. Give one item of each type of test data and the expected result: valid, boundary, invalid, erroneous.
- Valid: e.g. 35, accepted (1)
- Boundary: 3 or 120 accepted, or 2 or 121 rejected (1)
- Invalid: e.g. 150 or -5, rejected with error message (1)
- Erroneous: e.g. "ten" or blank, rejected with error message (1)
- Expected result must be given for the mark.
Explain two characteristics of robust code, using a self-service checkout as your example.
- Handles unexpected inputs (1), e.g. if a customer scans an item that is not in the database, it displays a message instead of freezing (1).
- Handles unexpected terminations (1), e.g. if the card reader disconnects mid-payment, the transaction is saved and can be resumed (1).
- Specific and meaningful error messages (1), e.g. 'Unexpected item in bagging area: please remove the last item' rather than 'Error 37' (1).
Explain the difference between integration testing and regression testing.
- Integration testing checks that separate components work together correctly (1), e.g. data passes correctly from the basket module to the payment module (1).
- Regression testing re-runs previous tests after a change (1) to make sure the change has not broken parts that worked before (1).
Write a function count_vowels(word) that returns the number of vowels (a, e, i, o, u) in a word, ignoring case. Show how it would be called and its result printed.
- Function defined with a parameter (1)
- Converts to lower case or checks both cases (1)
- Loops through characters and counts vowels (1)
- Returns the count, and calls/prints it (1)
Example answer:
def count_vowels(word):
count = 0
for letter in word.lower():
if letter in "aeiou":
count = count + 1
return count
print(count_vowels("Wakefield"))
4
A logistics company keeps a list of 250,000 parcel tracking numbers. New parcels are added throughout the day and customers search for their parcel thousands of times per hour. Evaluate the use of linear search and binary search for this system, including how the data should be sorted.
Indicative content. Credit other valid points.
- Linear search works on unsorted data, so new parcels can simply be appended with no sorting cost.
- Linear search is slow here: worst case 250,000 comparisons, average about 125,000, repeated thousands of times per hour, so slow responses and high server load.
- Binary search needs about 18 comparisons in the worst case for 250,000 items, so it is far faster for frequent searches.
- Binary search requires the list to be sorted; sorting takes time, but this cost is paid once rather than on every search.
- New parcels could be inserted into the correct position as they arrive (insertion sort is efficient for one new item into a sorted list), or added in batches and the list re-sorted with merge sort overnight.
- Merge sort is fast on large data but needs extra memory; a server is likely to have enough.
- Judgement: binary search on a sorted list is the better choice because searches far outnumber insertions; the extra complexity is justified.
A developer has written a working program for a café's loyalty scheme, but it uses single-letter variable names, no subroutines, no comments and the value 0.1 (the discount) typed in 14 places. Discuss the impact of these practices on the café and the developer's team.
◆ SUPPORT: SENTENCE STARTERS FOR 16 AND 17
- Linear search would be suitable because... However, with 250,000 items...
- Binary search needs the data to be sorted, which means...
- When new parcels are added, the company could...
- Overall, the best approach is ... because ...
- Single-letter names make it harder for another developer to...
- Typing 0.1 in 14 places means that if the discount changes...
Indicative content. Credit other valid points.
- Single-letter names make the code hard for other developers to understand, so changes take longer and cost the café more.
- No subroutines means repeated code; bugs must be fixed in many places, and parts cannot be tested on their own.
- No comments make it harder to maintain if the original developer leaves.
- The discount typed in 14 places (a 'magic number') means a change to the discount risks one copy being missed, so customers are charged inconsistently, a logic error that is hard to spot.
- Using a constant such as
LOYALTY_DISCOUNT = 0.1would allow a single change. - On the other hand, the program works now, so there is no immediate impact on customers; the cost appears later when it needs updating.
- Refactoring takes time and money in the short term, but reduces long-term maintenance cost.
▲ STRETCH
- For question 16: estimate how many comparisons each search would need in the worst case and use the figures in your answer.
- For question 10: extend your program so the password must also contain an upper-case letter, and write a test plan with five tests for it.