Save data in files, handle errors gracefully, and model real things with classes and objects.
Every code example has a ▶ Run button. Click ✎ Edit & Run, or press L, to open the Python Lab.
D:\python-practice).| Mode | Meaning | If file exists | If missing |
|---|---|---|---|
"r" | read (default) | reads it | FileNotFoundError |
"w" | write | erases it! | creates it |
"a" | append | adds at the end | creates it |
"x" | create only | error | creates it |
"w" wipes the old content. Use "a" when you want to keep adding.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")56print("notice.txt saved!")
1BRAINBIT INFOTECH PVT LTD, RANCHI2Python batch starts on 15 October.3Timing: 10 AM to 12 PM
with open(..., "w") creates (or empties) the filef.write(text) writes text; add \n yourself for new lineswith block ends, Python closes the file automaticallyencoding="utf-8" so Hindi text and symbols like ₹ save correctly on Windows.1with open("cities.txt", encoding="utf-8") as f:2 content = f.read()3print(repr(content[:20]))45with open("cities.txt", encoding="utf-8") as f:6 for number, line in enumerate(f, start=1):7 print(number, line.strip())89with open("cities.txt", encoding="utf-8") as f:10 cities = f.read().splitlines()11print(cities)
| Way | Gives |
|---|---|
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 |
\n. Use line.strip() to remove it before comparing or printing.1Ranchi2Jamshedpur3Dhanbad4Bokaro5Deoghar
1from datetime import datetime23def 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")78log("Teacher logged in")9log("Attendance marked for 9A")10log("Report downloaded")11print(open("activity.log").read())
log() call adds one line at the end. Old lines stay.| Path | Type | Meaning |
|---|---|---|
"marks.csv" | relative | same folder as where you run the program |
"data/marks.csv" | relative | inside the data sub-folder |
r"D:\python-practice\marks.csv" | absolute | full path on Windows (r = raw string) |
"D:/python-practice/marks.csv" | absolute | forward slashes also work on Windows |
1from pathlib import Path23folder = Path("D:/python-practice/reports")4folder.mkdir(parents=True, exist_ok=True) # make folder if needed5file = folder / "march.csv" # join paths with /6print(file.name, file.suffix, file.exists())78import os9print(os.getcwd()) # current working folder10print(os.path.exists("marks.csv"))
pathlib is the modern, cross-platform way to handle paths. You will use it more in Module 17.1import csv23rows = [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")
1roll,name,status21,Aman,P32,Priya,P43,Rahul,A
newline="" when writing CSV on Windows, otherwise you get blank lines between rows.1import csv23with 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}")
1roll,name,maths,science,english21,Aman,78,85,7232,Priya,95,91,8843,Rahul,65,58,7054,Sneha,88,92,7965,Kiran,45,52,61
DictReader uses the first line as keysrow["maths"]int()1import json23settings = {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)1213with open("settings.json", encoding="utf-8") as f:14 loaded = json.load(f)15print(loaded["theme"], loaded["subjects"][0], type(loaded))
1{2 "user": "priya",3 "theme": "dark",4 "font_size": 16,5 "subjects": [6 "Maths",7 "Physics"8 ],9 "notifications": true10}
| Function | Does |
|---|---|
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 |
True becomes true in JSON.diary.txt with a date header1== 07 Oct 2026 ==2Started Module 12.3== 08 Oct 2026 ==4Learned file handling in Python today!
1from datetime import date23entry = 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")67with open("diary.txt", encoding="utf-8") as f:8 text = f.read()9print(text)10print("Entries so far:", text.count("== "))
[date, roll, name, status] with csv.writer1date,roll,name,status208-10-2026,1,Aman,P308-10-2026,2,Priya,P408-10-2026,3,Rahul,A508-10-2026,4,Sneha,P608-10-2026,5,Kiran,P
1import csv2students = [(1, "Aman"), (2, "Priya"), (3, "Rahul"), (4, "Sneha"), (5, "Kiran")]3day = "08-10-2026"4present = 05with 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}")
marks.csv, calculate each student's total and grade, and write a new result.csv.csv.DictReaderresult.csv with csv.DictWriter1name,total,percent,grade2Aman,235,78.3,B3Priya,274,91.3,A4Rahul,193,64.3,B5Sneha,259,86.3,A6Kiran,158,52.7,C
1import csv23def grade(p):4 return "A" if p >= 80 else "B" if p >= 60 else "C" if p >= 40 else "F"56results = []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)})1213with 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)1718top = max(results, key=lambda r: r["total"])19print(f"{len(results)} results written. Topper: {top['name']} ({top['percent']}%)")
os.path.exists to check for config.jsonjson.load, or use defaultsindent=2 and print it1{2 "language": "English",3 "sound": true,4 "difficulty": "hard",5 "high_score": 856}
1import json, os23DEFAULTS = {"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")1112config["difficulty"] = input("Difficulty (easy/medium/hard): ")13config["high_score"] = max(config["high_score"], 85)1415with open("config.json", "w") as f:16 json.dump(config, f, indent=2)17print("Saved:", config)
load() and save() functions so contacts survive after the program closes.load() returns a dict from contacts.json (or {} if missing)save(book) writes it back1{2 "Aman": "98350 11111",3 "Priya": "70040 22222",4 "Sneha": "99310 44444"5}
1import json, os2FILE = "contacts.json"34def load():5 if not os.path.exists(FILE):6 return {}7 with open(FILE, encoding="utf-8") as f:8 return json.load(f)910def save(book):11 with open(FILE, "w", encoding="utf-8") as f:12 json.dump(book, f, indent=2, sort_keys=True)1314book = 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
with open(name, mode, encoding="utf-8") as f: (auto-close)"r" read, "w" write (erases!), "a" appendread(), or loop for line in f + strip()csv.writer / DictReader / DictWriter; use newline="" when writingjson.dump / json.load for dicts and listsWhich mode adds to the end without erasing?
What does with do for files?
Values read by csv.DictReader are...
| Exception | Example that causes it |
|---|---|
ValueError | int("abc") |
TypeError | "5" + 5 |
ZeroDivisionError | 10 / 0 |
IndexError | [1, 2][5] |
KeyError | {"a": 1}["b"] |
NameError | using a variable never created |
FileNotFoundError | open("missing.txt") |
AttributeError | "hi".push() |
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.")67print("Program continues normally.")
ValueError happens, it jumps to except ValueErrorexcept ValueError), not everything. A bare except: hides real bugs.1try:2 n = int(input("Number: "))3 result = 100 / n4except 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.")
| Block | Runs when |
|---|---|
try | always (first) |
except X | error X happened |
else | no error happened |
finally | always, error or not (cleanup) |
1data = {"Aman": [78, 85], "Priya": []}23for 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)
e holds the error object. Print it or save it in a log file.Exception last.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 age78for 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)
raises an exception with a clear messagetry / except1class InsufficientBalanceError(Exception):2 """Raised when a withdrawal is more than the balance."""34def 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 - amount1011balance = 500012for 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)
InsufficientFunds, CardExpired, LimitExceeded.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 loop7 except ValueError as e:8 print(f"Invalid ({e}). Try again.")910print("Saved marks:", marks)
while True keeps askingValueErrorbreak1def percent(marks, total):2 return marks / total * 10034def report(student):5 p = percent(student["marks"], student["total"])6 print(student["name"], round(p, 1))78report({"name": "Aman", "marks": 412, "total": 500})9report({"name": "Riya", "marks": 380, "total": 0})
ZeroDivisionError: division by zeropercent, line 2total = 0print() values, VS Code breakpoints (click left of a line number, press F5), and assert total > 0, "total is zero" to catch bad data early.finally to safe_divide that prints "done"KeyErrorcheck_pin(pin) that raises ValueError if it is not 4 digitsget_int() function that keeps asking until the user types a valid whole number within limits.while True + try / except ValueError1def get_int(prompt, low, high):2 while True:3 try:4 value = int(input(prompt))5 if low <= value <= high:6 return value7 print(f" Please enter a number from {low} to {high}.")8 except ValueError:9 print(" That is not a whole number.")1011class_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}")
1while True:2 op = input("Operator (+ - * /) or q: ")3 if op == "q":4 break5 try:6 a = float(input(" First number: "))7 b = float(input(" Second number: "))8 if op == "+": r = a + b9 elif op == "-": r = a - b10 elif op == "*": r = a * b11 elif op == "/": r = a / b12 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!")
students.txt. If it is missing, it creates it with sample data instead of crashing.FileNotFoundError create it with defaultselse to report how many names were loadedfinally to print "Startup complete"1Aman2Priya3Rahul
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 names1516print(load_students("students.txt"))17print(load_students("students.txt"))
InsufficientBalance and DailyLimitExceeded exception classeswithdraw(amount) that raises them1class InsufficientBalance(Exception): pass2class DailyLimitExceeded(Exception): pass34balance, withdrawn_today, LIMIT = 12000, 0, 100005errors = []67def withdraw(amount):8 global balance, withdrawn_today9 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 -= amount14 withdrawn_today += amount1516for 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 ")
assert to double-check the total count1raw = ["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] = marks10 except ValueError as e:11 bad.append((line, str(e)))1213assert 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}")
else runs when there is no error; finally always runsraise your own errors with clear messages; make custom ones with class X(Exception)while True + try / except + breakint("12a") raises...
Which block always runs?
Where should you start reading a traceback?
| Word | Meaning | Example |
|---|---|---|
| Class | blueprint / design | Student |
| Object (instance) | one thing made from it | Aman's record |
| Attribute | data the object has | name, marks |
| Method | action the object can do | report() |
"ranchi".upper() calls the upper method of a str object!1class Student:2 def __init__(self, name, cls, marks):3 self.name = name # attributes4 self.cls = cls5 self.marks = marks67 def report(self): # a method8 print(f"{self.name} (class {self.cls}) scored {self.marks}")910 def is_pass(self):11 return self.marks >= 331213a = Student("Aman", "9A", 78) # create objects14b = Student("Riya", "10B", 28)15a.report()16b.report()17print(a.name, a.is_pass(), "|", b.name, b.is_pass())
class Student: starts the blueprint (class names use CapitalWords)__init__ runs automatically when an object is created: it sets up the attributesself means "this particular object"a.report()self yourself. a.report() is really Student.report(a).Each object keeps its own attributes. Changing x never changes y.
1class Account:2 def __init__(self, owner, balance=0):3 self.owner = owner4 self.balance = balance56 def deposit(self, amount):7 self.balance += amount89x = Account("Neha", 1000)10y = Account("Ravi")11x.deposit(500)12y.deposit(200)13print(x.owner, x.balance, "|", y.owner, y.balance)
1class BankAccount:2 def __init__(self, owner, balance=0):3 self.owner = owner4 self.balance = balance5 self.history = []67 def deposit(self, amount):8 self.balance += amount9 self.history.append(f"+{amount}")1011 def withdraw(self, amount):12 if amount > self.balance:13 print(f" {self.owner}: insufficient balance")14 return False15 self.balance -= amount16 self.history.append(f"-{amount}")17 return True1819acc = BankAccount("Neha Oraon", 2000)20acc.deposit(1500)21acc.withdraw(5000)22acc.withdraw(800)23print(acc.owner, "balance:", acc.balance)24print("History:", acc.history)
1class Product:2 def __init__(self, name, price, stock):3 self.name, self.price, self.stock = name, price, stock45 def __str__(self):6 return f"{self.name} - Rs {self.price} ({self.stock} in stock)"78 def __repr__(self):9 return f"Product({self.name!r}, {self.price}, {self.stock})"1011p = Product("School Bag", 899, 12)12print(p) # uses __str__13print([p]) # lists use __repr__14print(str(p).upper())
print() and str().__str__, printing shows something like <__main__.Product object at 0x...>.1class Student:2 school = "St. Xavier's, Ranchi" # class attribute: shared3 count = 045 def __init__(self, name):6 self.name = name # instance attribute: own7 Student.count += 18 self.roll = Student.count910s1 = Student("Aman")11s2 = Student("Priya")12print(s1.name, s1.roll, s1.school)13print(s2.name, s2.roll, s2.school)14print("Total students:", Student.count)
| Class attribute | Instance attribute | |
|---|---|---|
| Defined | inside class, outside methods | with self.x = ... |
| Shared? | one value for all objects | each object has its own |
| Example | school name, GST rate, counter | name, roll, marks |
1class Student:2 def __init__(self, name, marks):3 self.name, self.marks = name, marks4 def __repr__(self):5 return f"{self.name}({self.marks})"67class_9a = [Student("Aman", 78), Student("Priya", 92), Student("Rahul", 65), Student("Sneha", 88)]89ranked = 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))
key=lambda s: s.marksstatus() that prints "Low battery!" below 20%__str__ so print(phone) looks nicemade_in = "India"__init__(name, cls) with an empty marks dictadd_marks(subject, m) methodpercentage() and grade() methods__str__ returns a one-line summary1class Student:2 def __init__(self, name, cls):3 self.name, self.cls = name, cls4 self.marks = {}56 def add_marks(self, subject, m):7 self.marks[subject] = m89 def percentage(self):10 return sum(self.marks.values()) / len(self.marks)1112 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"1516 def __str__(self):17 return f"{self.name} ({self.cls}): {self.percentage():.1f}% grade {self.grade()}"1819s = 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))
withdraw refuses if the balance would go below 500add_interest(rate) adds yearly intereststatement() prints the last 5 transactions1class SavingsAccount:2 MIN_BALANCE = 50034 def __init__(self, owner, acc_no, balance=0):5 self.owner, self.acc_no, self.balance = owner, acc_no, balance6 self.tx = []78 def deposit(self, amt):9 self.balance += amt10 self.tx.append(("CR", amt))1112 def withdraw(self, amt):13 if self.balance - amt < self.MIN_BALANCE:14 print(f"Declined: must keep Rs {self.MIN_BALANCE}")15 return16 self.balance -= amt17 self.tx.append(("DR", amt))1819 def add_interest(self, rate=3.5):20 i = round(self.balance * rate / 100, 2)21 self.deposit(i)2223 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}")2829a = SavingsAccount("Ravi Mahato", "XX4521", 1000)30a.deposit(5000)31a.withdraw(5800)32a.withdraw(2500)33a.add_interest()34a.statement()
Product(name, price) with __repr__Cart with add(product, qty) and remove(name)total() with 5% off above Rs 2000show() prints the cart1class Product:2 def __init__(self, name, price):3 self.name, self.price = name, price4 def __repr__(self):5 return f"{self.name}@{self.price}"67class 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 t18 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}")2223bag, 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()
use(app, minutes) with drain rates in a class dictcharge(minutes) adds 1% per minute up to 1001class Phone:2 DRAIN = {"YouTube": 0.5, "WhatsApp": 0.2, "Game": 0.8}34 def __init__(self, model, battery=60):5 self.model, self.battery, self.is_on = model, battery, True67 def use(self, app, minutes):8 if not self.is_on:9 print(f"{self.model} is off. Charge it first!")10 return11 self.battery = max(0, self.battery - self.DRAIN[app] * minutes)12 if self.battery == 0:13 self.is_on = False14 self.status(f"{app} {minutes} min")1516 def charge(self, minutes):17 self.battery = min(100, self.battery + minutes)18 self.is_on = True19 self.status(f"charged {minutes} min")2021 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'}")2425p = Phone("Galaxy M14")26p.use("YouTube", 40); p.use("Game", 60); p.use("WhatsApp", 30); p.charge(45); p.use("Game", 30)
Book(title, author) with is_issuedMember(name) with a list of booksLibrary with issue(member, title) and return_book()1class Book:2 def __init__(self, title, author):3 self.title, self.author, self.is_issued = title, author, False45class Member:6 LIMIT = 27 def __init__(self, name):8 self.name, self.books = name, []910class 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}")2526lib = 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")
__init__(self, ...) sets up attributes when an object is createdself = this object; attributes are self.nameacc.deposit(500)__str__ for friendly printing, __repr__ for developersWhat runs automatically when you create an object?
In a.report(), what is self inside report?
A class attribute is...
pay() method that works differently for UPI, card and wallet (polymorphism)+, ==, len() and sortingA Cab is a Vehicle. Instead of copying Vehicle's code into Cab, Cab inherits it and only adds what is different.
| Term | Meaning |
|---|---|
| Parent / base / super class | the general class (Vehicle) |
| Child / derived / sub class | the specific class (Cab) |
| Inherit | get all attributes and methods |
| Override | replace a method with a new version |
1class Employee:2 def __init__(self, name, salary):3 self.name = name4 self.salary = salary56 def monthly_pay(self):7 return self.salary89 def info(self):10 return f"{self.name}: Rs {self.monthly_pay():,}"1112class Manager(Employee): # Manager inherits Employee13 def __init__(self, name, salary, team_size):14 super().__init__(name, salary) # reuse parent setup15 self.team_size = team_size1617 def monthly_pay(self): # override18 return self.salary + 2000 * self.team_size1920staff = [Employee("Rohit", 32000), Manager("Kavita", 55000, 6)]21for s in staff:22 print(s.info())23print(isinstance(staff[1], Employee), isinstance(staff[0], Manager))
class Manager(Employee): Manager gets everything from Employeesuper().__init__(...) runs the parent's setup, then we add team_sizemonthly_pay; info() is inherited unchangedinfo() calls the right monthly_pay for each objectPython searches for a method in the object's own class first, then its parent. This is called the method resolution order.
1class Animal:2 def speak(self):3 return "..."4 def intro(self):5 return "I say " + self.speak()67class Dog(Animal):8 def speak(self):9 return "Woof"1011class Cat(Animal):12 pass1314print(Dog().intro())15print(Cat().intro())
1import math23class Shape:4 def area(self):5 raise NotImplementedError67class Rectangle(Shape):8 def __init__(self, l, b): self.l, self.b = l, b9 def area(self): return self.l * self.b1011class Circle(Shape):12 def __init__(self, r): self.r = r13 def area(self): return math.pi * self.r ** 21415class Triangle(Shape):16 def __init__(self, b, h): self.b, self.h = b, h17 def area(self): return 0.5 * self.b * self.h1819plot = [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))
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.
1class Student:2 def __init__(self, name, marks):3 self.name = name4 self._marks = 05 self.marks = marks # goes through the setter67 @property8 def marks(self):9 return self._marks1011 @marks.setter12 def marks(self, value):13 if not 0 <= value <= 100:14 raise ValueError(f"Invalid marks: {value}")15 self._marks = value1617 @property18 def grade(self): # computed, read-only19 return "A" if self._marks >= 80 else "B" if self._marks >= 60 else "C"2021s = Student("Isha", 72)22print(s.marks, s.grade)23s.marks = 9124print(s.marks, s.grade)25try:26 s.marks = 14027except ValueError as e:28 print("Blocked:", e)
| Name | Meaning |
|---|---|
name | public: use freely |
_marks | "internal, please do not touch" (a convention) |
__secret | name-mangled: harder to reach from outside |
s.marks) but runs a method, so you can validate or compute values.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.rupees10 def __lt__(self, other):11 return self.rupees < other.rupees12 def __str__(self):13 return f"Rs {self.rupees:,.2f}"1415fee, books = Money(4500), Money(1250.5)16print(fee + books)17print(books * 3)18print(fee == Money(4500), books < fee)19print(max([fee, books, Money(999)]))
| Method | Lets you write |
|---|---|
__add__ | a + b |
__mul__ | a * 3 |
__eq__ | a == b |
__lt__ | a < b, sorted(), max() |
__len__ | len(a) |
__str__ | print(a) |
df["price"] * 1.18 work, and how dates support date2 - date1.1from abc import ABC, abstractmethod23class Payment(ABC):4 def __init__(self, amount):5 self.amount = amount6 @abstractmethod7 def pay(self): ...8 def receipt(self):9 print(f"{type(self).__name__}: Rs {self.amount} -> {self.pay()}")1011class UPI(Payment):12 def pay(self): return "paid via UPI, ref 4521"1314class Card(Payment):15 def pay(self): return "card charged, 2% fee Rs " + str(self.amount * 0.02)1617for p in [UPI(350), Card(1200)]:18 p.receipt()1920try:21 Payment(100)22except TypeError as e:23 print("Error:", e)
Payment(ABC) is a template that cannot be created directly@abstractmethod pay: every child must write its own pay()receipt) lives in the parentWhatsApp child classSMS.send() cut the text to 20 characters (override send, use super().send())__str__ to NotificationEmployee with gross(), pf() (12% of basic) and net()Manager adds Rs 2,500 per team memberIntern overrides pf() to return 01class Employee:2 def __init__(self, name, basic):3 self.name, self.basic = name, basic4 def gross(self): return self.basic + self.basic * 0.4 # 40% HRA + DA5 def pf(self): return self.basic * 0.126 def net(self): return self.gross() - self.pf()78class Manager(Employee):9 def __init__(self, name, basic, team):10 super().__init__(name, basic)11 self.team = team12 def gross(self): return super().gross() + 2500 * self.team1314class Intern(Employee):15 def gross(self): return self.basic16 def pf(self): return 01718staff = [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}")
Vehicle with fare(km, night=False)BASE and PER_KM class attributesCab overrides fare to add 20% at night1class Vehicle:2 BASE, PER_KM = 30, 103 def fare(self, km, night=False):4 return self.BASE + self.PER_KM * km56class Bike(Vehicle):7 BASE, PER_KM = 20, 689class Auto(Vehicle):10 BASE, PER_KM = 30, 111112class Cab(Vehicle):13 BASE, PER_KM = 60, 1614 def fare(self, km, night=False):15 f = super().fare(km)16 return f * 1.2 if night else f1718for 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}")
Shape with area() and perimeter()math.ceilsorted(..., key=...)1import math2class Shape:3 def area(self): raise NotImplementedError4 def perimeter(self): raise NotImplementedError5 def __repr__(self): return f"{type(self).__name__}({self.area():.1f})"67class Rectangle(Shape):8 def __init__(self, l, b): self.l, self.b = l, b9 def area(self): return self.l * self.b10 def perimeter(self): return 2 * (self.l + self.b)1112class Circle(Shape):13 def __init__(self, r): self.r = r14 def area(self): return math.pi * self.r ** 215 def perimeter(self): return 2 * math.pi * self.r1617class Triangle(Shape):18 def __init__(self, a, b, c): self.a, self.b, self.c = a, b, c19 def perimeter(self): return self.a + self.b + self.c20 def area(self):21 s = self.perimeter() / 222 return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))2324walls = [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")
Money class so that expense code reads naturally: total = rent + food.__add__, __sub__, __mul__, __lt__, __eq____str__ prints "Rs 1,234.50"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.paise8 def __eq__(self, o): return self.paise == o.paise9 def __str__(self): return f"Rs {self.paise / 100:,.2f}"10 __repr__ = __str__1112expenses = {"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))
PaymentMethod with abstract validate() and fee()process(amount) in the parent1from abc import ABC, abstractmethod23class PaymentMethod(ABC):4 @abstractmethod5 def validate(self, amount): ...6 @abstractmethod7 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}")1415class UPI(PaymentMethod):16 def __init__(self, upi_id): self.upi_id = upi_id17 def validate(self, amount): return ("@" in self.upi_id, "invalid UPI ID")18 def fee(self, amount): return 01920class 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.0152425class Cash(PaymentMethod):26 def validate(self, amount): return (amount < 10000, "cash only below Rs 10,000")27 def fee(self, amount): return 02829for method, amt in [(UPI("aman@okaxis"), 4500), (Card("4111 1111 1111 1111"), 12000), (Cash(), 15000), (UPI("aman.okaxis"), 900)]:30 method.process(amt)
class Child(Parent) reuses parent code (is-a)super().__init__(...) runs the parent's setup; override methods to change behaviour_internal names and @property to validate data__add__, __eq__, __lt__, __str__) make objects work with operatorsABC, @abstractmethod) force children to implement methodsclass Cab(Vehicle) means...
Which method makes a + b work?
A Car HAS an Engine. Best design?
@login_required1marks = [78, 92, 34, 65, 88, 27, 71]23# the long way4passed = []5for m in marks:6 if m >= 40:7 passed.append(m)89# the Pythonic way10passed2 = [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]1314print(passed == passed2, passed2)15print(with_grace)16print(labels)
1names = ["aman", "priya", "rahul", "priya"]2marks = [78, 92, 65, 92]34report = {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]910print(report)11print(unique_initials)12print(squares)13print(table)14print(flat)
| Type | Syntax |
|---|---|
| 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) |
1prices = ["120", "45", "300", "99"]2nums = list(map(int, prices)) # convert all3cheap = list(filter(lambda p: p < 100, nums)) # keep some4print(nums, cheap)56items = ["Pen", "Book", "Bag", "Box"]7for i, (item, p) in enumerate(zip(items, nums), start=1):8 print(i, item, p)910print(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))
| Tool | Does |
|---|---|
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 |
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])910# marks high to low, then name A-Z for ties11ranked = sorted(students, key=lambda s: (-s["marks"], s["name"]))12print([s["name"] for s in ranked])1314youngest = min(students, key=lambda s: s["age"])15print("Youngest:", youngest["name"])16words = ["kiwi", "banana", "fig", "mango"]17print(sorted(words, key=len))
(-marks, name) sorts by marks descending, then name.sorted(..., key=...).for x in cities secretly calls iter(cities)next() again and againStopIteration is raised, the loop ends quietlyUnlike return, yield pauses the function and keeps its variables. Next time, it continues where it stopped.
1def countdown(n):2 while n > 0:3 yield n4 n -= 156for x in countdown(3):7 print("Got", x)8print("Lift off!")
1def read_errors(path):2 with open(path, encoding="utf-8") as f:3 for line in f: # one line at a time4 if " ERROR " in line:5 yield line.strip()67for e in read_errors("server.log"):8 print(e)910import sys11big_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))
12026-10-08 09:00:05 INFO server started22026-10-08 09:01:12 INFO user aman logged in32026-10-08 09:02:40 WARN slow response 2.1s42026-10-08 09:03:02 ERROR payment timeout order 104352026-10-08 09:04:55 INFO user priya logged in62026-10-08 09:05:30 ERROR db connection lost72026-10-08 09:06:10 INFO db reconnected82026-10-08 09:07:45 WARN disk 85% full92026-10-08 09:08:20 ERROR payment timeout order 1051102026-10-08 09:09:59 INFO backup complete
@timer above def is short for make_bill = timer(make_bill)1import time23def timer(func):4 def wrapper(*args, **kwargs):5 start = time.perf_counter()6 result = func(*args, **kwargs)7 ms = (time.perf_counter() - start) * 10008 print(f"{func.__name__} took {ms:.0f} ms")9 return result10 return wrapper1112@timer13def make_bill(n):14 time.sleep(0.12) # pretend to do work15 return n * 181617print("Bill:", make_bill(250))
1from functools import wraps, lru_cache23current_user = {"name": "aman", "logged_in": False}45def 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 None11 return func(*args, **kwargs)12 return wrapper1314@login_required15def view_marks(roll):16 print(f"Marks for roll {roll}: 92")1718view_marks(17)19current_user["logged_in"] = True20view_marks(17)2122@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))
fib(80) would take years; with it, it is instant.@wraps(func) keeps the original function's name and docstring on the wrapper.any() to check if anyone scored above 90squares(n) and print the first 5@shout that prints the function name before calling it1raw = ["Aman:78", "Priya:92", "Rahul:", "Sneha:88", "Kiran:35", "Dev:64", "Ira:39"]23pairs = [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()}89print("Marks :", marks)10print("Retest:", retest)11print("Grades used:", sorted(grades))12print("Missing data:", [n for n, m in pairs if not m])
(-total, -last_round)enumerate for rankszip to show each school's round scores1scores = {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]))910print(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)])
parse(path) that yields (time, level, message) tuples1def 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, msg67counts = {}8for _, level, _ in parse("server.log"):9 counts[level] = counts.get(level, 0) + 11011payment = [f"{t} {m}" for t, lvl, m in parse("server.log") if "payment" in m]1213print("Lines by level:", counts)14print("Payment problems:")15for p in payment:16 print(" ", p)
@timer that prints the function name and time in ms1import time2from functools import wraps34def 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 result11 return wrapper1213@timer14def has_dup_list(rolls):15 seen = []16 for r in rolls:17 if r in seen:18 return True19 seen.append(r)20 return False2122@timer23def has_dup_set(rolls):24 return len(set(rolls)) != len(rolls)2526rolls = list(range(3000)) + [2999]27has_dup_list(rolls)28has_dup_set(rolls)
@login_required blocks if no user is logged in@role_required("teacher") is a decorator with an argumentview_marks and edit_marks@login_required), Django (@permission_required) and FastAPI use decorators to secure web pages and APIs.1from functools import wraps2session = {"user": None, "role": None}34def 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 w1112def 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 w20 return deco2122@login_required23def view_marks(roll):24 print(f" view_marks: roll {roll} has 92")2526@login_required27@role_required("teacher")28def edit_marks(roll, m):29 print(f" edit_marks: roll {roll} set to {m}")3031for 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)
[expr for x in seq if cond] builds lists; also dict and set comprehensionsmap, filter, zip, enumerate, any, all are handy built-inssorted(data, key=lambda x: ...); tuples in the key break tiesnext()yield: pause, resume, low memory@timer, @login_required, @lru_cache[x * 2 for x in [1, 2, 3] if x > 1] is?
Which keyword makes a function a generator?
@timer above def f means...
You finished Files, Errors & OOP: 5 modules and 25 practice examples.
D:\python-practice\part3