Filling the last Year 11 gaps — then putting everything to the test
Quick recall — no notes. The four pillars of OOP. Name as many as you can.
Bundling + access control. You've got this.
Child classes. You've got this.
Same method, different behaviour. You've got this.
Today's gap. Let's fix it.
A Set is an unordered collection of unique values. That's the whole idea:
In Python, you make a Set with curly braces — but unlike a Dict, there are no colons:
# Creating a Set
tags = {"python", "web", "security", "python"} # duplicate "python" is silently dropped
print(tags) # {'python', 'web', 'security'} — order may vary
# Empty set — must use set(), not {} (that makes an empty dict)
empty = set()
# Adding and removing
tags.add("automation")
tags.discard("web") # safe remove — no error if missing
tags.remove("web") # also removes, but raises KeyError if missing
# Membership check — O(1), much faster than a List for large data
print("security" in tags) # True
Why does order not matter? Sets are backed by a hash table — the same structure as Dict keys. That's why membership checking is O(1) instead of O(n) like a List. The trade-off: you can't have duplicates, and order is not defined.
Blake, in Lesson 2 you chose a Dict when a Set was the better fit. Here's how to think about it:
Use when: you care about order, or you need duplicates, or you need index access.
Use when: you need to map one thing to another (name → score, username → role).
Use when: you need deduplication, fast membership checks, or math on groups.
len(visitors_set) gives you the unique count instantly.
This is where Sets shine. You can do set math with simple operators:
admins = {"blake", "luis", "admin"}
students = {"blake", "kai", "mia", "jordan"}
# Union — everyone in either group
print(admins | students) # {'blake', 'luis', 'admin', 'kai', 'mia', 'jordan'}
# Intersection — only people in BOTH groups
print(admins & students) # {'blake'}
# Difference — in admins but NOT in students
print(admins - students) # {'luis', 'admin'}
# Symmetric difference — in one or the other, but not both
print(admins ^ students) # {'luis', 'admin', 'kai', 'mia', 'jordan'}
Your Volcanic Pantheon site has protected pages. A user can view a page only if their permission set contains the required permissions. How do you check?
user_permissions = {"view_shrine", "leave_comment", "view_gallery"}
required_for_page = {"view_shrine", "view_gallery"}
# Does the user have ALL required permissions?
# Intersection should equal the required set
can_access = required_for_page.issubset(user_permissions)
print(can_access) # True
# Or equivalently:
can_access = required_for_page <= user_permissions # <= means "is subset of"
For each scenario, say whether you'd use a List, Dict, or Set, and why in one sentence.
Abstraction means hiding how something works and exposing only what it does.
You already use abstraction every day without knowing it:
list.sort() without knowing it uses Timsort internallymodel.fit(X, y) without knowing it runs gradient descentopen() without knowing how the OS handles file descriptorsAll of those are abstracted APIs — the complexity is hidden, a clean interface is exposed.
Encapsulation: bundling data and methods together and controlling who can access them (public/private). It's about structure.
Abstraction: hiding the implementation and exposing a clean interface. It's about design.
Encapsulation is how you implement abstraction — but they answer different questions.
Remember the MLPipeline class from Lesson 19? That was abstraction in action. The caller didn't need to know about StandardScaler, or that fit() calls two things internally. They just called pipeline.train(X, y).
# Lesson 19 — the caller uses this simple interface:
pipeline = MLPipeline(LinearRegression())
pipeline.train(X_train, y_train)
result = pipeline.evaluate(X_test, y_test)
# The IMPLEMENTATION is hidden — they don't see this complexity:
def train(self, X, y):
X_scaled = self.scaler.fit_transform(X) # hidden
self.model.fit(X_scaled, y) # hidden
self._is_trained = True # hidden
The test: if a caller can use your class without reading your implementation, you've achieved abstraction. A good abstraction means the internal logic could be completely rewritten and the caller wouldn't need to change a line.
Python's abc module lets you define an abstract base class (ABC). An ABC says: "any class that inherits from me must implement these methods." It's like a contract.
This is how Python enforces abstraction at the design level.
from abc import ABC, abstractmethod
class AuthProvider(ABC):
"""Anyone who implements authentication must provide these two methods."""
@abstractmethod
def login(self, username: str, password: str) -> bool:
pass # No implementation here — just the contract
@abstractmethod
def logout(self, username: str) -> None:
pass
# A concrete class that fulfils the contract:
class JWTAuth(AuthProvider):
def login(self, username, password):
# Real JWT verification logic here
return verify_jwt(username, password)
def logout(self, username):
invalidate_token(username)
# A different concrete class — same interface, different internals:
class SessionAuth(AuthProvider):
def login(self, username, password):
# Session-based login logic
return check_session_db(username, password)
def logout(self, username):
clear_session(username)
# If you forget to implement login() or logout(), Python raises a TypeError at runtime:
class BrokenAuth(AuthProvider):
pass # Missing both methods
auth = BrokenAuth() # TypeError: Can't instantiate abstract class BrokenAuth
AuthProvider ABC so you could swap implementations without breaking the rest of the site.
Here's a quick example that uses all four in one class hierarchy:
from abc import ABC, abstractmethod
# ABSTRACTION: defines the interface — "what" without "how"
class Shape(ABC):
@abstractmethod
def area(self) -> float:
pass
@abstractmethod
def perimeter(self) -> float:
pass
def describe(self): # concrete method — shared by all
return f"Area: {self.area():.2f}, Perimeter: {self.perimeter():.2f}"
# INHERITANCE: Circle and Rectangle inherit from Shape
class Circle(Shape):
def __init__(self, radius: float):
self._radius = radius # ENCAPSULATION: private attribute
def area(self):
return 3.14159 * self._radius ** 2
def perimeter(self):
return 2 * 3.14159 * self._radius
class Rectangle(Shape):
def __init__(self, w: float, h: float):
self._w = w
self._h = h
def area(self):
return self._w * self._h
def perimeter(self):
return 2 * (self._w + self._h)
# POLYMORPHISM: same method name, different behaviour
shapes = [Circle(5), Rectangle(4, 6)]
for shape in shapes:
print(shape.describe()) # calls each class's own area() + perimeter()
Match each numbered line to its OOP pillar (Encapsulation, Inheritance, Polymorphism, Abstraction):
@abstractmethod on area() in the base classself._radius = radius (note the underscore)class Circle(Shape):shape.describe() calling different area() for each type in the loopdescribe() call, different area() results.Every HSC question uses a specific verb. Getting the verb wrong loses marks even if your content is correct.
| Verb | What it means | Typical marks | Example |
|---|---|---|---|
| describe | Give features/characteristics. What it is, what it does. | 1–3 | "Describe what hashing is." |
| explain | Make something clear. Show cause-and-effect or how/why. | 2–4 | "Explain how a SQL injection attack works." |
| assess | Weigh up evidence. Consider both sides. Make a judgement. | 4–6 | "Assess the impact of ML on employment." |
| design/develop | Produce a plan, diagram, or code. Show the artefact. | 3–6 | "Design a class diagram for this system." |
For "assess" questions (4+ marks), use PEEL:
Point — state your claim.
Evidence — give a specific example or case study.
Explanation — explain why the evidence supports your point.
Link — tie it back to the question.
Explain how parameterised queries prevent SQL injection attacks. In your answer, refer to how user input is handled differently to a regular query.
Approx. 5–6 lines
In a regular (vulnerable) SQL query, user input is concatenated directly into the query string. This means an attacker can enter input like ' OR '1'='1, which is interpreted as part of the SQL command, giving them unauthorised access.
Parameterised queries separate the SQL structure from the data. The query is compiled first with placeholders (e.g., ? or %s), and user input is passed in as a separate parameter. The database treats the input purely as data — it is never interpreted as SQL syntax, so injected commands cannot execute.
Mark allocation: 1 mark — identifies the vulnerability in direct concatenation. 1 mark — explains how placeholders separate structure from data. 1 mark — explains why input cannot execute as SQL.
Describe the roles of the client and the server in a client-server web application. In your answer, include examples of tasks performed by each.
Approx. 6–8 lines
Client: The client is the user's device (typically a browser). It is responsible for sending requests to the server (e.g., clicking a link sends an HTTP GET request), rendering the HTML/CSS/JavaScript response, and handling user interaction. The client runs code that executes on the user's machine — JavaScript, for example, can update the page without reloading it.
Server: The server listens for requests, processes business logic (e.g., querying a database, verifying authentication), and returns a response — typically HTML, JSON, or other data. The server runs code that the user cannot see or modify, making it the appropriate place for sensitive operations such as password verification, database access, and API key handling.
Mark allocation: 1 mark each — client role, client example, server role, server example.
Assess the impact of machine learning and automation on employment. In your answer, consider both negative and positive effects on individuals and society.
Approx. 10–12 lines
Negative — job displacement: Automation eliminates repetitive roles. Amazon's warehouse robotics reduced picker headcount significantly, and self-checkout kiosks displaced retail cashiers. These workers, often low-income, may lack the skills to transition quickly, increasing short-term unemployment and economic inequality.
Positive — job creation and safety: Automation also creates roles — ML engineers, data annotators, robot maintenance technicians. In high-risk industries such as mining and manufacturing, cobots handle dangerous tasks, reducing workplace injuries. Workers with disabilities benefit from AI-powered assistive technology that enables participation in the workforce.
Overall judgement: The net impact depends heavily on reskilling investment. Without government and industry support for displaced workers, automation risks widening inequality. With it, automation has the potential to improve safety, productivity, and accessibility across society.
Mark allocation: 1–2 marks — describes negative impact with example. 1–2 marks — describes positive impact with example. 1 mark — makes a supported overall judgement.
Describe the difference between functional and non-functional requirements. Give ONE example of each for a student mark-tracking web application.
Approx. 4–6 lines
Functional requirements define what the system shall do — specific, observable behaviours. Example: "The system shall allow a student to enter a mark for each assessment task and store it persistently."
Non-functional requirements define the quality of how the system does it — performance, security, usability. Example: "The system shall load the dashboard in under two seconds on a standard school network connection."
Mark allocation: 1 mark — defines functional requirement. 1 mark — defines non-functional requirement. 1 mark — gives a correct and distinct example of each.
Explain the concept of abstraction in object-oriented programming. In your answer, describe how abstract base classes support abstraction and give ONE benefit of using them.
Approx. 6–8 lines
Abstraction is the OOP principle of hiding the implementation details of a class and exposing only the interface that callers need. The caller knows what a method does, not how it does it. This reduces complexity and makes systems easier to use and maintain.
Abstract base classes (ABCs) support abstraction by defining methods that subclasses must implement. Using Python's abc module, a method decorated with @abstractmethod cannot be inherited without providing an implementation — Python raises a TypeError if a concrete subclass skips it. This enforces a consistent interface across all subclasses.
Benefit: ABCs enable substitutability — any subclass can be swapped for another without changing the calling code, as long as both implement the same interface. This supports modular, maintainable software.
Mark allocation: 1 mark — defines abstraction. 1 mark — explains how ABCs work. 1 mark — gives a concrete benefit. 1 mark — quality/depth of explanation.
Encapsulation, Inheritance, Polymorphism, Abstraction. The Year 11 OOP gap is closed.
Sets are now in your toolkit alongside List and Dict. You know when to use each.
Pick one of the following:
AuthProvider ABC with login() and logout() methods. Write one concrete subclass that represents your current client-side SHA-256 approach. Write a second concrete subclass that represents a hypothetical server-side JWT approach. You don't need working code — just the class structure and method stubs.