← All lessons · Cambridge 9618 A Level · Unit 20.1 · Paper 4 Practical
Work through this page, then build your code in Python and submit the Evidence Document on Teams.
Start here
Paper 4 is sat at a computer. You cannot learn it by reading about it, so this page teaches, then makes you type.
Why the evidence document matters
Examiners report marks lost in every single series for evidence, not for code: screenshots that show the output but not the inputs, code pasted as a picture instead of text, missing tests, Python indentation destroyed. Those are free marks and you are going to get into the habit of taking them now.
Retrieval
Answer these before you read anything else. Write them in your book, then reveal the answers and mark them.
Questions
Student has fields Name and Mark. Describe one difference between a record type and an array. [2]| Q | Accepted | Marks |
|---|---|---|
| 1 | A value passed into a procedure when it is called OR a variable in the procedure's definition that receives a value from the call. Reject: "an input" on its own. | 1 |
| 2 | MP1 — a record groups items of different data types, an array holds items of the same data type. MP2 — record elements are accessed by field name, array elements by index. 1 mark each, max 2. | 2 |
| 3 | A function returns a value to the point at which it was called; a procedure does not. Reject: "a function does a calculation". | 1 |
Q3 is the prerequisite check. Everything in this lesson depends on the difference between returning and outputting. If you got it wrong, read that row again before you go on — it is the single biggest cause of lost marks on Paper 4.
Where this is going
| By the end of this lesson I can… | Syllabus | Paper |
|---|---|---|
| Use the terms class, object, attribute, method, instantiation and encapsulation in the form a mark scheme accepts. | 20.1 | 3 & 4 |
| Declare a class in Python with private attributes whose data types are visible. | 20.1 | 4 |
| Write a constructor that takes exactly the parameters the class design specifies, and assigns the fixed default values the design states. | 20.1 | 4 |
| Instantiate an object and call one of its methods. | 20.1 | 4 |
Where this sits in the paper
A class appears in every Paper 4 since June 2021 — all 29 papers. It is not an optional topic — it is roughly a third of the paper, every year. Examiners now say that declaring classes has become a national strength, and that the marks are being lost further on, at the point where candidates have to create objects and use them. We start at the declaration this week and spend the next five on the part that is actually costing marks.
Vocabulary
These are written the way a mark scheme writes them. A looser paraphrase of the right idea scores nothing, so learn the wording, not the gist.
| Term | Definition that earns the mark |
|---|---|
| Class | A template (blueprint) that defines the attributes and methods of a type of object. |
| Object | An instance of a class. |
| Attribute (property) | A data item belonging to an object, declared with a data type. |
| Method | A subroutine belonging to a class that defines a behaviour of its objects. |
| Constructor | The method that runs automatically when an object is created, which initialises the object's attributes. |
| Instantiation | The process of creating an object from a class. |
| Encapsulation | Attributes are declared private so they can only be accessed through the class's own methods. |
What examiners reject
"A class is an object" — no mark. "An object is a copy of a class" — no mark; it is an instance. "Encapsulation means hiding the code" — no mark; it is about the data being private and reached only through methods.
PRIMM · Step 1
Read this program carefully. Do not run it yet. Write down exactly what you think it will output, character for character.
Python — Paper 4class Book: def __init__(self, Title, Author): self.__Title = Title # STRING self.__Author = Author # STRING self.__OnLoan = False # BOOLEAN def Display(self): print("Title : " + self.__Title) print("Author: " + self.__Author) print("On loan: " + str(self.__OnLoan)) MyBook = Book("Dune", "Frank Herbert") MyBook.Display()
Commit your prediction before you scroll
Two harder questions while you are here: how many times does the constructor run? And what is self actually referring to on line 3?
PRIMM · Step 2
Now run it. Press Run and compare the real output with your prediction. Python runs inside this page — nothing is sent anywhere, and you can break it as much as you like.
Press Run to execute.
Your code and your prediction are saved in this browser, so closing the tab does not lose them. If a program never finishes, press Stop.
Title : Dune Author: Frank Herbert On loan: False
MyBook = Book("Dune", "Frank Herbert"). That line is the instantiation.self refers to the object being worked on. Inside the constructor it is the new Book; inside Display() it is whichever object the method was called on.str(self.__OnLoan) is needed because you cannot join a BOOLEAN to a STRING with +. Getting that wrong is a runtime crash, and a crash in the exam means the screenshot marks go too.Try this — then read why it matters
Add this line to the bottom of the code above and run it again:
print(MyBook.__Title)
It crashes with an AttributeError. That crash is encapsulation working. The double underscore has made __Title private, so code outside the class cannot touch it directly. To read it, the class has to offer you a method — which is exactly what next week's lesson builds.
PRIMM · Step 3
class Book: — the name is a singular noun with a capital letter. Book, not Books, and not BookClass. The class describes one of the thing; you make as many objects from it as you like.
Python has no PRIVATE keyword and no type declarations, so you have to show the examiner what a VB.NET or Java candidate gets for free:
| You write | What it evidences |
|---|---|
| self.__Title | The two leading underscores make the attribute private. One underscore is not enough. |
| # STRING | A comment naming the data type. Examiners accept a comment as evidence of the data type — and without it you are relying on the marker's goodwill. |
Write both, every time, on every attribute. It costs you four seconds and it is a mark.
In Python the constructor is always called __init__ — two underscores, init, two underscores. Its first parameter is always self.
The misspelling that costs the whole question
Examiners name __int__ as a recurring error. Python does not complain — it simply never runs your constructor, so every attribute is missing and every method that follows crashes. If your object "has no attribute", check the spelling of __init__ first.
Display() is a method: a subroutine that belongs to the class. Its first parameter is self, and it is called on an object with a dot — MyBook.Display().
Which line correctly declares a private attribute and evidences its data type, in a Python class?
PRIMM · Step 4
Go back to the runner above and change the Book class so that it matches this class design. This is exactly the form Cambridge gives you a class in, so read it the way you will read it in the exam.
Class design — Book (version 2)
| Attribute | Data type | Set when the object is created |
|---|---|---|
| Title | STRING | From a parameter |
| Author | STRING | From a parameter |
| Year | INTEGER | From a parameter |
| OnLoan | BOOLEAN | Always False |
| TimesBorrowed | INTEGER | Always 0 |
Then: update Display() to output all five attributes with labels, and create a second object of your own choosing so that two different books are displayed.
Read the right-hand column very carefully
OnLoan and TimesBorrowed say "always". That means the constructor assigns them itself — they are not parameters. A constructor that takes five parameters when the design specifies three has not followed the design, and examiners mark it as wrong even when the code runs perfectly. Count the parameters against the design before you write the header.
class Book: def __init__(self, Title, Author, Year): self.__Title = Title # STRING self.__Author = Author # STRING self.__Year = Year # INTEGER self.__OnLoan = False # BOOLEAN self.__TimesBorrowed = 0 # INTEGER def Display(self): print("Title : " + self.__Title) print("Author : " + self.__Author) print("Year : " + str(self.__Year)) print("On loan : " + str(self.__OnLoan)) print("Times borrowed : " + str(self.__TimesBorrowed)) FirstBook = Book("Dune", "Frank Herbert", 1965) SecondBook = Book("Emma", "Jane Austen", 1815) FirstBook.Display() SecondBook.Display()
Three parameters, five attributes. Two objects from one class — that is the whole point of a class.
A class Member has a constructor. Which line creates an object of that class correctly?
Member(...) in your program crashes.new is Java and C#, not Python. Every language on this paper has its own instantiation syntax and you must use the one for the language you are sitting.The marks
Almost everything that goes wrong on an OOP question goes wrong before any code is written, because the candidate implemented the class they remembered rather than the class they were given. Cambridge does this deliberately: the designs change slightly every year specifically to test whether you read them.
Predict, then reveal. A candidate is given the Book design above and writes this. Decide what an examiner would say before you open the answer.
class Book: def __init__(self, Title, Author, Year, OnLoan, TimesBorrowed): self.Title = Title self.Author = Author self.Year = Year self.OnLoan = OnLoan self.TimesBorrowed = TimesBorrowed
The code runs. It still loses most of the available marks.
OnLoan and TimesBorrowed are always set to fixed values. Taking them as parameters means the class does not meet the specification, and it lets the caller create a book that has already been borrowed 400 times.The habit to build: before you write the class header, put a finger on the design table and count how many attributes come from a parameter. That number is how many parameters your constructor takes, after self. Everything else is assigned inside.
A class design states: "Attribute Balance, INTEGER, set to 100 when the object is created." Which constructor follows the design?
Balance as a parameter. The design says it is set to a fixed value on creation, so it must not be a parameter."100" in quotes is a string, not an integer. Examiners have sharpened up on exactly this since 2025: -1 and '-1' are not the same thing, and neither are these.Assessed work
Everyone starts at Independent. Drop to Scaffolded if you stall. Go to Exam Conditions when you have finished — and by the mock, that is where everyone needs to be.
The scenario
A sports club stores a record of each of its members. A programmer has produced the following class design.
| Attribute | Data type | Set when the object is created |
|---|---|---|
| MemberID | STRING | From a parameter |
| Name | STRING | From a parameter |
| Age | INTEGER | From a parameter |
| Credits | INTEGER | Always 0 |
| Active | BOOLEAN | Always True |
| Method | Description |
|---|---|
| Constructor | Initialises all five attributes as described above. |
| DisplayDetails() | Outputs all five attributes, each on its own line, with a label. |
(a) Write program code to declare the class Member, including its constructor and the method DisplayDetails(). [8]
(b) Write program code to create one object of the class Member using this test data, and then display its details: "M4021", "Sara Ahmed", 16. [2]
(c) Take a screenshot showing the program running with that test data. The screenshot must show the test data and the complete output — here, the line of your program that creates the object, alongside the console. [2]
Same three questions, same marks. Copy this skeleton into your editor and complete every # .... Do not change anything else.
class Member: def __init__(self, ______, ______, ______): self.__MemberID = ______ # STRING # ... Name # ... Age # ... Credits (fixed value — not a parameter) # ... Active (fixed value — not a parameter) def DisplayDetails(self): print("Member ID : " + self.__MemberID) # ... one print for each remaining attribute # ... remember str() around the ones that are not strings # ... create the object here, with a sensible variable name # ... call DisplayDetails on it
Close this page. Set a timer for 20 minutes. Work from the class design table alone, with no notes and no skeleton. Then, as an extension, add this second class from its own design and create one object of it as well:
Class Coach | Data type | Set when the object is created |
|---|---|---|
| CoachID | STRING | From a parameter |
| Name | STRING | From a parameter |
| Sport | STRING | From a parameter |
| Qualified | BOOLEAN | Always False |
Test data for the Coach object: "C107", "Tom Reilly", "Athletics". Both objects' details must appear in one screenshot.
Before you screenshot
Self-marking
Mark your own work honestly against this, then record your total in the Evidence Document. Marking your own work against the real wording is how you learn what "one mark per point, max 8" actually means.
| Point | Description | Marks |
|---|---|---|
| (a) MP1 | Class Member declared, correctly named. | 1 |
| (a) MP2 | Constructor correctly named for the language (__init__) taking exactly three parameters after self. | 1 |
| (a) MP3 | All five attributes declared private (double underscore). | 1 |
| (a) MP4 | Data type of every attribute evidenced (comment is accepted). | 1 |
| (a) MP5 | MemberID, Name and Age assigned from the parameters, the correct way round. | 1 |
| (a) MP6 | Credits assigned the fixed value 0 inside the constructor, as an integer not a string. | 1 |
| (a) MP7 | Active assigned the fixed value True inside the constructor, as a Boolean not a string. | 1 |
| (a) MP8 | DisplayDetails() declared as a method of the class and outputs all five attributes, each labelled. | 1 |
| (b) MP9 | An object created from the class, stored in a variable not named after the class, using the given test data in the correct order. | 1 |
| (b) MP10 | DisplayDetails() called on that object using dot notation. | 1 |
| (c) MP11 | Screenshot shows the program running with the given test data, including the test data itself. | 1 |
| (c) MP12 | Screenshot shows the complete, correct output for all five attributes. | 1 |
Accepted alternatives
DisplayDetails() may build one long string and print it once, or print line by line. Both are accepted.Not credited, however good the code looks
Credits or Active taken as parameters — MP2, MP6 and MP7 lost. Three marks for not reading the table.DisplayDetails() that returns a string instead of outputting it — MP8 lost. The design says "outputs".Hand-in
One file: the Evidence Document for this lesson, saved as .docx and named Surname Initial — OOP Home Lesson 1.docx.
Checklist before you upload
.py file.Your next taught lesson opens by checking these submissions, so an empty one is visible immediately. If you got stuck, submit what you have with a note saying where — that is far more useful to me than nothing.
Home Lesson 2 — Get and set methods. Getting a value out of a private attribute and putting a new one in. It sounds trivial. A get method written with a parameter is the single most repeated OOP error in the examiner reports — all ten recent series — so it is worth a lesson of its own.