BRAINBIT INFOTECH PVT LTD · RANCHI
COMPLETE PYTHON COURSE · PART 3 OF 5

Files, Errors & OOP

Save data in files, handle errors gracefully, and model real things with classes and objects.

5 modules~17 hours25 practice examplesNeeds Parts 1-2

By the end of this part you can build

  • An attendance register saved in CSV
  • A safe calculator that never crashes
  • Bank account, library and cart classes
  • Leaderboards with comprehensions and decorators

Every code example has a ▶ Run button. Click ✎ Edit & Run, or press L, to open the Python Lab.

ROADMAP FOR THIS PART

Module 12Files: Text, CSV & JSON3.5 h
Module 13Errors, Exceptions & Debugging3 h
Module 14Classes & Objects (OOP Part 1)3.5 h
Module 15Inheritance, Polymorphism & Magic Methods3.5 h
Module 16Pythonic Python: Comprehensions, Generators & Decorators3.5 h
print("Hi") >>> _
PythonBefore we begin

How to Use This Part

Suggested teaching hours per module (Part 3)012343.5 hM123 hM133.5 hM143.5 hM153.5 hM16

Every module has

  1. 1
    A "Where is it used?" diagram of real projects
  2. 2
    Concept slides with code + terminal: press ▶ Run to watch the output appear
  3. 3
    Step-through animations: watch variables change line by line
  4. 4
    A Live Lab: edit the code and run real Python in the browser
  5. 5
    5 practice examples (Beginner to Advanced) with solutions
  6. 6
    An animated recap + quiz
Controls→ next, ← back, M menu, L Python Lab, F fullscreen. In the Lab, Ctrl+Enter runs the code.
Running PythonOutputs on the slides were produced by real Python 3, so they work offline. The Lab loads Python from the internet the first time (10-40 s). Offline? Use ⬇ .py and run the file in IDLE or VS Code (folder D:\python-practice).
MODULE 12

Files: Text, CSV & JSON

⏱ 3.5 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • Why programs need files
  • open(), modes r / w / a and the with statement
  • Reading and writing text files line by line
  • CSV files for tables (csv module)
  • JSON files for settings and records (json module)

You will build

  • A diary app that remembers
  • An attendance register in CSV
  • A contact book saved as JSON
open()with open(f) as f:json.dump(data, f)
Module 12Real-world use

Where Is This Used in Real Projects?

open()save & load data📝Notes appssave text📊Excel / CSVreports, marks⚙SettingsJSON config📜Log fileswhat happened when✅Attendancedaily CSV sheets🎮Game saveslevel, score

Real projects that use this

  • Every app that "remembers" after you close it saves data to files (or a database)
  • Offices exchange data as CSV files that open in Excel and Google Sheets
  • Apps keep settings (theme, language) in JSON files; APIs send JSON too
  • Servers write log files so engineers can see what went wrong
Module 12Why files?

Variables Forget, Files Remember

Variables (RAM)marks = [78, 92]lost when the program ends⚡Files (disk)marks.csvstays after you close the app💾write / saveread / load
open the file➜read or write➜close the file
ModeMeaningIf file existsIf missing
"r"read (default)reads itFileNotFoundError
"w"writeerases it!creates it
"a"appendadds at the endcreates it
"x"create onlyerrorcreates it
with auto-closes
open("notes.txt", "w")file name + mode
as ff = the file
:
Common mistake"w" wipes the old content. Use "a" when you want to keep adding.
Module 12Write a file

Writing a Text File

main.py  PY
1with open("notice.txt", "w", encoding="utf-8") as f:
2 f.write("BRAINBIT INFOTECH PVT LTD, RANCHI\n")
3 f.write("Python batch starts on 15 October.\n")
4 f.write("Timing: 10 AM to 12 PM\n")
5
6print("notice.txt saved!")
Terminal
C:\python-practice> python main.py
notice.txt saved!
📄 notice.txt (file on disk)TXT
1BRAINBIT INFOTECH PVT LTD, RANCHI
2Python batch starts on 15 October.
3Timing: 10 AM to 12 PM
  1. 1
    with open(..., "w") creates (or empties) the file
  2. 2
    f.write(text) writes text; add \n yourself for new lines
  3. 3
    When the with block ends, Python closes the file automatically
RememberAlways add encoding="utf-8" so Hindi text and symbols like ₹ save correctly on Windows.
Module 12Read a file

Reading a Text File: All at Once or Line by Line

main.py  PY
1with open("cities.txt", encoding="utf-8") as f:
2 content = f.read()
3print(repr(content[:20]))
4
5with open("cities.txt", encoding="utf-8") as f:
6 for number, line in enumerate(f, start=1):
7 print(number, line.strip())
8
9with open("cities.txt", encoding="utf-8") as f:
10 cities = f.read().splitlines()
11print(cities)
Terminal
C:\python-practice> python main.py
'Ranchi\nJamshedpur\nDh'
1 Ranchi
2 Jamshedpur
3 Dhanbad
4 Bokaro
5 Deoghar
['Ranchi', 'Jamshedpur', 'Dhanbad', 'Bokaro', 'Deoghar']
WayGives
f.read()the whole file as one string
for line in f:one line at a time (best for big files)
f.readlines()a list of lines with \n
f.read().splitlines()a clean list of lines
RememberEach line ends with \n. Use line.strip() to remove it before comparing or printing.
📄 cities.txt (file on disk)TXT
1Ranchi
2Jamshedpur
3Dhanbad
4Bokaro
5Deoghar
Module 12Append

Append Mode: Add Without Erasing (A Simple Log)

main.py  PY
1from datetime import datetime
2
3def log(message):
4 stamp = datetime(2026, 10, 8, 10, 15).strftime("%d-%m-%Y %H:%M")
5 with open("activity.log", "a", encoding="utf-8") as f:
6 f.write(f"[{stamp}] {message}\n")
7
8log("Teacher logged in")
9log("Attendance marked for 9A")
10log("Report downloaded")
11print(open("activity.log").read())
Terminal
C:\python-practice> python main.py
[07-10-2026 17:40] System started
[08-10-2026 10:15] Teacher logged in
[08-10-2026 10:15] Attendance marked for 9A
[08-10-2026 10:15] Report downloaded
+

"a" mode

Each log() call adds one line at the end. Old lines stay.
In real projectsServers, ATMs and apps write logs exactly like this. When something breaks, engineers read the log.
Module 12Paths

File Paths: Where Is My File?

PathTypeMeaning
"marks.csv"relativesame folder as where you run the program
"data/marks.csv"relativeinside the data sub-folder
r"D:\python-practice\marks.csv"absolutefull path on Windows (r = raw string)
"D:/python-practice/marks.csv"absoluteforward slashes also work on Windows
Common mistakeMost "FileNotFoundError" problems happen because the program runs from a different folder. In VS Code, open the project folder first.
paths.pyPY
1from pathlib import Path
2
3folder = Path("D:/python-practice/reports")
4folder.mkdir(parents=True, exist_ok=True) # make folder if needed
5file = folder / "march.csv" # join paths with /
6print(file.name, file.suffix, file.exists())
7
8import os
9print(os.getcwd()) # current working folder
10print(os.path.exists("marks.csv"))
Rememberpathlib is the modern, cross-platform way to handle paths. You will use it more in Module 17.
Module 12CSV write

CSV Files: Tables That Open in Excel

main.py  PY
1import csv
2
3rows = [
4 ["roll", "name", "status"],
5 [1, "Aman", "P"],
6 [2, "Priya", "P"],
7 [3, "Rahul", "A"],
8]
9with open("attendance.csv", "w", newline="", encoding="utf-8") as f:
10 writer = csv.writer(f)
11 writer.writerows(rows)
12print("Saved", len(rows) - 1, "students")
Terminal
C:\python-practice> python main.py
Saved 3 students
📄 attendance.csv (file on disk)CSV
1roll,name,status
21,Aman,P
32,Priya,P
43,Rahul,A
,

CSV = Comma-Separated Values

Plain text where each line is a row and commas separate columns. Excel, Google Sheets and databases all read it.
Common mistakeUse newline="" when writing CSV on Windows, otherwise you get blank lines between rows.
Module 12CSV read

Reading CSV with DictReader: Each Row Becomes a Dict

main.py  PY
1import csv
2
3with open("marks.csv", encoding="utf-8") as f:
4 reader = csv.DictReader(f)
5 for row in reader:
6 total = int(row["maths"]) + int(row["science"]) + int(row["english"])
7 print(f"{row['name']:<6} total {total:>3} avg {total / 3:5.1f}")
Terminal
C:\python-practice> python main.py
Aman total 235 avg 78.3
Priya total 274 avg 91.3
Rahul total 193 avg 64.3
Sneha total 259 avg 86.3
Kiran total 158 avg 52.7
📄 marks.csv (file on disk)CSV
1roll,name,maths,science,english
21,Aman,78,85,72
32,Priya,95,91,88
43,Rahul,65,58,70
54,Sneha,88,92,79
65,Kiran,45,52,61
  1. 1
    DictReader uses the first line as keys
  2. 2
    Each row is a dict: row["maths"]
  3. 3
    Values are strings: convert with int()
In real projectsReports from school ERPs, bank statements and online forms download as CSV. Python can process thousands of rows in a second.
Module 12JSON

JSON Files: Save Dictionaries and Lists

main.py  PY
1import json
2
3settings = {
4 "user": "priya",
5 "theme": "dark",
6 "font_size": 16,
7 "subjects": ["Maths", "Physics"],
8 "notifications": True,
9}
10with open("settings.json", "w", encoding="utf-8") as f:
11 json.dump(settings, f, indent=2)
12
13with open("settings.json", encoding="utf-8") as f:
14 loaded = json.load(f)
15print(loaded["theme"], loaded["subjects"][0], type(loaded))
Terminal
C:\python-practice> python main.py
dark Maths <class 'dict'>
📄 settings.json (file on disk)JSON
1{
2 "user": "priya",
3 "theme": "dark",
4 "font_size": 16,
5 "subjects": [
6 "Maths",
7 "Physics"
8 ],
9 "notifications": true
10}
FunctionDoes
json.dump(obj, f)save to a file
json.load(f)read from a file
json.dumps(obj)convert to a JSON string
json.loads(text)convert a string to Python
In real projectsJSON is the language of the web: APIs (Module 20) send and receive it. True becomes true in JSON.
Module 12Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
Mango costs 60
Banana costs 40
{"Mango": 60, "Banana": 40}
Your tasks
  1. Add two more fruits to the CSV
  2. Calculate and print the total price of all fruits while reading
  3. Change mode "w" to "a" and run twice: what happens?
  4. Load fruits.json and print the costliest fruit
PracticePractice 1 of 5

Daily Diary App

BeginnerScenario. A student diary saves each entry with the date, adding to the same file every day.

Your tasks

  1. Ask for today's entry
  2. Append it to diary.txt with a date header
  3. Then read the whole diary back and print it
  4. Count how many entries exist (lines starting with "==")
🏢 Used in real projects: Journal apps, chat history and audit logs append new records without touching old ones.
practice_12_1.py  PY
1from datetime import date
2
3entry = input("Dear diary: ")
4with open("diary.txt", "a", encoding="utf-8") as f:
5 f.write(f"== {date(2026, 10, 8):%d %b %Y} ==\n{entry}\n")
6
7with open("diary.txt", encoding="utf-8") as f:
8 text = f.read()
9print(text)
10print("Entries so far:", text.count("== "))
PracticePractice 2 of 5

Attendance Register in CSV

EasyScenario. The class teacher marks attendance for 5 students and saves it to a CSV that the office can open in Excel.

Your tasks

  1. Loop over the students and ask P or A
  2. Save rows [date, roll, name, status] with csv.writer
  3. Count present and absent
  4. Print a summary
🏢 Used in real projects: School ERPs and HR attendance systems export daily attendance as CSV for Excel and payroll.
practice_12_2.py  PY
1import csv
2students = [(1, "Aman"), (2, "Priya"), (3, "Rahul"), (4, "Sneha"), (5, "Kiran")]
3day = "08-10-2026"
4present = 0
5with open("attendance_9A.csv", "w", newline="", encoding="utf-8") as f:
6 w = csv.writer(f)
7 w.writerow(["date", "roll", "name", "status"])
8 for roll, name in students:
9 s = input(f"{roll} {name} (P/A): ").strip().upper()
10 w.writerow([day, roll, name, s])
11 present += s == "P"
12print(f"Present: {present}, Absent: {len(students) - present}")
PracticePractice 3 of 5

Marks CSV Report Card

IntermediateScenario. Read marks.csv, calculate each student's total and grade, and write a new result.csv.

Your tasks

  1. Read with csv.DictReader
  2. Calculate total, percentage and grade
  3. Write result.csv with csv.DictWriter
  4. Print the class topper
🏢 Used in real projects: Result processing for boards, payroll calculations and sales reports: read CSV, calculate, write CSV.
practice_12_3.py  PY
1import csv
2
3def grade(p):
4 return "A" if p >= 80 else "B" if p >= 60 else "C" if p >= 40 else "F"
5
6results = []
7with open("marks.csv", encoding="utf-8") as f:
8 for r in csv.DictReader(f):
9 total = int(r["maths"]) + int(r["science"]) + int(r["english"])
10 pct = round(total / 3, 1)
11 results.append({"name": r["name"], "total": total, "percent": pct, "grade": grade(pct)})
12
13with open("result.csv", "w", newline="", encoding="utf-8") as f:
14 w = csv.DictWriter(f, fieldnames=["name", "total", "percent", "grade"])
15 w.writeheader()
16 w.writerows(results)
17
18top = max(results, key=lambda r: r["total"])
19print(f"{len(results)} results written. Topper: {top['name']} ({top['percent']}%)")
PracticePractice 4 of 5

App Settings in JSON

ChallengingScenario. A quiz app stores user settings in JSON. Load them (or create defaults if missing), change one setting, and save again.

Your tasks

  1. Use os.path.exists to check for config.json
  2. Load it with json.load, or use defaults
  3. Update the setting chosen by the user
  4. Save with indent=2 and print it
🏢 Used in real projects: VS Code, games and mobile apps save preferences in JSON (or similar) files.
practice_12_4.py  PY
1import json, os
2
3DEFAULTS = {"language": "English", "sound": True, "difficulty": "easy", "high_score": 0}
4if os.path.exists("config.json"):
5 with open("config.json") as f:
6 config = json.load(f)
7 print("Loaded saved settings")
8else:
9 config = DEFAULTS.copy()
10 print("No settings found, using defaults")
11
12config["difficulty"] = input("Difficulty (easy/medium/hard): ")
13config["high_score"] = max(config["high_score"], 85)
14
15with open("config.json", "w") as f:
16 json.dump(config, f, indent=2)
17print("Saved:", config)
PracticePractice 5 of 5

Contact Book That Remembers

AdvancedScenario. Build a contact book with load() and save() functions so contacts survive after the program closes.

Your tasks

  1. load() returns a dict from contacts.json (or {} if missing)
  2. save(book) writes it back
  3. Menu: add, search, list, quit
  4. Save automatically after every change
🏢 Used in real projects: Every real app separates load/save (persistence) from the menu logic. Later you can swap JSON for a database without changing the menu.
practice_12_5.py  PY
1import json, os
2FILE = "contacts.json"
3
4def load():
5 if not os.path.exists(FILE):
6 return {}
7 with open(FILE, encoding="utf-8") as f:
8 return json.load(f)
9
10def save(book):
11 with open(FILE, "w", encoding="utf-8") as f:
12 json.dump(book, f, indent=2, sort_keys=True)
13
14book = load()
15print(f"{len(book)} contacts loaded")
16while True:
17 c = input("1 Add 2 Search 3 List 4 Quit: ")
18 if c == "1":
19 name = input("Name: ").title()
20 book[name] = input("Phone: ")
21 save(book)
22 elif c == "2":
23 name = input("Search: ").title()
24 print(" ", book.get(name, "Not found"))
25 elif c == "3":
26 for n, p in sorted(book.items()):
27 print(f" {n:<8} {p}")
28 else:
29 break
Module 12Recap

Module 12 Recap & Quiz

What you learned

  • 1Variables are lost when a program ends; files keep data
  • 2Use with open(name, mode, encoding="utf-8") as f: (auto-close)
  • 3Modes: "r" read, "w" write (erases!), "a" append
  • 4Read all with read(), or loop for line in f + strip()
  • 5CSV: csv.writer / DictReader / DictWriter; use newline="" when writing
  • 6JSON: json.dump / json.load for dicts and lists

Quick quiz

Which mode adds to the end without erasing?

a = append.

What does with do for files?

The file is closed when the block ends, even on errors.

Values read by csv.DictReader are...

CSV is text: convert with int() or float().
MODULE 13

Errors, Exceptions & Debugging

⏱ 3 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • Common error types and what they mean
  • try / except to handle errors without crashing
  • else, finally and catching many errors
  • raise and custom exceptions
  • Debugging: tracebacks, print, breakpoints and assert

You will build

  • A calculator that never crashes
  • A bank withdrawal with a custom error
  • A marks entry with retry
try:except ValueError:raise
Module 13Real-world use

Where Is This Used in Real Projects?

try / excepthandle problems gracefully🏧ATMinvalid amount📝Formswrong input type💳Paymentsgateway timeout📁Filesfile not found🌐Internetnetwork down🚨Crash reportslog the error

Real projects that use this

  • A banking app must never crash when a user types letters in the amount box: it catches the error and shows a message
  • Payment apps retry or show "Try again" when the network fails
  • Apps send crash details to developers (logging) so bugs get fixed
  • Good error handling is the difference between a student project and a professional product
Module 13Error types

Common Exceptions You Will Meet

ExceptionExample that causes it
ValueErrorint("abc")
TypeError"5" + 5
ZeroDivisionError10 / 0
IndexError[1, 2][5]
KeyError{"a": 1}["b"]
NameErrorusing a variable never created
FileNotFoundErroropen("missing.txt")
AttributeError"hi".push()
Python Shell (IDLE / terminal)
# Python interactive shell. Press Next to type each line.
0 / 6
RememberAn exception is Python saying "I cannot do this". Without handling, the program stops.
Module 13try / except

try / except: Catch the Error, Keep Running

main.py  PY
1try:
2 age = int(input("Enter your age: "))
3 print("Next year you will be", age + 1)
4except ValueError:
5 print("Please type a number like 15, not words.")
6
7print("Program continues normally.")
Terminal
C:\python-practice> python main.py
Enter your age: fifteen
Please type a number like 15, not words.
Program continues normally.
  1. 1
    Python tries the code in the try block
  2. 2
    If a ValueError happens, it jumps to except ValueError
  3. 3
    The program does not crash; it continues after the block
Common mistakeCatch specific errors (except ValueError), not everything. A bare except: hides real bugs.
Module 13Full picture

try / except / else / finally

try: risky codeerror?Noelse:Yesexcept ValueErrorexcept ZeroDivision...finally: alwaysprogram continues (no crash)
Choose an input to follow the path.
full_try.py  PY
1try:
2 n = int(input("Number: "))
3 result = 100 / n
4except ValueError:
5 print("Not a number!")
6except ZeroDivisionError:
7 print("Cannot divide by zero!")
8else:
9 print("100 /", n, "=", result)
10finally:
11 print("Calculation attempt finished.")
BlockRuns when
tryalways (first)
except Xerror X happened
elseno error happened
finallyalways, error or not (cleanup)
Module 13Error details

Catch Several Errors and Read the Message with "as e"

main.py  PY
1data = {"Aman": [78, 85], "Priya": []}
2
3for name in ["Aman", "Priya", "Rahul"]:
4 try:
5 marks = data[name]
6 avg = sum(marks) / len(marks)
7 print(f"{name}: average {avg}")
8 except KeyError as e:
9 print(f"{name}: no record found ({e})")
10 except ZeroDivisionError:
11 print(f"{name}: no marks entered yet")
12 except Exception as e:
13 print("Unexpected problem:", type(e).__name__, e)
Terminal
C:\python-practice> python main.py
Aman: average 81.5
Priya: no marks entered yet
Rahul: no record found ('Rahul')
e

except X as e

e holds the error object. Print it or save it in a log file.
1-2-3

Order matters

Put specific errors first, general Exception last.
In real projectsEach student is processed safely; one bad record does not stop the whole report.
Module 13raise

raise: Create Your Own Error When Data Is Wrong

main.py  PY
1def set_age(age):
2 if not isinstance(age, int):
3 raise TypeError("Age must be a whole number")
4 if age < 0 or age > 120:
5 raise ValueError(f"Age {age} is not realistic")
6 return age
7
8for test in [15, -3, 200, "ten"]:
9 try:
10 print("Saved age:", set_age(test))
11 except (ValueError, TypeError) as e:
12 print("Rejected:", e)
Terminal
C:\python-practice> python main.py
Saved age: 15
Rejected: Age -3 is not realistic
Rejected: Age 200 is not realistic
Rejected: Age must be a whole number
  1. 1
    A function checks its input
  2. 2
    If something is wrong, it raises an exception with a clear message
  3. 3
    The caller decides what to do with try / except
In real projectsLibraries like pandas and requests raise exceptions to tell you what went wrong. Your functions should do the same.
Module 13Custom exceptions

Custom Exceptions for Your App

main.py  PY
1class InsufficientBalanceError(Exception):
2 """Raised when a withdrawal is more than the balance."""
3
4def withdraw(balance, amount):
5 if amount <= 0:
6 raise ValueError("Amount must be positive")
7 if amount > balance:
8 raise InsufficientBalanceError(f"Balance Rs {balance}, asked Rs {amount}")
9 return balance - amount
10
11balance = 5000
12for amt in [1500, 9000, -50]:
13 try:
14 balance = withdraw(balance, amt)
15 print(f"Withdrew {amt}. New balance {balance}")
16 except InsufficientBalanceError as e:
17 print("Declined:", e)
18 except ValueError as e:
19 print("Invalid:", e)
Terminal
C:\python-practice> python main.py
Withdrew 1500. New balance 3500
Declined: Balance Rs 3500, asked Rs 9000
Invalid: Amount must be positive
!

class MyError(Exception)

A new exception type with a meaningful name. (Classes are explained fully in Module 14.)
In real projectsPayment and banking systems define errors like InsufficientFunds, CardExpired, LimitExceeded.
Module 13Retry pattern

Keep Asking Until the Input Is Valid

main.py  PY
1while True:
2 try:
3 marks = int(input("Marks (0-100): "))
4 if not 0 <= marks <= 100:
5 raise ValueError("out of range")
6 break # valid: leave the loop
7 except ValueError as e:
8 print(f"Invalid ({e}). Try again.")
9
10print("Saved marks:", marks)
Terminal
C:\python-practice> python main.py
Marks (0-100): ninety
Invalid (invalid literal for int() with base 10: 'ninety'). Try again.
Marks (0-100): 150
Invalid (out of range). Try again.
Marks (0-100): 92
Saved marks: 92
  1. 1
    while True keeps asking
  2. 2
    Bad type or range raises ValueError
  3. 3
    Only a valid number reaches break
RememberThis is the standard way to get safe input in command-line programs.
Module 13Debugging

Reading a Traceback Through Functions

report.py  PY
1def percent(marks, total):
2 return marks / total * 100
3
4def report(student):
5 p = percent(student["marks"], student["total"])
6 print(student["name"], round(p, 1))
7
8report({"name": "Aman", "marks": 412, "total": 500})
9report({"name": "Riya", "marks": 380, "total": 0})
Terminal
C:\python-practice> python report.py
Aman 82.4
Traceback (most recent call last):
File "main.py", line 9, in <module>
report({"name": "Riya", "marks": 380, "total": 0})
~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "main.py", line 5, in report
p = percent(student["marks"], student["total"])
File "main.py", line 2, in percent
return marks / total * 100
~~~~~~^~~~~~~
ZeroDivisionError: division by zero
  1. 1
    Read from the bottom: ZeroDivisionError: division by zero
  2. 2
    The last "File" line shows where it broke: inside percent, line 2
  3. 3
    Lines above show how we got there: line 9 called report(), which called percent()
  4. 4
    Fix the cause: the data had total = 0
RememberDebugging tools: print() values, VS Code breakpoints (click left of a line number, press F5), and assert total > 0, "total is zero" to catch bad data early.
Module 13Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
5.0
Warning: division by zero, returning 0
0
Skipping bad value: abc
Total: 35
Your tasks
  1. Add finally to safe_divide that prints "done"
  2. Make a list with a missing key lookup and catch KeyError
  3. Write check_pin(pin) that raises ValueError if it is not 4 digits
  4. Remove a try/except on purpose and read the traceback
PracticePractice 1 of 5

Safe Number Input Function

BeginnerScenario. Make a reusable get_int() function that keeps asking until the user types a valid whole number within limits.

Your tasks

  1. Parameters: prompt, low, high
  2. Use while True + try / except ValueError
  3. Check the range and give a helpful message
  4. Use it to ask the class (1-12) and the roll number (1-60)
🏢 Used in real projects: Every command-line tool and form validator has a safe-input helper like this.
practice_13_1.py  PY
1def get_int(prompt, low, high):
2 while True:
3 try:
4 value = int(input(prompt))
5 if low <= value <= high:
6 return value
7 print(f" Please enter a number from {low} to {high}.")
8 except ValueError:
9 print(" That is not a whole number.")
10
11class_no = get_int("Class (1-12): ", 1, 12)
12roll = get_int("Roll number (1-60): ", 1, 60)
13print(f"Registered: class {class_no}, roll {roll}")
PracticePractice 2 of 5

Calculator That Never Crashes

EasyScenario. A calculator for a primary school: it must handle wrong numbers, unknown operators and division by zero.

Your tasks

  1. Ask two numbers and an operator in a loop
  2. Use a dict of operations or if / elif
  3. Catch ValueError and ZeroDivisionError separately
  4. Stop when the user types "q"
🏢 Used in real projects: Calculator apps, billing screens and spreadsheet formulas handle every bad input without crashing.
practice_13_2.py  PY
1while True:
2 op = input("Operator (+ - * /) or q: ")
3 if op == "q":
4 break
5 try:
6 a = float(input(" First number: "))
7 b = float(input(" Second number: "))
8 if op == "+": r = a + b
9 elif op == "-": r = a - b
10 elif op == "*": r = a * b
11 elif op == "/": r = a / b
12 else:
13 raise ValueError(f"unknown operator {op}")
14 print(f" = {r:g}")
15 except ZeroDivisionError:
16 print(" Cannot divide by zero")
17 except ValueError as e:
18 print(" Error:", e)
19print("Bye!")
PracticePractice 3 of 5

Load a File with a Fallback

IntermediateScenario. A school app loads students.txt. If it is missing, it creates it with sample data instead of crashing.

Your tasks

  1. Try to open and read the file
  2. On FileNotFoundError create it with defaults
  3. Use else to report how many names were loaded
  4. Use finally to print "Startup complete"
🏢 Used in real projects: Apps create default config files on first run; this is exactly how they avoid crashing on a new computer.
practice_13_3.py  PY
1def load_students(path):
2 try:
3 with open(path, encoding="utf-8") as f:
4 names = f.read().splitlines()
5 except FileNotFoundError:
6 print(f"{path} not found. Creating it with sample data.")
7 names = ["Aman", "Priya", "Rahul"]
8 with open(path, "w", encoding="utf-8") as f:
9 f.write("\n".join(names))
10 else:
11 print(f"Loaded {len(names)} students from {path}")
12 finally:
13 print("Startup complete")
14 return names
15
16print(load_students("students.txt"))
17print(load_students("students.txt"))
PracticePractice 4 of 5

Bank Withdrawal with Custom Errors

ChallengingScenario. An ATM program defines its own exceptions for low balance and daily limit, and logs every failure.

Your tasks

  1. Create InsufficientBalance and DailyLimitExceeded exception classes
  2. Write withdraw(amount) that raises them
  3. Process a list of requests with try / except
  4. Keep a list of error messages and print it at the end
🏢 Used in real projects: Core banking systems use named exceptions so each failure gets the right message, SMS alert and log entry.
practice_13_4.py  PY
1class InsufficientBalance(Exception): pass
2class DailyLimitExceeded(Exception): pass
3
4balance, withdrawn_today, LIMIT = 12000, 0, 10000
5errors = []
6
7def withdraw(amount):
8 global balance, withdrawn_today
9 if amount > balance:
10 raise InsufficientBalance(f"need {amount}, have {balance}")
11 if withdrawn_today + amount > LIMIT:
12 raise DailyLimitExceeded(f"limit {LIMIT}, used {withdrawn_today}")
13 balance -= amount
14 withdrawn_today += amount
15
16for amt in [4000, 5000, 3000, 15000]:
17 try:
18 withdraw(amt)
19 print(f"OK {amt:>6} balance {balance}")
20 except (InsufficientBalance, DailyLimitExceeded) as e:
21 errors.append(f"{type(e).__name__}: {e}")
22 print(f"NO {amt:>6} {type(e).__name__}")
23print("Error log:", *errors, sep="\n ")
PracticePractice 5 of 5

Validate a Batch of Marks Records

AdvancedScenario. A data-entry operator typed marks for many students. Clean the data: skip and report bad rows without stopping.

Your tasks

  1. Each record is a string like "Aman,78"
  2. Split, convert and validate (0-100) inside try
  3. Collect good records in a dict and bad ones with reasons
  4. Print a summary and use assert to double-check the total count
🏢 Used in real projects: Data pipelines in banks, schools and government portals validate records and send bad rows to an error report.
practice_13_5.py  PY
1raw = ["Aman,78", "Priya,95", "Rahul,abc", "Sneha", "Kiran,105", "Dev,64"]
2good, bad = {}, []
3for line in raw:
4 try:
5 name, marks = line.split(",")
6 marks = int(marks)
7 if not 0 <= marks <= 100:
8 raise ValueError("marks out of range")
9 good[name] = marks
10 except ValueError as e:
11 bad.append((line, str(e)))
12
13assert len(good) + len(bad) == len(raw), "lost a record!"
14print("Valid:", good)
15print("Rejected:")
16for line, why in bad:
17 print(f" {line!r:<12} -> {why}")
Module 13Recap

Module 13 Recap & Quiz

What you learned

  • 1Errors are exceptions; unhandled ones stop the program
  • 2try / except catches them; catch specific types like ValueError
  • 3else runs when there is no error; finally always runs
  • 4raise your own errors with clear messages; make custom ones with class X(Exception)
  • 5Retry pattern: while True + try / except + break
  • 6Debug: read tracebacks bottom-up, print values, breakpoints, assert

Quick quiz

int("12a") raises...

The text cannot be converted to int: ValueError.

Which block always runs?

finally runs whether or not an error happened.

Where should you start reading a traceback?

The last line names the error and reason.
MODULE 14

Classes & Objects (OOP Part 1)

⏱ 3.5 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • What object-oriented programming is
  • Classes as blueprints, objects as things
  • __init__, self and attributes
  • Methods: functions that belong to objects
  • __str__, class attributes and lists of objects

You will build

  • A Student class with a report
  • A BankAccount with deposit and withdraw
  • A Library book issue system
classdef __init__(self):acc.deposit(500)
Module 14Real-world use

Where Is This Used in Real Projects?

classmodel real things🎓School ERPStudent, Teacher🛒E-commerceProduct, Order🏦BankingAccount, Loan🎮GamesPlayer, Enemy🚕Ride appsDriver, Trip📸Social mediaUser, Post

Real projects that use this

  • Big apps model real things as classes: Swiggy has Restaurant, Order, DeliveryPartner
  • Each object keeps its own data: your bank account balance is separate from mine
  • Django (used by Instagram) defines database tables as Python classes
  • Game engines make every character an object with health, position and actions
Module 14The idea

A Class Is a Blueprint; Objects Are Built From It

class Studentname, class, marksreport()is_pass()blueprint (one)object 1name = "Aman"cls = "9A"marks = 78object 2name = "Priya"cls = "10B"marks = 92object 3name = "Rahul"cls = "8C"marks = 65objects (many), each with its own data
WordMeaningExample
Classblueprint / designStudent
Object (instance)one thing made from itAman's record
Attributedata the object hasname, marks
Methodaction the object can doreport()
RememberThink of a cookie cutter (class) and cookies (objects). One cutter, many cookies, each can have different toppings (data).
TipYou have used objects all along: "ranchi".upper() calls the upper method of a str object!
Module 14First class

Writing Your First Class

main.py  PY
1class Student:
2 def __init__(self, name, cls, marks):
3 self.name = name # attributes
4 self.cls = cls
5 self.marks = marks
6
7 def report(self): # a method
8 print(f"{self.name} (class {self.cls}) scored {self.marks}")
9
10 def is_pass(self):
11 return self.marks >= 33
12
13a = Student("Aman", "9A", 78) # create objects
14b = Student("Riya", "10B", 28)
15a.report()
16b.report()
17print(a.name, a.is_pass(), "|", b.name, b.is_pass())
Terminal
C:\python-practice> python main.py
Aman (class 9A) scored 78
Riya (class 10B) scored 28
Aman True | Riya False
  1. 1
    class Student: starts the blueprint (class names use CapitalWords)
  2. 2
    __init__ runs automatically when an object is created: it sets up the attributes
  3. 3
    self means "this particular object"
  4. 4
    Call methods with a dot: a.report()
RememberYou never pass self yourself. a.report() is really Student.report(a).
Module 14Watch objects

Step Through: Creating Two Bank Accounts

Each object keeps its own attributes. Changing x never changes y.

main.py  PY
1class Account:
2 def __init__(self, owner, balance=0):
3 self.owner = owner
4 self.balance = balance
5
6 def deposit(self, amount):
7 self.balance += amount
8
9x = Account("Neha", 1000)
10y = Account("Ravi")
11x.deposit(500)
12y.deposit(200)
13print(x.owner, x.balance, "|", y.owner, y.balance)
Output
Module 14Methods

Methods That Change and Check Object Data

main.py  PY
1class BankAccount:
2 def __init__(self, owner, balance=0):
3 self.owner = owner
4 self.balance = balance
5 self.history = []
6
7 def deposit(self, amount):
8 self.balance += amount
9 self.history.append(f"+{amount}")
10
11 def withdraw(self, amount):
12 if amount > self.balance:
13 print(f" {self.owner}: insufficient balance")
14 return False
15 self.balance -= amount
16 self.history.append(f"-{amount}")
17 return True
18
19acc = BankAccount("Neha Oraon", 2000)
20acc.deposit(1500)
21acc.withdraw(5000)
22acc.withdraw(800)
23print(acc.owner, "balance:", acc.balance)
24print("History:", acc.history)
Terminal
C:\python-practice> python main.py
Neha Oraon: insufficient balance
Neha Oraon balance: 2700
History: ['+1500', '-800']
💾

State

An object remembers its data between method calls: balance and history keep updating.
🛡

Rules inside

The withdraw method protects the balance: the rule lives with the data.
In real projectsThis is why OOP is used in banking software: data and the rules for changing it stay together.
Module 14Printing objects

__str__ and __repr__: Make Objects Print Nicely

main.py  PY
1class Product:
2 def __init__(self, name, price, stock):
3 self.name, self.price, self.stock = name, price, stock
4
5 def __str__(self):
6 return f"{self.name} - Rs {self.price} ({self.stock} in stock)"
7
8 def __repr__(self):
9 return f"Product({self.name!r}, {self.price}, {self.stock})"
10
11p = Product("School Bag", 899, 12)
12print(p) # uses __str__
13print([p]) # lists use __repr__
14print(str(p).upper())
Terminal
C:\python-practice> python main.py
School Bag - Rs 899 (12 in stock)
[Product('School Bag', 899, 12)]
SCHOOL BAG - RS 899 (12 IN STOCK)
s

__str__

Friendly text for users. Used by print() and str().
r

__repr__

Developer text, ideally looks like the code to recreate it. Used in lists and the shell.
RememberWithout __str__, printing shows something like <__main__.Product object at 0x...>.
Module 14Shared data

Class Attributes vs Instance Attributes

main.py  PY
1class Student:
2 school = "St. Xavier's, Ranchi" # class attribute: shared
3 count = 0
4
5 def __init__(self, name):
6 self.name = name # instance attribute: own
7 Student.count += 1
8 self.roll = Student.count
9
10s1 = Student("Aman")
11s2 = Student("Priya")
12print(s1.name, s1.roll, s1.school)
13print(s2.name, s2.roll, s2.school)
14print("Total students:", Student.count)
Terminal
C:\python-practice> python main.py
Aman 1 St. Xavier's, Ranchi
Priya 2 St. Xavier's, Ranchi
Total students: 2
Class attributeInstance attribute
Definedinside class, outside methodswith self.x = ...
Shared?one value for all objectseach object has its own
Exampleschool name, GST rate, countername, roll, marks
In real projectsAuto-numbering like roll numbers and invoice numbers often uses a class counter.
Module 14Many objects

Lists of Objects: Sort, Filter and Report

main.py  PY
1class Student:
2 def __init__(self, name, marks):
3 self.name, self.marks = name, marks
4 def __repr__(self):
5 return f"{self.name}({self.marks})"
6
7class_9a = [Student("Aman", 78), Student("Priya", 92), Student("Rahul", 65), Student("Sneha", 88)]
8
9ranked = sorted(class_9a, key=lambda s: s.marks, reverse=True)
10print("Ranked:", ranked)
11print("Topper:", ranked[0].name)
12print("Above 80:", [s.name for s in class_9a if s.marks > 80])
13print("Average:", sum(s.marks for s in class_9a) / len(class_9a))
Terminal
C:\python-practice> python main.py
Ranked: [Priya(92), Sneha(88), Aman(78), Rahul(65)]
Topper: Priya
Above 80: ['Priya', 'Sneha']
Average: 80.75
  1. 1
    Store objects in a list like any value
  2. 2
    Sort by an attribute with key=lambda s: s.marks
  3. 3
    Filter and total using attributes
In real projectsDatabases return rows as objects in frameworks like Django; you loop, sort and filter them exactly like this.
Module 14Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
Redmi: used 90 min, battery 70%
Redmi: used 120 min, battery 30%
Redmi: fully charged
Your tasks
  1. Create a second phone object and use it differently
  2. Add a method status() that prints "Low battery!" below 20%
  3. Add __str__ so print(phone) looks nice
  4. Add a class attribute made_in = "India"
PracticePractice 1 of 5

Student Class with Report Card

BeginnerScenario. Build a Student class that stores marks for subjects and prints a report.

Your tasks

  1. __init__(name, cls) with an empty marks dict
  2. add_marks(subject, m) method
  3. percentage() and grade() methods
  4. __str__ returns a one-line summary
🏢 Used in real projects: School ERPs model Student, Teacher and Subject as classes with methods for results and attendance.
practice_14_1.py  PY
1class Student:
2 def __init__(self, name, cls):
3 self.name, self.cls = name, cls
4 self.marks = {}
5
6 def add_marks(self, subject, m):
7 self.marks[subject] = m
8
9 def percentage(self):
10 return sum(self.marks.values()) / len(self.marks)
11
12 def grade(self):
13 p = self.percentage()
14 return "A" if p >= 80 else "B" if p >= 60 else "C" if p >= 33 else "F"
15
16 def __str__(self):
17 return f"{self.name} ({self.cls}): {self.percentage():.1f}% grade {self.grade()}"
18
19s = Student("Ankita Toppo", "10A")
20for sub, m in [("Maths", 88), ("Science", 79), ("English", 91), ("Hindi", 84)]:
21 s.add_marks(sub, m)
22print(s)
23print("Best subject:", max(s.marks, key=s.marks.get))
PracticePractice 2 of 5

BankAccount with Mini Statement

EasyScenario. Model a savings account: deposits, withdrawals with a minimum balance of Rs 500, interest, and a mini statement.

Your tasks

  1. Attributes: owner, account number, balance, transactions list
  2. withdraw refuses if the balance would go below 500
  3. add_interest(rate) adds yearly interest
  4. statement() prints the last 5 transactions
🏢 Used in real projects: Core banking systems (and UPI apps) represent accounts as objects with rules inside their methods.
practice_14_2.py  PY
1class SavingsAccount:
2 MIN_BALANCE = 500
3
4 def __init__(self, owner, acc_no, balance=0):
5 self.owner, self.acc_no, self.balance = owner, acc_no, balance
6 self.tx = []
7
8 def deposit(self, amt):
9 self.balance += amt
10 self.tx.append(("CR", amt))
11
12 def withdraw(self, amt):
13 if self.balance - amt < self.MIN_BALANCE:
14 print(f"Declined: must keep Rs {self.MIN_BALANCE}")
15 return
16 self.balance -= amt
17 self.tx.append(("DR", amt))
18
19 def add_interest(self, rate=3.5):
20 i = round(self.balance * rate / 100, 2)
21 self.deposit(i)
22
23 def statement(self):
24 print(f"A/c {self.acc_no} | {self.owner}")
25 for kind, amt in self.tx[-5:]:
26 print(f" {kind} {amt:>10,.2f}")
27 print(f" Balance {self.balance:>8,.2f}")
28
29a = SavingsAccount("Ravi Mahato", "XX4521", 1000)
30a.deposit(5000)
31a.withdraw(5800)
32a.withdraw(2500)
33a.add_interest()
34a.statement()
PracticePractice 3 of 5

Product and Cart Classes

IntermediateScenario. Two classes work together: Product objects go inside a Cart object.

Your tasks

  1. Product(name, price) with __repr__
  2. Cart with add(product, qty) and remove(name)
  3. total() with 5% off above Rs 2000
  4. show() prints the cart
🏢 Used in real projects: Every online shop has Product, Cart, Order and Customer classes working together.
practice_14_3.py  PY
1class Product:
2 def __init__(self, name, price):
3 self.name, self.price = name, price
4 def __repr__(self):
5 return f"{self.name}@{self.price}"
6
7class Cart:
8 def __init__(self):
9 self.items = {} # name -> (product, qty)
10 def add(self, product, qty=1):
11 old = self.items.get(product.name, (product, 0))[1]
12 self.items[product.name] = (product, old + qty)
13 def remove(self, name):
14 self.items.pop(name, None)
15 def total(self):
16 t = sum(p.price * q for p, q in self.items.values())
17 return t * 0.95 if t > 2000 else t
18 def show(self):
19 for p, q in self.items.values():
20 print(f" {p.name:<12} x{q} Rs {p.price * q}")
21 print(f" Payable: Rs {self.total():.2f}")
22
23bag, shoes, bottle = Product("School bag", 899), Product("Shoes", 1250), Product("Bottle", 199)
24cart = Cart()
25cart.add(bag); cart.add(bottle, 2); cart.add(shoes); cart.remove("Bottle"); cart.add(bottle)
26cart.show()
PracticePractice 4 of 5

Mobile Phone Battery Simulator

ChallengingScenario. Simulate a phone: apps drain the battery at different rates, charging adds charge over time, and the phone switches off at 0%.

Your tasks

  1. Attributes: model, battery, is_on
  2. use(app, minutes) with drain rates in a class dict
  3. charge(minutes) adds 1% per minute up to 100
  4. Print a status line after each action
🏢 Used in real projects: Simulations in games, IoT devices and testing tools model devices as objects with state.
practice_14_4.py  PY
1class Phone:
2 DRAIN = {"YouTube": 0.5, "WhatsApp": 0.2, "Game": 0.8}
3
4 def __init__(self, model, battery=60):
5 self.model, self.battery, self.is_on = model, battery, True
6
7 def use(self, app, minutes):
8 if not self.is_on:
9 print(f"{self.model} is off. Charge it first!")
10 return
11 self.battery = max(0, self.battery - self.DRAIN[app] * minutes)
12 if self.battery == 0:
13 self.is_on = False
14 self.status(f"{app} {minutes} min")
15
16 def charge(self, minutes):
17 self.battery = min(100, self.battery + minutes)
18 self.is_on = True
19 self.status(f"charged {minutes} min")
20
21 def status(self, action):
22 bar = "#" * int(self.battery // 10)
23 print(f"{action:<18} [{bar:<10}] {self.battery:5.1f}% {'ON' if self.is_on else 'OFF'}")
24
25p = Phone("Galaxy M14")
26p.use("YouTube", 40); p.use("Game", 60); p.use("WhatsApp", 30); p.charge(45); p.use("Game", 30)
PracticePractice 5 of 5

Library Book Issue System

AdvancedScenario. A school library tracks books and members. Members can issue at most 2 books, and a book cannot be issued twice.

Your tasks

  1. Book(title, author) with is_issued
  2. Member(name) with a list of books
  3. Library with issue(member, title) and return_book()
  4. Print clear messages for every rule
🏢 Used in real projects: Library management, hotel booking and inventory software are classic OOP designs with these exact rules.
practice_14_5.py  PY
1class Book:
2 def __init__(self, title, author):
3 self.title, self.author, self.is_issued = title, author, False
4
5class Member:
6 LIMIT = 2
7 def __init__(self, name):
8 self.name, self.books = name, []
9
10class Library:
11 def __init__(self, books):
12 self.books = {b.title: b for b in books}
13 def issue(self, m, title):
14 b = self.books.get(title)
15 if b is None: return print(f"x {title}: not in library")
16 if b.is_issued: return print(f"x {title}: already issued")
17 if len(m.books) >= Member.LIMIT: return print(f"x {m.name}: limit of {Member.LIMIT} reached")
18 b.is_issued = True; m.books.append(b)
19 print(f"+ {title} -> {m.name}")
20 def return_book(self, m, title):
21 for b in m.books:
22 if b.title == title:
23 b.is_issued = False; m.books.remove(b)
24 return print(f"- {title} returned by {m.name}")
25
26lib = Library([Book("Wings of Fire", "A.P.J. Abdul Kalam"), Book("Godan", "Premchand"), Book("Malgudi Days", "R.K. Narayan")])
27asha, vikas = Member("Asha"), Member("Vikas")
28lib.issue(asha, "Wings of Fire"); lib.issue(vikas, "Wings of Fire"); lib.issue(asha, "Godan")
29lib.issue(asha, "Malgudi Days"); lib.return_book(asha, "Godan"); lib.issue(asha, "Malgudi Days"); lib.issue(vikas, "Python 101")
Module 14Recap

Module 14 Recap & Quiz

What you learned

  • 1A class is a blueprint; an object is one thing built from it
  • 2__init__(self, ...) sets up attributes when an object is created
  • 3self = this object; attributes are self.name
  • 4Methods are functions inside the class: acc.deposit(500)
  • 5__str__ for friendly printing, __repr__ for developers
  • 6Class attributes are shared; instance attributes belong to one object

Quick quiz

What runs automatically when you create an object?

__init__ is the initialiser.

In a.report(), what is self inside report?

self is the object the method was called on.

A class attribute is...

Defined in the class body, shared by all instances.
MODULE 15

Inheritance, Polymorphism & Magic Methods

⏱ 3.5 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • Inheritance: child classes reuse parent code
  • super() and method overriding
  • Polymorphism: same method name, different behaviour
  • Encapsulation with _private names and @property
  • Magic (dunder) methods and abstract classes

You will build

  • Employee and Manager payroll
  • Shapes that calculate their own area
  • UPI / Card / Cash payment classes
super()class Car(Vehicle):__add__
Module 15Real-world use

Where Is This Used in Real Projects?

inheritancereuse & extend💼HR / payrollEmployee, Manager💳PaymentsUPI, Card, Wallet🚕Ride appsAuto, Bike, Cab📐Drawing appsCircle, Rectangle🔔NotificationsSMS, Email, Push🧙GamesHero, Enemy, Boss

Real projects that use this

  • Ola and Rapido treat Auto, Bike and Cab as kinds of Vehicle: shared code, different fares
  • Payment gateways have one pay() method that works differently for UPI, card and wallet (polymorphism)
  • Django and Flask apps create your classes by inheriting from framework classes
  • Magic methods let your objects work with +, ==, len() and sorting
Module 15The idea

Inheritance: "Is-A" Relationships

Vehiclewheels, fare_per_km, fare()Autowheels=3Bikewheels=2CabAC chargePremiumCab+ wifi, waterArrows point to the parent. Children inherit everything and add their own.

A Cab is a Vehicle. Instead of copying Vehicle's code into Cab, Cab inherits it and only adds what is different.

TermMeaning
Parent / base / super classthe general class (Vehicle)
Child / derived / sub classthe specific class (Cab)
Inheritget all attributes and methods
Overridereplace a method with a new version
RememberUse inheritance for is-a (a Manager is an Employee). Use an attribute for has-a (a Car has an Engine): that is called composition.
Module 15Inherit and extend

Child Classes with super()

main.py  PY
1class Employee:
2 def __init__(self, name, salary):
3 self.name = name
4 self.salary = salary
5
6 def monthly_pay(self):
7 return self.salary
8
9 def info(self):
10 return f"{self.name}: Rs {self.monthly_pay():,}"
11
12class Manager(Employee): # Manager inherits Employee
13 def __init__(self, name, salary, team_size):
14 super().__init__(name, salary) # reuse parent setup
15 self.team_size = team_size
16
17 def monthly_pay(self): # override
18 return self.salary + 2000 * self.team_size
19
20staff = [Employee("Rohit", 32000), Manager("Kavita", 55000, 6)]
21for s in staff:
22 print(s.info())
23print(isinstance(staff[1], Employee), isinstance(staff[0], Manager))
Terminal
C:\python-practice> python main.py
Rohit: Rs 32,000
Kavita: Rs 67,000
True False
  1. 1
    class Manager(Employee): Manager gets everything from Employee
  2. 2
    super().__init__(...) runs the parent's setup, then we add team_size
  3. 3
    Manager overrides monthly_pay; info() is inherited unchanged
  4. 4
    info() calls the right monthly_pay for each object
In real projectsPayroll systems in companies (and HR apps like Keka or Zoho People) handle many employee types this way.
Module 15Watch the lookup

Step Through: Which Method Runs?

Python searches for a method in the object's own class first, then its parent. This is called the method resolution order.

main.py  PY
1class Animal:
2 def speak(self):
3 return "..."
4 def intro(self):
5 return "I say " + self.speak()
6
7class Dog(Animal):
8 def speak(self):
9 return "Woof"
10
11class Cat(Animal):
12 pass
13
14print(Dog().intro())
15print(Cat().intro())
Output
Module 15Polymorphism

Polymorphism: One Interface, Many Forms

main.py  PY
1import math
2
3class Shape:
4 def area(self):
5 raise NotImplementedError
6
7class Rectangle(Shape):
8 def __init__(self, l, b): self.l, self.b = l, b
9 def area(self): return self.l * self.b
10
11class Circle(Shape):
12 def __init__(self, r): self.r = r
13 def area(self): return math.pi * self.r ** 2
14
15class Triangle(Shape):
16 def __init__(self, b, h): self.b, self.h = b, h
17 def area(self): return 0.5 * self.b * self.h
18
19plot = [Rectangle(40, 30), Circle(7), Triangle(20, 15)]
20for s in plot:
21 print(f"{type(s).__name__:<10} area = {s.area():8.2f} sq ft")
22print("Total:", round(sum(s.area() for s in plot), 2))
Terminal
C:\python-practice> python main.py
Rectangle area = 1200.00 sq ft
Circle area = 153.94 sq ft
Triangle area = 150.00 sq ft
Total: 1503.94

The loop calls s.area() without caring what shape it is. Each class answers in its own way. Adding a new shape needs no change to the loop.

In real projectsDrawing apps (Canva, CAD tools) and game engines treat all shapes or characters through one common method.
Module 15Encapsulation

Protecting Data: _private Names and @property

main.py  PY
1class Student:
2 def __init__(self, name, marks):
3 self.name = name
4 self._marks = 0
5 self.marks = marks # goes through the setter
6
7 @property
8 def marks(self):
9 return self._marks
10
11 @marks.setter
12 def marks(self, value):
13 if not 0 <= value <= 100:
14 raise ValueError(f"Invalid marks: {value}")
15 self._marks = value
16
17 @property
18 def grade(self): # computed, read-only
19 return "A" if self._marks >= 80 else "B" if self._marks >= 60 else "C"
20
21s = Student("Isha", 72)
22print(s.marks, s.grade)
23s.marks = 91
24print(s.marks, s.grade)
25try:
26 s.marks = 140
27except ValueError as e:
28 print("Blocked:", e)
Terminal
C:\python-practice> python main.py
72 B
91 A
Blocked: Invalid marks: 140
NameMeaning
namepublic: use freely
_marks"internal, please do not touch" (a convention)
__secretname-mangled: harder to reach from outside
@

@property

Looks like an attribute (s.marks) but runs a method, so you can validate or compute values.
In real projectsBank balance, stock quantity and exam marks should never be set to invalid values. Properties guard them.
Module 15Magic methods

Dunder (Magic) Methods: Make Objects Work with + == < len()

main.py  PY
1class Money:
2 def __init__(self, rupees):
3 self.rupees = round(rupees, 2)
4 def __add__(self, other):
5 return Money(self.rupees + other.rupees)
6 def __mul__(self, n):
7 return Money(self.rupees * n)
8 def __eq__(self, other):
9 return self.rupees == other.rupees
10 def __lt__(self, other):
11 return self.rupees < other.rupees
12 def __str__(self):
13 return f"Rs {self.rupees:,.2f}"
14
15fee, books = Money(4500), Money(1250.5)
16print(fee + books)
17print(books * 3)
18print(fee == Money(4500), books < fee)
19print(max([fee, books, Money(999)]))
Terminal
C:\python-practice> python main.py
Rs 5,750.50
Rs 3,751.50
True True
Rs 4,500.00
MethodLets you write
__add__a + b
__mul__a * 3
__eq__a == b
__lt__a < b, sorted(), max()
__len__len(a)
__str__print(a)
In real projectsThis is how pandas makes df["price"] * 1.18 work, and how dates support date2 - date1.
Module 15Abstract classes

Abstract Base Classes: Force Children to Implement Methods

main.py  PY
1from abc import ABC, abstractmethod
2
3class Payment(ABC):
4 def __init__(self, amount):
5 self.amount = amount
6 @abstractmethod
7 def pay(self): ...
8 def receipt(self):
9 print(f"{type(self).__name__}: Rs {self.amount} -> {self.pay()}")
10
11class UPI(Payment):
12 def pay(self): return "paid via UPI, ref 4521"
13
14class Card(Payment):
15 def pay(self): return "card charged, 2% fee Rs " + str(self.amount * 0.02)
16
17for p in [UPI(350), Card(1200)]:
18 p.receipt()
19
20try:
21 Payment(100)
22except TypeError as e:
23 print("Error:", e)
Terminal
C:\python-practice> python main.py
UPI: Rs 350 -> paid via UPI, ref 4521
Card: Rs 1200 -> card charged, 2% fee Rs 24.0
Error: Can't instantiate abstract class Payment without an implementation for abstract method 'pay'
  1. 1
    Payment(ABC) is a template that cannot be created directly
  2. 2
    @abstractmethod pay: every child must write its own pay()
  3. 3
    Shared code (receipt) lives in the parent
In real projectsFrameworks use abstract classes to define "plug-in" points: you write the child class, the framework calls it.
Module 15Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
[SMS] to 98350 11111: Fee due on 10th
[EMAIL] to parent@mail.com: PTM on Saturday
Your tasks
  1. Add a WhatsApp child class
  2. Make SMS.send() cut the text to 20 characters (override send, use super().send())
  3. Add __str__ to Notification
  4. Store 5 notifications in a list and send them all
PracticePractice 1 of 5

Employee and Manager Payroll

BeginnerScenario. A company in Ranchi pays staff monthly. Managers get a team allowance; interns get a fixed stipend with no PF.

Your tasks

  1. Base class Employee with gross(), pf() (12% of basic) and net()
  2. Manager adds Rs 2,500 per team member
  3. Intern overrides pf() to return 0
  4. Print a payslip table for all staff
🏢 Used in real projects: Payroll software (Tally, Keka, SAP) models employee types with inheritance and overridden pay rules.
practice_15_1.py  PY
1class Employee:
2 def __init__(self, name, basic):
3 self.name, self.basic = name, basic
4 def gross(self): return self.basic + self.basic * 0.4 # 40% HRA + DA
5 def pf(self): return self.basic * 0.12
6 def net(self): return self.gross() - self.pf()
7
8class Manager(Employee):
9 def __init__(self, name, basic, team):
10 super().__init__(name, basic)
11 self.team = team
12 def gross(self): return super().gross() + 2500 * self.team
13
14class Intern(Employee):
15 def gross(self): return self.basic
16 def pf(self): return 0
17
18staff = [Employee("Rohit", 30000), Manager("Kavita", 52000, 5), Intern("Sahil", 12000)]
19print(f"{'Name':<8}{'Role':<10}{'Gross':>10}{'PF':>8}{'Net':>10}")
20for s in staff:
21 print(f"{s.name:<8}{type(s).__name__:<10}{s.gross():>10,.0f}{s.pf():>8,.0f}{s.net():>10,.0f}")
PracticePractice 2 of 5

Ride Fare Calculator

EasyScenario. A ride-hailing app has Auto, Bike and Cab. Each has a base fare and per-km rate; Cab adds AC charges at night.

Your tasks

  1. Parent Vehicle with fare(km, night=False)
  2. Children set their own BASE and PER_KM class attributes
  3. Cab overrides fare to add 20% at night
  4. Compare all three for a 12 km ride
🏢 Used in real projects: Ola, Uber and Rapido compute fares per vehicle type with shared logic and special rules.
practice_15_2.py  PY
1class Vehicle:
2 BASE, PER_KM = 30, 10
3 def fare(self, km, night=False):
4 return self.BASE + self.PER_KM * km
5
6class Bike(Vehicle):
7 BASE, PER_KM = 20, 6
8
9class Auto(Vehicle):
10 BASE, PER_KM = 30, 11
11
12class Cab(Vehicle):
13 BASE, PER_KM = 60, 16
14 def fare(self, km, night=False):
15 f = super().fare(km)
16 return f * 1.2 if night else f
17
18for night in (False, True):
19 print("NIGHT" if night else "DAY", "fares for 12 km (Ranchi Station -> Kanke):")
20 for v in [Bike(), Auto(), Cab()]:
21 print(f" {type(v).__name__:<5} Rs {v.fare(12, night):>6.1f}")
PracticePractice 3 of 5

Shapes and a Paint Estimate

IntermediateScenario. A painter estimates paint for walls of different shapes. Each shape knows its own area and perimeter.

Your tasks

  1. Abstract-style base Shape with area() and perimeter()
  2. Children: Rectangle, Circle, Triangle (3 sides)
  3. 1 litre covers 100 sq ft; round up with math.ceil
  4. Sort shapes by area with sorted(..., key=...)
🏢 Used in real projects: Graphics software, CAD and game engines rely on polymorphic shape classes.
practice_15_3.py  PY
1import math
2class Shape:
3 def area(self): raise NotImplementedError
4 def perimeter(self): raise NotImplementedError
5 def __repr__(self): return f"{type(self).__name__}({self.area():.1f})"
6
7class Rectangle(Shape):
8 def __init__(self, l, b): self.l, self.b = l, b
9 def area(self): return self.l * self.b
10 def perimeter(self): return 2 * (self.l + self.b)
11
12class Circle(Shape):
13 def __init__(self, r): self.r = r
14 def area(self): return math.pi * self.r ** 2
15 def perimeter(self): return 2 * math.pi * self.r
16
17class Triangle(Shape):
18 def __init__(self, a, b, c): self.a, self.b, self.c = a, b, c
19 def perimeter(self): return self.a + self.b + self.c
20 def area(self):
21 s = self.perimeter() / 2
22 return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
23
24walls = [Rectangle(14, 10), Circle(4), Triangle(10, 10, 12)]
25for w in sorted(walls, key=lambda s: s.area()):
26 print(f"{w!r:<18} perimeter {w.perimeter():6.1f}")
27total = sum(w.area() for w in walls)
28print(f"Total {total:.1f} sq ft -> {math.ceil(total / 100)} litre(s) of paint")
PracticePractice 4 of 5

Money Class with Operators

ChallengingScenario. Make a Money class so that expense code reads naturally: total = rent + food.

Your tasks

  1. Store paise as an int to avoid float errors (Rs 10.50 = 1050 paise)
  2. Add __add__, __sub__, __mul__, __lt__, __eq__
  3. __str__ prints "Rs 1,234.50"
  4. Use sum() with a start value and sorted()
🏢 Used in real projects: Finance apps store money in paise (integers) and use operator methods so calculations stay exact.
practice_15_4.py  PY
1class Money:
2 def __init__(self, rupees=0, paise=None):
3 self.paise = paise if paise is not None else round(rupees * 100)
4 def __add__(self, o): return Money(paise=self.paise + o.paise)
5 def __sub__(self, o): return Money(paise=self.paise - o.paise)
6 def __mul__(self, n): return Money(paise=round(self.paise * n))
7 def __lt__(self, o): return self.paise < o.paise
8 def __eq__(self, o): return self.paise == o.paise
9 def __str__(self): return f"Rs {self.paise / 100:,.2f}"
10 __repr__ = __str__
11
12expenses = {"Rent": Money(8500), "Food": Money(4320.75), "Bus pass": Money(650), "Internet": Money(499.99)}
13total = sum(expenses.values(), Money(0))
14print("Total spent :", total)
15print("Left of 15k :", Money(15000) - total)
16print("Food x 12 :", expenses["Food"] * 12)
17print("Cheapest :", min(expenses.values()))
18print("Sorted :", sorted(expenses.values()))
19print(0.1 + 0.2, "vs", Money(0.1) + Money(0.2))
PracticePractice 5 of 5

Payment Methods with an Abstract Class

AdvancedScenario. A school fee portal accepts UPI, Card and Cash. Each method validates differently and charges a different fee.

Your tasks

  1. Abstract PaymentMethod with abstract validate() and fee()
  2. A shared process(amount) in the parent
  3. UPI: ID must contain "@", no fee; Card: 16 digits, 1.5% fee; Cash: only below Rs 10,000
  4. Process a list of payments and print receipts
🏢 Used in real projects: Payment gateways (Razorpay, Paytm) use a common interface with a separate class per payment method.
practice_15_5.py  PY
1from abc import ABC, abstractmethod
2
3class PaymentMethod(ABC):
4 @abstractmethod
5 def validate(self, amount): ...
6 @abstractmethod
7 def fee(self, amount): ...
8 def process(self, amount):
9 ok, why = self.validate(amount)
10 if not ok:
11 return print(f"FAILED {type(self).__name__:<5} Rs {amount:>6}: {why}")
12 total = amount + self.fee(amount)
13 print(f"PAID {type(self).__name__:<5} Rs {amount:>6} + fee {self.fee(amount):>6.2f} = {total:,.2f}")
14
15class UPI(PaymentMethod):
16 def __init__(self, upi_id): self.upi_id = upi_id
17 def validate(self, amount): return ("@" in self.upi_id, "invalid UPI ID")
18 def fee(self, amount): return 0
19
20class Card(PaymentMethod):
21 def __init__(self, number): self.number = number.replace(" ", "")
22 def validate(self, amount): return (len(self.number) == 16 and self.number.isdigit(), "card number must be 16 digits")
23 def fee(self, amount): return amount * 0.015
24
25class Cash(PaymentMethod):
26 def validate(self, amount): return (amount < 10000, "cash only below Rs 10,000")
27 def fee(self, amount): return 0
28
29for method, amt in [(UPI("aman@okaxis"), 4500), (Card("4111 1111 1111 1111"), 12000), (Cash(), 15000), (UPI("aman.okaxis"), 900)]:
30 method.process(amt)
Module 15Recap

Module 15 Recap & Quiz

What you learned

  • 1Inheritance: class Child(Parent) reuses parent code (is-a)
  • 2super().__init__(...) runs the parent's setup; override methods to change behaviour
  • 3Polymorphism: different classes, same method name; call it without checking the type
  • 4Encapsulation: _internal names and @property to validate data
  • 5Dunder methods (__add__, __eq__, __lt__, __str__) make objects work with operators
  • 6Abstract classes (ABC, @abstractmethod) force children to implement methods

Quick quiz

class Cab(Vehicle) means...

The class in brackets is the parent.

Which method makes a + b work?

__add__ is called for +.

A Car HAS an Engine. Best design?

has-a means composition: store it as an attribute.
MODULE 16

Pythonic Python: Comprehensions, Generators & Decorators

⏱ 3.5 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • List, dict and set comprehensions
  • map, filter, zip, enumerate, any and all
  • sorted() and max() with key functions
  • Iterators and generators (yield) for big data
  • Decorators: add features to functions

You will build

  • A one-line marks filter
  • A memory-friendly log file reader
  • A timer and a login-required decorator
[x for x]yield line@timer
Module 16Real-world use

Where Is This Used in Real Projects?

Pythonicwrite it the Python way🧹Data cleaningone-line filters🏆Leaderboardssorted with key📜Huge log filesgenerators⏱Speed checks@timer decorator🔒Web security@login_required⚡Caching@lru_cache

Real projects that use this

  • Data scientists clean thousands of rows with comprehensions in one readable line
  • Servers read gigabyte log files line by line with generators, using very little memory
  • Flask and Django protect pages with decorators like @login_required
  • "Pythonic" code is shorter, clearer and usually faster
Module 16Comprehensions

List Comprehensions: Build a List in One Line

main.py  PY
1marks = [78, 92, 34, 65, 88, 27, 71]
2
3# the long way
4passed = []
5for m in marks:
6 if m >= 40:
7 passed.append(m)
8
9# the Pythonic way
10passed2 = [m for m in marks if m >= 40]
11with_grace = [m + 5 for m in marks]
12labels = ["PASS" if m >= 40 else "FAIL" for m in marks]
13
14print(passed == passed2, passed2)
15print(with_grace)
16print(labels)
Terminal
C:\python-practice> python main.py
True [78, 92, 65, 88, 71]
[83, 97, 39, 70, 93, 32, 76]
['PASS', 'PASS', 'FAIL', 'PASS', 'PASS', 'FAIL', 'PASS']
[
m + 5what to put
in the new list
for m in marksloop
if m >= 40optional filter
]
RememberRead it as English: "m + 5 for each m in marks if m >= 40". Keep comprehensions short; if it needs two lines of thinking, use a normal loop.
Module 16More comprehensions

Dict and Set Comprehensions, and Nested Ones

main.py  PY
1names = ["aman", "priya", "rahul", "priya"]
2marks = [78, 92, 65, 92]
3
4report = {n.title(): m for n, m in zip(names, marks)}
5unique_initials = {n[0].upper() for n in names}
6squares = {n: n ** 2 for n in range(1, 6)}
7table = [[r * c for c in range(1, 4)] for r in range(1, 4)]
8flat = [x for row in table for x in row]
9
10print(report)
11print(unique_initials)
12print(squares)
13print(table)
14print(flat)
Terminal
C:\python-practice> python main.py
{'Aman': 78, 'Priya': 92, 'Rahul': 65}
{'R', 'A', 'P'}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
[[1, 2, 3], [2, 4, 6], [3, 6, 9]]
[1, 2, 3, 2, 4, 6, 3, 6, 9]
TypeSyntax
list[expr for x in seq]
dict{key: value for x in seq}
set{expr for x in seq}
generator(expr for x in seq)
RememberNotice "Priya" appears once in the dict: a repeated key just overwrites. The set removes duplicate initials.
Module 16Built-in helpers

map, filter, zip, enumerate, any and all

main.py  PY
1prices = ["120", "45", "300", "99"]
2nums = list(map(int, prices)) # convert all
3cheap = list(filter(lambda p: p < 100, nums)) # keep some
4print(nums, cheap)
5
6items = ["Pen", "Book", "Bag", "Box"]
7for i, (item, p) in enumerate(zip(items, nums), start=1):
8 print(i, item, p)
9
10print(any(p > 250 for p in nums)) # at least one?
11print(all(p > 40 for p in nums)) # every one?
12print(sum(p for p in nums if p >= 100))
Terminal
C:\python-practice> python main.py
[120, 45, 300, 99] [45, 99]
1 Pen 120
2 Book 45
3 Bag 300
4 Box 99
True
True
420
ToolDoes
map(f, seq)apply f to every item
filter(f, seq)keep items where f is True
zip(a, b)pair items side by side
enumerate(seq)add a counter
any(...)True if at least one is True
all(...)True if every one is True
RememberComprehensions usually read better than map/filter, but you will see both in real code.
Module 16Sorting by key

sorted(), min() and max() with a key Function

main.py  PY
1students = [
2 {"name": "Aman", "marks": 78, "age": 15},
3 {"name": "Priya", "marks": 92, "age": 14},
4 {"name": "Rahul", "marks": 78, "age": 16},
5 {"name": "Sneha", "marks": 88, "age": 15},
6]
7by_marks = sorted(students, key=lambda s: s["marks"], reverse=True)
8print([s["name"] for s in by_marks])
9
10# marks high to low, then name A-Z for ties
11ranked = sorted(students, key=lambda s: (-s["marks"], s["name"]))
12print([s["name"] for s in ranked])
13
14youngest = min(students, key=lambda s: s["age"])
15print("Youngest:", youngest["name"])
16words = ["kiwi", "banana", "fig", "mango"]
17print(sorted(words, key=len))
Terminal
C:\python-practice> python main.py
['Priya', 'Sneha', 'Aman', 'Rahul']
['Priya', 'Sneha', 'Aman', 'Rahul']
Youngest: Priya
['fig', 'kiwi', 'mango', 'banana']
k

key=function

Python calls the key function on each item and sorts by the answer. The items themselves are not changed.
(a, b)

Tie-breaking

Return a tuple: (-marks, name) sorts by marks descending, then name.
In real projectsLeaderboards, price "low to high" filters and "sort by date" buttons are all sorted(..., key=...).
Module 16Iterators

Iterators: How a for Loop Really Works

Python Shell (IDLE / terminal)
# Python interactive shell. Press Next to type each line.
0 / 6
  1. 1
    for x in cities secretly calls iter(cities)
  2. 2
    Then it calls next() again and again
  3. 3
    When StopIteration is raised, the loop ends quietly
RememberFiles, ranges, dicts, zip and map are all iterable. Some (files, zip, map) are lazy: they produce items only when asked, which saves memory.
Module 16Generators

Generators: Functions That yield Values One at a Time

Unlike return, yield pauses the function and keeps its variables. Next time, it continues where it stopped.

main.py  PY
1def countdown(n):
2 while n > 0:
3 yield n
4 n -= 1
5
6for x in countdown(3):
7 print("Got", x)
8print("Lift off!")
Output
Module 16Why generators

Generators Save Memory: Reading a Big Log File

main.py  PY
1def read_errors(path):
2 with open(path, encoding="utf-8") as f:
3 for line in f: # one line at a time
4 if " ERROR " in line:
5 yield line.strip()
6
7for e in read_errors("server.log"):
8 print(e)
9
10import sys
11big_list = [n * n for n in range(1_000_000)]
12big_gen = (n * n for n in range(1_000_000))
13print("List :", sys.getsizeof(big_list) // 1024, "KB")
14print("Generator:", sys.getsizeof(big_gen), "bytes")
15print("Sum via generator:", sum(big_gen))
Terminal
C:\python-practice> python main.py
2026-10-08 09:03:02 ERROR payment timeout order 1043
2026-10-08 09:05:30 ERROR db connection lost
2026-10-08 09:08:20 ERROR payment timeout order 1051
List : 8250 KB
Generator: 200 bytes
Sum via generator: 333332833333500000
📄 server.log (file on disk)TXT
12026-10-08 09:00:05 INFO server started
22026-10-08 09:01:12 INFO user aman logged in
32026-10-08 09:02:40 WARN slow response 2.1s
42026-10-08 09:03:02 ERROR payment timeout order 1043
52026-10-08 09:04:55 INFO user priya logged in
62026-10-08 09:05:30 ERROR db connection lost
72026-10-08 09:06:10 INFO db reconnected
82026-10-08 09:07:45 WARN disk 85% full
92026-10-08 09:08:20 ERROR payment timeout order 1051
102026-10-08 09:09:59 INFO backup complete
In real projectsA generator gives one item at a time, so a 10 GB log file can be processed with almost no memory. This is how data pipelines work.
Module 16Decorators

Decorators: Wrap a Function to Add a Feature

make_bill()your original function@timer (wrapper)start clock -> call original -> stop clockcallresult + time
  1. 1
    Functions are objects: you can pass them to other functions
  2. 2
    A decorator takes a function and returns a new wrapped function
  3. 3
    @timer above def is short for make_bill = timer(make_bill)
decorator.py  PY
1import time
2
3def timer(func):
4 def wrapper(*args, **kwargs):
5 start = time.perf_counter()
6 result = func(*args, **kwargs)
7 ms = (time.perf_counter() - start) * 1000
8 print(f"{func.__name__} took {ms:.0f} ms")
9 return result
10 return wrapper
11
12@timer
13def make_bill(n):
14 time.sleep(0.12) # pretend to do work
15 return n * 18
16
17print("Bill:", make_bill(250))
Terminal
C:\python-practice> python decorator.py
make_bill took 120 ms
Bill: 4500
Module 16Real decorators

Decorators in Real Apps: Login Checks and Caching

main.py  PY
1from functools import wraps, lru_cache
2
3current_user = {"name": "aman", "logged_in": False}
4
5def login_required(func):
6 @wraps(func)
7 def wrapper(*args, **kwargs):
8 if not current_user["logged_in"]:
9 print(f"Blocked {func.__name__}(): please log in")
10 return None
11 return func(*args, **kwargs)
12 return wrapper
13
14@login_required
15def view_marks(roll):
16 print(f"Marks for roll {roll}: 92")
17
18view_marks(17)
19current_user["logged_in"] = True
20view_marks(17)
21
22@lru_cache(maxsize=None)
23def fib(n):
24 return n if n < 2 else fib(n - 1) + fib(n - 2)
25print("fib(80) =", fib(80))
Terminal
C:\python-practice> python main.py
Blocked view_marks(): please log in
Marks for roll 17: 92
fib(80) = 23416728348467685
🔒

@login_required

One line protects any function (or web page). Flask-Login and Django provide exactly this decorator.
⚡

@lru_cache

Remembers results. Without it, fib(80) would take years; with it, it is instant.
Remember@wraps(func) keeps the original function's name and docstring on the wrapper.
Module 16Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
Passed: ['Aman', 'Priya', 'Sneha']
With grace marks: {'Aman': 83, 'Priya': 97, 'Rahul': 39, 'Sneha': 93, 'Kiran': 32}
1 Priya 92
2 Sneha 88
3 Aman 78
4 Rahul 34
5 Kiran 27
[0, 2, 4, 6, 8, 10]
Your tasks
  1. Make a list of names in CAPITALS using a comprehension
  2. Use any() to check if anyone scored above 90
  3. Write a generator squares(n) and print the first 5
  4. Write a decorator @shout that prints the function name before calling it
PracticePractice 1 of 5

Marks Filtering with Comprehensions

BeginnerScenario. Clean and analyse raw marks data in a few readable lines.

Your tasks

  1. Convert the raw strings to ints, skipping blanks
  2. Make a dict of name -> marks
  3. List the names that need a retest (below 40)
  4. Make a set of grades used
🏢 Used in real projects: Data cleaning in pandas and plain Python (analytics, ML preprocessing) is mostly filtering and transforming lists and dicts.
practice_16_1.py  PY
1raw = ["Aman:78", "Priya:92", "Rahul:", "Sneha:88", "Kiran:35", "Dev:64", "Ira:39"]
2
3pairs = [r.split(":") for r in raw]
4marks = {n: int(m) for n, m in pairs if m}
5retest = [n for n, m in marks.items() if m < 40]
6grade = lambda m: "A" if m >= 80 else "B" if m >= 60 else "C" if m >= 40 else "F"
7grades = {grade(m) for m in marks.values()}
8
9print("Marks :", marks)
10print("Retest:", retest)
11print("Grades used:", sorted(grades))
12print("Missing data:", [n for n, m in pairs if not m])
PracticePractice 2 of 5

Inter-School Quiz Leaderboard

EasyScenario. Scores from 3 rounds are stored per school. Rank schools by total, then by the last-round score for ties.

Your tasks

  1. Use a dict comprehension to total the rounds
  2. Sort with a tuple key (-total, -last_round)
  3. Use enumerate for ranks
  4. Use zip to show each school's round scores
🏢 Used in real projects: Sports scoreboards, exam merit lists and e-sports rankings use sorting with tie-breaker keys.
practice_16_2.py  PY
1scores = {
2 "DPS Ranchi": [18, 22, 25],
3 "KV Hinoo": [20, 24, 21],
4 "St. Xavier's": [25, 15, 25],
5 "DAV Bariatu": [19, 21, 22],
6}
7totals = {s: sum(r) for s, r in scores.items()}
8ranked = sorted(scores, key=lambda s: (-totals[s], -scores[s][-1]))
9
10print(f"{'#':<3}{'School':<14}{'R1':>4}{'R2':>4}{'R3':>4}{'Total':>7}")
11for rank, school in enumerate(ranked, 1):
12 r1, r2, r3 = scores[school]
13 print(f"{rank:<3}{school:<14}{r1:>4}{r2:>4}{r3:>4}{totals[school]:>7}")
14print("Perfect round by:", [s for s, r in scores.items() if any(x == 25 for x in r)])
PracticePractice 3 of 5

Log File Analyser with a Generator

IntermediateScenario. A server writes thousands of log lines. Count each level and list the payment errors without loading the whole file into memory.

Your tasks

  1. Write a generator parse(path) that yields (time, level, message) tuples
  2. Count levels with a dict
  3. Collect payment timeouts with a comprehension
  4. Print a summary
🏢 Used in real projects: Monitoring tools (Grafana, ELK, CloudWatch) stream and parse logs line by line exactly like this.
practice_16_3.py  PY
1def parse(path):
2 with open(path, encoding="utf-8") as f:
3 for line in f:
4 date, time, level, msg = line.strip().split(" ", 3)
5 yield time, level, msg
6
7counts = {}
8for _, level, _ in parse("server.log"):
9 counts[level] = counts.get(level, 0) + 1
10
11payment = [f"{t} {m}" for t, lvl, m in parse("server.log") if "payment" in m]
12
13print("Lines by level:", counts)
14print("Payment problems:")
15for p in payment:
16 print(" ", p)
PracticePractice 4 of 5

A @timer Decorator for Slow Code

ChallengingScenario. Compare two ways of checking for duplicate roll numbers and time them with your own decorator.

Your tasks

  1. Write @timer that prints the function name and time in ms
  2. Version A: check duplicates with a list and nested loop
  3. Version B: use a set
  4. Run both on 3,000 roll numbers and compare
🏢 Used in real projects: Performance monitoring tools wrap functions with timing decorators to find slow parts of an app.
practice_16_4.py  PY
1import time
2from functools import wraps
3
4def timer(func):
5 @wraps(func)
6 def wrapper(*args):
7 t = time.perf_counter()
8 result = func(*args)
9 print(f"{func.__name__:<14} {(time.perf_counter() - t) * 1000:8.2f} ms -> {result}")
10 return result
11 return wrapper
12
13@timer
14def has_dup_list(rolls):
15 seen = []
16 for r in rolls:
17 if r in seen:
18 return True
19 seen.append(r)
20 return False
21
22@timer
23def has_dup_set(rolls):
24 return len(set(rolls)) != len(rolls)
25
26rolls = list(range(3000)) + [2999]
27has_dup_list(rolls)
28has_dup_set(rolls)
PracticePractice 5 of 5

login_required and role_required Decorators

AdvancedScenario. A school portal has pages for students and teachers. Protect them with decorators instead of repeating if-checks everywhere.

Your tasks

  1. @login_required blocks if no user is logged in
  2. @role_required("teacher") is a decorator with an argument
  3. Apply them to view_marks and edit_marks
  4. Test as nobody, as a student and as a teacher
🏢 Used in real projects: Flask (@login_required), Django (@permission_required) and FastAPI use decorators to secure web pages and APIs.
practice_16_5.py  PY
1from functools import wraps
2session = {"user": None, "role": None}
3
4def login_required(f):
5 @wraps(f)
6 def w(*a):
7 if not session["user"]:
8 return print(f" {f.__name__}: 401 please log in")
9 return f(*a)
10 return w
11
12def role_required(role):
13 def deco(f):
14 @wraps(f)
15 def w(*a):
16 if session["role"] != role:
17 return print(f" {f.__name__}: 403 {role}s only")
18 return f(*a)
19 return w
20 return deco
21
22@login_required
23def view_marks(roll):
24 print(f" view_marks: roll {roll} has 92")
25
26@login_required
27@role_required("teacher")
28def edit_marks(roll, m):
29 print(f" edit_marks: roll {roll} set to {m}")
30
31for user, role in [(None, None), ("aman", "student"), ("mrs_kujur", "teacher")]:
32 session.update(user=user, role=role)
33 print(f"As {user or 'guest'}:")
34 view_marks(17)
35 edit_marks(17, 95)
Module 16Recap

Module 16 Recap & Quiz

What you learned

  • 1[expr for x in seq if cond] builds lists; also dict and set comprehensions
  • 2map, filter, zip, enumerate, any, all are handy built-ins
  • 3sorted(data, key=lambda x: ...); tuples in the key break ties
  • 4Iterators produce items one by one with next()
  • 5Generators use yield: pause, resume, low memory
  • 6Decorators wrap functions: @timer, @login_required, @lru_cache

Quick quiz

[x * 2 for x in [1, 2, 3] if x > 1] is?

Filter keeps 2 and 3, then doubles them.

Which keyword makes a function a generator?

Any function containing yield is a generator.

@timer above def f means...

The decorator replaces f with its wrapped version.
PART 3 COMPLETE

Well done!

You finished Files, Errors & OOP: 5 modules and 25 practice examples.

Before moving on

  • Re-type every practice example yourself without looking
  • Save your work in D:\python-practice\part3
  • Try the quizzes again until you get all of them right
  • Change one thing in each example and predict the output before you run it
print("Hi") >>> _
🐍 Python LabReady
Edit the code, then press Run. input() opens a small box to type your answer.
Terminal