Week 15 Tutorial: Revision Week
Problem 1 (Easy): Pet Profile
A local pet shelter needs a simple digital record for each animal. Your task is to build a Pet class from scratch.
Requirements:
- Define a class named
Pet. Its constructor should accept three pieces of information: the pet’s name, its species, and its age in years. Store all three as instance variables. - Add a method that returns a description sentence in this exact format: “Name is a Species aged Age” (replace the capitalised words with the actual values).
- Add another method that simulates a birthday. It should increase the pet’s age by one year and print a celebratory message showing the updated age.
- Create a dog named Buddy who is 3 years old. Print the dog’s description.
- Create a cat named Whiskers who is 2 years old. Celebrate Whiskers’ birthday by calling the birthday method.
Expected Output
Buddy is a Dog aged 3
Whiskers is now 3 years old!
Problem 2 (Easy+): Team Roster
A local sports league wants to track teams and how many players each team has signed up.
Requirements:
- Define a class named
Team. Its constructor should accept a team name and store it as an instance variable. - The class should also keep a shared count of how many teams have been created in total. This count is not specific to any single team — it belongs to the class itself.
- Add a method called
register_playerthat increases a per-team player count by one. - Add a class method called
total_teamsthat returns the current shared team count. - Create a team called “Lions” and register two players for it.
- Create a team called “Tigers” and register one player for it.
- Print how many players the Lions team has.
- Print the total number of teams that exist.
Expected Output
Lions players: 2
Total teams: 2
Problem 3 (Medium): Dimmer Switch
A smart-home system controls a light dimmer. The brightness must stay within a safe range.
Requirements:
- Define a class named
DimmerSwitch. Its constructor should accept an initial brightness level between 0 and 100 inclusive. Store it in a protected variable. - Expose the brightness through a property called
brightness. The getter should return the current level. - The setter for
brightnessshould reject any value outside the 0–100 range. If an invalid value is assigned, print a warning message and leave the brightness unchanged. - Add a method called
increasethat accepts a number of points and raises the brightness by that amount, still respecting the 0–100 cap through the setter. - Create a dimmer starting at 50 brightness. Print the current brightness.
- Increase it by 30 points and print again.
- Try to set the brightness to 150 and observe the warning.
- Print the final brightness to confirm it did not change after the invalid attempt.
Expected Output
Current: 50
Current: 80
Warning: brightness must be between 0 and 100
Current: 80
Problem 4 (Medium+): Student Grading
A university wants to grade different types of students. Regular students are graded on exams only. Honours students get a bonus mark added to their average.
Requirements:
- Define a class named
Student. Its constructor acceptsnameand a list of scores. It calculates and stores the average of those scores. - Add a method called
reportthat returns a string in this format: “Name: average=X” where X is the average score. - Define a subclass named
HonoursStudent. It inherits fromStudent. Its constructor accepts the same arguments plus abonusvalue. - Override the
reportmethod so that it returns: “Name: average=X (honours)”, where X includes the bonus added to the base average. Delegate to the parent class to help compute the base average. - Create a regular student named “Ali” with scores 70, 80, and 90. Print the report.
- Create an honours student named “Bob” with scores 60 and 70 and a bonus of 10. Print the report.
Expected Output
Ali: average=80.0
Bob: average=75.0 (honours)
Problem 5 (Advanced): Airport Gate Scheduler
A small airport needs to schedule flights at boarding gates. Flights must be spaced at least 30 minutes apart to avoid passenger conflicts.
Requirements:
- Define a dataclass named
Flight. It has three fields:code(str, e.g."AZ102"),airline(str), andscheduled_time(str, in"HH:MM"24-hour format, e.g."14:30"). - Enable automatic ordering on
Flightso thatscheduled_timeis the primary field for sorting.sorted(flights)should put flights in chronological order. - Add a
__post_init__method that validates:scheduled_timemust match the"HH:MM"format with hours from00to23and minutes from00to59.- If the format is invalid, raise
ValueError("Invalid time format, expected HH:MM").
- Add a method
time_in_minutesthat converts thescheduled_timeinto total minutes since midnight (e.g.,"09:15"→ 555). - Define a class named
Gate. Its constructor accepts a gate name (e.g."A1"). Gatestores a list of assignedFlightobjects, initially empty.- Add a method
conflicting_flight(self, flight)that checks whether the new flight is less than 30 minutes away from any already-assigned flight at this gate.- If there is a conflict, return the existing
Flightobject that caused it. - If there is no conflict, return
None.
- If there is a conflict, return the existing
- Add a method
assign(self, flight)that usesconflicting_flight.- If there is no conflict, append the flight and return
True. - If there is a conflict, print a message in this format:
"Conflict with CODE at HH:MM", then returnFalse.
- If there is no conflict, append the flight and return
- Add a method
schedulethat prints all assigned flights sorted by time, one per line, in this format:"HH:MM — CODE (Airline)". - Create a gate named
"B3". Attempt to assign these flights in this exact order:"AZ101"by"Azur Air"at"08:00""TR205"by"Turkish Airlines"at"08:15"(should conflict with AZ101)"TR205"by"Turkish Airlines"at"08:45"(should succeed)"UZ777"by"Uzbekistan Airways"at"09:20"(should succeed)"FR304"by"Ryanair"at"09:00"(should conflict with the 08:45 flight)
- After all assignments, print the final gate schedule.
Expected Output
Conflict with AZ101 at 08:00
Conflict with TR205 at 08:45
08:00 — AZ101 (Azur Air)
08:45 — TR205 (Turkish Airlines)
09:20 — UZ777 (Uzbekistan Airways)
Problem 6 (Advanced): Retry Decorator
A web-scraping library sometimes fails due to temporary network timeouts. You need a reusable retry mechanism that can be configured with a maximum number of attempts.
Requirements:
- Write a function
fetch_data(url)that simulates a temporary network problem.- It should count how many times it has been called.
- The first 2 calls should raise
ConnectionError("timeout"). - The 3rd call should return
"data from {url}". - Hint: You can store the call count in a variable outside the function, or treat the function itself as an object and store the count as a function attribute.
- Write a decorator named
retrythat accepts a parametermax_attemptswith a default value of3. - The decorator should call the decorated function. If the function raises
ConnectionError, retry it. - The decorator should try at most
max_attemptstotal times, including the first call. - After each failed attempt except the last, print:
Attempt N failed, retrying... - If the function succeeds on any attempt, return its result immediately and stop retrying.
- If all attempts are exhausted, re-raise the last
ConnectionErrorwith its original traceback intact. - The wrapper must accept any arguments, so use
*argsand**kwargs. - Apply
@retry(3)tofetch_data(url). - Call the decorated function with
fetch_data("example.com")and print the result. - Demonstrate a second function that always fails.
- Decorate it with
@retry(3). - Call it inside a
try/except ConnectionErrorblock. - Print the final exception message in this format:
Caught: timeout
- Decorate it with
Expected Output
Attempt 1 failed, retrying...
Attempt 2 failed, retrying...
data from example.com
Attempt 1 failed, retrying...
Attempt 2 failed, retrying...
Caught: timeout
Problem 7 (Advanced): Enemy Spawner
A game engine spawns different enemy types based on string configuration data. The exact enemy class is chosen at runtime based on an enum value.
Requirements:
- Define an Enum
EnemyTypewith members:GOBLIN,ORC,DRAGON. - Define an abstract base class
Enemy. Its constructor storesname(str) andhealth(int). It has an abstract methodspecial_attack(self) -> str. - Create three concrete subclasses:
Goblin—special_attackreturns"Name backstabs for 5 damage!"Orc—special_attackreturns"Name smashes for 15 damage!"Dragon—special_attackreturns"Name breathes fire for 50 damage!"
- Define a class
EnemyFactorywith a class methodcreate_from_config(config: str)that reads a string like"GOBLIN:Goblin,30;ORC:Orc,80;DRAGON:Dragon,200". Each segment before the semicolon has the formatTYPE:Name,Health. The factory must split the string, look up the correct enemy type, and return a list of enemy instances in the order they appear. If any type in the config string is not a validEnemyTypemember, raiseValueError("Unknown enemy type").- Hint: You can access an enum member by its name as a string, for example
EnemyType["GOBLIN"].
- Hint: You can access an enum member by its name as a string, for example
- Define a
Gameclass. It stores a list of active enemies (initially empty).spawn_enemies(self, enemies)accepts a list of enemy objects and extends the active list.battle_report(self)iterates over all active enemies and prints one line per enemy in this exact format:Name | HP: Health | AttackOutput
- Demonstrate:
- Parse the config string
"GOBLIN:Goblin,30;ORC:Orc,80;DRAGON:Dragon,200"using the factory. - Create a game and spawn the parsed enemies.
- Print the battle report.
- Attempt to parse an invalid config
"GOBLIN:Goblin,30;TROLL:Troll,50"inside a try/except block. Catch theValueErrorand print its message.
- Parse the config string
Expected Output
Goblin | HP: 30 | Goblin backstabs for 5 damage!
Orc | HP: 80 | Orc smashes for 15 damage!
Dragon | HP: 200 | Dragon breathes fire for 50 damage!
Unknown enemy type
Problem 8 (New, Medium): Vehicle Fleet with Factory Constructor
Requirements:
- Create a
Vehiclebase class storingbrand,model, andyear. Vehicle.info()returns"brand model (year)".- Create an
ElectricVehiclesubclass. It also storesbattery_kwh(float). - Override
info()to return"brand model (year), battery: X kWh"whereXis the battery size. - Add
ElectricVehicle.from_dict(data)as a classmethod. It receives a dict like{"brand": "Tesla", "model": "Model 3", "year": 2023, "battery_kwh": 75}and returns an instance. - If any required key is missing, raise
KeyErrorwith a message indicating which key is missing. - Add
ElectricVehicle.from_string(data)as a classmethod. It reads a comma-separated string like"Tesla,Model 3,2023,75"and returns an instance. - If the string does not split into exactly 4 parts, raise
ValueError("Invalid vehicle data"). - Demonstrate: create one electric vehicle using
from_string, create another usingfrom_dict, and printinfo()for both.
Problem 9 (New, Medium): Weather Alert System
Requirements:
- Create a
WeatherReadingdataclass with three fields:station_id: str,temperature: float,humidity: int. - Define an abstract base class
WeatherWatcherwith one abstract methodupdate(self, event)whereeventis aWeatherReading. - Create
TemperatureAlert(WeatherWatcher)that stores athreshold(float). It prints an alert only whenevent.temperature > threshold, in this format:ALERT: Station-A temperature 32.5C exceeds 30.0C. - Create
StormLogger(WeatherWatcher)that stores every event in a list and prints a log line, for example:LOG: Station-A — temp=35.5C, humidity=80%. - Create
WeatherStation. It needs three methods:subscribe,unsubscribe, andbroadcast. broadcast(event)sends the reading to all current watchers in subscription order.- Demonstrate: add two watchers, broadcast one reading, remove one watcher, broadcast another reading.