Simple Programs to demonstrate Classes
Next Topic(s):
Created:
4th of November 2025
05:42:01 PM
Modified:
4th of November 2025
05:44:35 PM
Exercises: Classes in Python (Simple Practice)
These ten short exercises reinforce class basics using familiar daily objects before moving into civil engineering elements—walls, doors, and windows. Each task includes a minimal starter you can run and extend. Use British English in comments and clear, descriptive names.
Tip: In Java you’d speak of fields and methods. In Python, the data on an object are attributes and the behaviours are still methods. The constructor is __init__; self plays the role of Java’s this.
Exercise 01 — Student (Attributes & Methods)
Filename: ex01_student.py
- Create a class
Studentwith attributesname(str) andenrolled(bool, defaultTrue). - Add methods
suspend()andreinstate()that flipenrolled. - Implement
__str__to printStudent(name="Ada", enrolled=True).
class Student:
def __init__(self, name: str, enrolled: bool = True):
# TODO: store arguments on self
pass
def suspend(self):
# TODO: set enrolled to False
pass
def reinstate(self):
# TODO: set enrolled to True
pass
def __str__(self) -> str:
# TODO: return a friendly string
return ""
if __name__ == "__main__":
s = Student("Ada")
print(s)
s.suspend()
print(s)
s.reinstate()
print(s)
Exercise 02 — Cup (State Changes)
Filename: ex02_cup.py
- Create
Cupwithcolour,capacity_ml, andcontents_ml(start at 0). - Add
fill(amount_ml)(do not exceed capacity) andempty(). - Print the contents after each action.
class Cup:
def __init__(self, colour: str, capacity_ml: int):
# TODO
pass
def fill(self, amount_ml: int):
# TODO: increase contents_ml without exceeding capacity
pass
def empty(self):
# TODO: set contents_ml to 0
pass
if __name__ == "__main__":
mug = Cup("blue", 300)
mug.fill(120)
print(mug.contents_ml) # expected: 120
mug.fill(300)
print(mug.contents_ml) # expected: 300
mug.empty()
print(mug.contents_ml) # expected: 0
Exercise 03 — Opening (Geometry & Units)
Filename: ex03_opening.py
- Create
Openingwithwidth_mmandheight_mm. - Add method
area_m2()that returns area in square metres. - Round to two decimals when printing.
class Opening:
def __init__(self, width_mm: int, height_mm: int):
# TODO
pass
def area_m2(self) -> float:
# TODO: convert mm to m then compute area
return 0.0
if __name__ == "__main__":
o = Opening(900, 2100) # typical door leaf
print(f"Opening area (m²): {o.area_m2():.2f}")
Exercise 04 — Door (Inheritance)
Filename: ex04_door.py
- Subclass
DoorfromOpening. - Add attributes
material(str) andfire_rating_min(int, default 0). - Add method
is_fire_door()returningTrueif rating ≥ 30.
from ex03_opening import Opening # or paste Opening here
class Door(Opening):
def __init__(self, width_mm: int, height_mm: int, material: str, fire_rating_min: int = 0):
# TODO: call super().__init__(...) and store extras
pass
def is_fire_door(self) -> bool:
# TODO: return True if rating meets threshold
return False
if __name__ == "__main__":
d = Door(900, 2100, "timber", 60)
print(d.area_m2())
print(d.is_fire_door()) # expected: True
Exercise 05 — Window (Property)
Filename: ex05_window.py
- Subclass
WindowfromOpening. - Add
frame_thickness_mm(int, default 60) andglass_type(str). - Expose a computed
@propertynamedclear_opening_m2that subtracts the frame on all sides.
from ex03_opening import Opening # or paste Opening here
class Window(Opening):
def __init__(self, width_mm: int, height_mm: int, glass_type: str, frame_thickness_mm: int = 60):
# TODO
pass
@property
def clear_opening_m2(self) -> float:
# TODO: compute daylight opening area
return 0.0
if __name__ == "__main__":
w = Window(1200, 1200, "double-glazed", 70)
print(f"Clear opening (m²): {w.clear_opening_m2:.3f}")
Exercise 06 — Wall (Composition)
Filename: ex06_wall.py
- Create
Wallwithwidth_mm,height_mm, and a listopenings. - Add
add_opening(opening),gross_area_m2(),openings_area_m2(), andnet_area_m2(). - Test with at least one
Doorand oneWindow.
from ex04_door import Door
from ex05_window import Window
class Wall:
def __init__(self, width_mm: int, height_mm: int):
# TODO
pass
def add_opening(self, opening):
# TODO: append to the list
pass
def gross_area_m2(self) -> float:
# TODO
return 0.0
def openings_area_m2(self) -> float:
# TODO
return 0.0
def net_area_m2(self) -> float:
# TODO
return 0.0
if __name__ == "__main__":
wall = Wall(4000, 3000)
wall.add_opening(Door(900, 2100, "timber", 60))
wall.add_opening(Window(1200, 1200, "double-glazed"))
print(f"Net area (m²): {wall.net_area_m2():.2f}")
Exercise 07 — Wall Schedule (String Representation)
Filename: ex07_wall_schedule.py
- Extend
Wallto implement__str__, returning a multi-line summary:- Gross area, openings area, net area
- Count of doors and windows
from ex06_wall import Wall
from ex04_door import Door
from ex05_window import Window
class SchedulableWall(Wall):
def __str__(self) -> str:
# TODO: build and return a readable summary
return ""
if __name__ == "__main__":
w = SchedulableWall(5000, 3000)
w.add_opening(Door(900, 2100, "steel", 120))
w.add_opening(Window(1500, 1200, "laminated"))
print(w) # expected: multi-line report
Exercise 08 — Building (Collection of Walls)
Filename: ex08_building.py
- Create
Buildingwith a list ofwalls. - Add methods
add_wall(wall),total_net_area_m2(), andlargest_wall()(return the wall with the greatest net area).
from ex06_wall import Wall
from ex04_door import Door
from ex05_window import Window
class Building:
def __init__(self, name: str):
# TODO
pass
def add_wall(self, wall: Wall):
# TODO
pass
def total_net_area_m2(self) -> float:
# TODO
return 0.0
def largest_wall(self) -> Wall | None:
# TODO: return the wall with max net area (or None)
return None
if __name__ == "__main__":
b = Building("Block A")
w1 = Wall(4000, 3000); w1.add_opening(Window(1200, 1200, "double-glazed"))
w2 = Wall(6000, 3000); w2.add_opening(Door(1000, 2100, "timber"))
b.add_wall(w1); b.add_wall(w2)
print(f"Total net area (m²): {b.total_net_area_m2():.2f}")
print(b.largest_wall())
Exercise 09 — Door Factory (Class Method)
Filename: ex09_door_factory.py
- Add a
@classmethodstandardonDoorthat constructs common sizes by code:"D826"→ 826 mm × 2040 mm"D900"→ 900 mm × 2100 mm
- Unknown codes should raise
ValueError.
from ex04_door import Door
class DoorWithFactory(Door):
@classmethod
def standard(cls, code: str, material: str = "timber", fire_rating_min: int = 0):
# TODO: map code to (width_mm, height_mm) and return cls(...)
# Hint: use a dict
pass
if __name__ == "__main__":
d = DoorWithFactory.standard("D900", material="steel", fire_rating_min=60)
print(d.width_mm, d.height_mm)
Exercise 10 — Geometry Utils (Static Method)
Filename: ex10_geometry_utils.py
- Create
GeometryUtilswith a@staticmethodmm_to_m(value_mm)returning metres asfloat. - Refactor
Opening.area_m2to use this utility.
class GeometryUtils:
@staticmethod
def mm_to_m(value_mm: float) -> float:
# TODO
return 0.0
# Optional: demonstrate reuse with Opening
class Opening:
def __init__(self, width_mm: int, height_mm: int):
self.width_mm = width_mm
self.height_mm = height_mm
def area_m2(self) -> float:
w = GeometryUtils.mm_to_m(self.width_mm)
h = GeometryUtils.mm_to_m(self.height_mm)
return w * h
if __name__ == "__main__":
o = Opening(1000, 1000)
print(o.area_m2()) # expected: 1.0
Hint for all exercises: Keep classes small and focused. Prefer composition (has-a) before inheritance (is-a). Write tiny tests in if __name__ == "__main__": to check behaviour early.
Next Steps
- Create a simple “solutions” page mirroring these headings.
- Add unit tests with
pytestlater to formalise checks.