Learning outcomes
- Combine multiple concepts into a working application
Core concepts
Lessons
Module goal
Move from single-file scripts to properly organised, multi-file programs — closing out Level 3 and preparing for the Intermediate Capstone.
Lesson 1 of 5 · lecture
Why one giant file becomes a problem
Step 1 of 2
Every project so far has lived in a single .py file. That's manageable at 50 or 100 lines. Real applications grow to thousands of lines covering many concerns — user input, data storage, calculations, API calls, display formatting. Cramming all of that into one file makes it hard to find anything, hard to test pieces independently, and hard for more than one person to work on the project without stepping on each other's changes.
Teaching point
Imagine a repair shop where every tool, spare part and customer file sat in one unsorted pile in the middle of the room. Everything is technically there, but finding anything — or letting two technicians work at once — becomes painful. Organising a codebase into files is the same principle as labelled drawers and sections.
Guided labs
Lab — build a small expense tracker (multi-file)
- 1.Create a project folder with three files: main.py, expense.py, storage.py
- 2.In expense.py, define an Expense class with description and amount attributes and a display() method
- 3.In storage.py, write save_expense(expense) (appends a formatted line to expenses.txt) and load_expenses() (reads the file and returns a list of Expense objects)
- 4.In main.py, build a menu loop letting the user add an expense, view all expenses, see a running total, or exit
# expense.py
class Expense:
def __init__(self, description, amount):
self.description = description
self.amount = amount
def display(self):
return f"{self.description}: {self.amount}"
def to_line(self):
return f"{self.description},{self.amount}\n"
# storage.py
from expense import Expense
def save_expense(expense):
with open("expenses.txt", "a") as file:
file.write(expense.to_line())
def load_expenses():
expenses = []
try:
with open("expenses.txt", "r") as file:
for line in file:
description, amount = line.strip().split(",")
expenses.append(Expense(description, float(amount)))
except FileNotFoundError:
pass
return expenses
# main.py
from expense import Expense
from storage import save_expense, load_expenses
expenses = load_expenses()
while True:
print("\n1. Add expense 2. View expenses 3. View total 4. Exit")
choice = input("Choose an option: ")
if choice == "1":
description = input("Description: ")
amount = float(input("Amount: "))
new_expense = Expense(description, amount)
expenses.append(new_expense)
save_expense(new_expense)
elif choice == "2":
for expense in expenses:
print(expense.display())
elif choice == "3":
total = sum(expense.amount for expense in expenses)
print(f"Total expenses: {total}")
elif choice == "4":
break
else:
print("Invalid option, try again.")Teaching point
storage.py imports Expense from expense.py, and main.py imports from both — the first time files depend on each other, not just on an external library. Trace the flow: main.py calls a function from storage.py, which uses the Expense class from expense.py.
Intermediate Capstone — weather/currency dashboard app
- 1.Fetch live data from a public API (weather or currency exchange)
- 2.Handle connection errors and invalid input gracefully, without crashing
- 3.Organise into at least main.py (program flow/menu) and a separate file handling the API logic
- 4.Present the fetched data in clean, readable output
# weather_api.py
import requests
def get_weather(city, api_key):
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
try:
response = requests.get(url)
data = response.json()
if response.status_code == 200:
return {
"temperature": data["main"]["temp"],
"condition": data["weather"][0]["description"]
}
else:
return None
except requests.exceptions.ConnectionError:
return "connection_error"
# main.py
from weather_api import get_weather
api_key = "your_api_key_here"
while True:
print("\n1. Check weather 2. Exit")
choice = input("Choose an option: ")
if choice == "1":
city = input("Enter city: ")
result = get_weather(city, api_key)
if result == "connection_error":
print("No internet connection. Please try again later.")
elif result is None:
print(f"Could not find weather for '{city}'.")
else:
print(f"{city}: {result['temperature']}°C, {result['condition']}")
elif choice == "2":
break
else:
print("Invalid option, try again.")Teaching point
main.py never calls requests directly — it only calls get_weather(). The part of the program that talks to the outside world is isolated from the part that talks to the user, so either can change without touching the other.
Success criteria
The expense tracker runs correctly split across three files with expenses persisting between runs and a correct total; the capstone dashboard shows live data for a valid city, gives a clear message for an invalid city, and doesn't crash when the connection drops.
Knowledge Check
Question 1 of 6
Why does splitting a large program into multiple files matter as a project grows?
What you read
- How to break a problem into smaller pieces
- Organizing code across multiple files
What you understand
Key takeaways
- — Real applications are modular, not one giant script
Hands-on lab
Lab tasks
- 1.Build a small multi-file application (e.g., an expense tracker)
Knowledge check
- Identify poor vs. good code organization
Write your answers in your own notes before moving on — explaining a concept in your own words is the fastest way to find the gaps.
Intermediate capstone
Weather / Currency Dashboard
Build a Weather/Currency Dashboard App: fetches live data from an API, handles errors gracefully, and presents results through a clean, modular program structure.