Python Practical Course
Python Practical Course
A complete self-study workbook — read it, type every example, build every project.
BEFORE YOU BEGIN
Example 1 & 2 A basic and a practical example, each explained line by line with output.
Common Mistakes The errors almost every beginner hits, so you can skip the pain.
Exercise + Solution You try first; then check against a fully explained answer.
Setup in 3 minutes Install Python from [Link]/downloads (tick “Add Python to PATH” on Windows). Install the free VS
Code editor and its Python extension. Create a file ending in .py , type code, and press the ▶ Run button. To run from a
terminal, type python [Link] . That is the entire toolchain you need for the whole book.
CONTENTS
Table of Contents
Module 1 — Python Fundamentals
Variables • Data Types • Input/Output • Type Conversion
Module 2 — Operators
Arithmetic • Comparison • Logical • Assignment
Module 3 — Conditions
if • if/else • elif • nested if • Project: Student Grade Calculator
Module 4 — Loops
for • while • break • continue • Project: Number Analyzer
Module 5 — Strings
Indexing • Slicing • Methods • Formatting • Project: Password Strength Checker
Module 6 — Lists
CRUD • Methods • Nested Lists • Comprehensions • Project: Shopping Cart
Module 8 — Functions
Parameters • Arguments • Return • Scope • Recursion • Project: Calculator
Python Fundamentals
Every program, no matter how advanced, is built from four humble actions: store a value, know what kind of value it is, talk to
the user, and change a value from one kind to another. Master these four and you have the vocabulary for everything that
follows.
1.1 Variables
Concept
A variable is a name that points to a value stored in the computer's memory. Think of memory as a giant wall of numbered
lockers. A value lives inside a locker; the variable name is the sticky-note label you put on it so you never have to remember the
locker number. Variables exist so that humans can work with names ( price , username ) instead of raw memory addresses.
Why it is used: programs must remember things — a user's name, a running total, the result of a calculation. Where in real
projects: a shopping site stores cart_total ; a game stores player_health ; a bank app stores balance . Practically every line of
real code touches a variable.
Syntax
variable_name = value
Example 1 — Basic
1 name = "John"
2 print(name)
OUTPUT
John
Output explained: the quotes are gone — they were only instructions to Python, never part of the value. The user sees the text
content alone.
Example 2 — Practical
1 price = 100
2 quantity = 3
3 total = price * quantity
4 print("Total bill:", total)
Line 1–2: two variables hold whole numbers (integers). Each name points to its own value in memory.
Line 3: * multiplies. Python evaluates the right side first ( 100 * 3 = 300 ), then a brand-new variable total is created pointing to 300 .
Line 4: print receives two items separated by a comma; it prints them on one line with a space between, so we get a label and a number
together.
OUTPUT
Real-world use: this is the exact pattern behind every invoice, cart subtotal, and payroll calculation.
Common Mistakes • Writing 100 = price (assignment is always name = value, never the reverse).
• Forgetting quotes around text: name = John makes Python hunt for a variable called John and crash with a NameError .
• Using a name before assigning it. A variable must be given a value before it is read.
Practice Exercise Create variables for your first_name and last_name , then print Full name: <first> <last> .
Solution
1 first_name = "Ada"
2 last_name = "Lovelace"
3 print("Full name:", first_name, last_name)
Line 1–2 store two strings. Line 3 prints a label and both names; print inserts a space between each comma-separated item,
giving Full name: Ada Lovelace .
Where used: a form's age field is an int , a price is a float , a name is a str , an “is the user logged in?” flag is a bool , and a
“no value yet” placeholder is None .
Example 1 — Basic
1 age = 25
2 pi = 3.14
3 city = "Paris"
4 is_student = True
5 print(type(age), type(pi), type(city), type(is_student))
Lines 1–4: four variables, four different types. Python decides the type automatically from how the value is written (no quotes + no dot =
int; a dot = float; quotes = str; the words True/False = bool).
Line 5: type(x) is a built-in that reports the type of its argument. We print all four.
OUTPUT
Output explained: Python confirms each value's category. <class '...'> is just Python's way of naming a type.
Example 2 — Practical
1 product = "Laptop"
2 price = 899.99
3 in_stock = True
4 discount = None
5 print(product, "costs", price, "| available:", in_stock, "| discount:", discount)
Line 4: None is a real value meaning “nothing decided yet” — perfect for a discount that hasn't been set.
Line 5: mixing types in one print is fine because print turns everything into text for display.
OUTPUT
Laptop costs 899.99 | available: True | discount: None
Real-world use: this mirrors one row of a product catalog in any e-commerce backend.
Common Mistakes • Writing true / false (lowercase) — Python requires capital True / False .
• Quoting a number you intend to do math with: "5" + "5" gives "55" (text joined), not 10 .
• Confusing None with 0 or "" — None means “no value,” not “zero.”
Practice Exercise Create one variable of each type (str, int, float, bool) describing a movie, then print each value together with its
type.
Solution
1 title = "Inception"
2 year = 2010
3 rating = 8.8
4 is_classic = True
5 print(title, type(title))
6 print(year, type(year))
7 print(rating, type(rating))
8 print(is_classic, type(is_classic))
Each print shows a value next to type(...) , confirming Python inferred str , int , float , and bool respectively from how
each literal was written.
Critical rule: input() always returns a string, even if the user types digits. We convert it when we need a number (see 1.4).
Syntax
Example 1 — Basic
Line 1: input(...) prints the prompt, pauses, and waits for the user to type and press Enter. Whatever they type comes back as a string
and is stored in name .
Line 2: we greet them using the captured value.
Example 2 — Practical
Lines 1–2: input(...) hands back text like "8" ; int(...) wraps it and converts that text into the whole number 8 so arithmetic will
work. This “wrap and convert” pattern is everywhere.
Line 3: now that both are real integers, + adds them numerically.
Real-world use: any calculator, quiz, or data-entry tool reads typed input and converts it before computing.
Common Mistakes • Forgetting int() / float() around numeric input → "8" + "5" becomes "85" .
• Calling int(input(...)) when the user types letters → crashes with ValueError .
• Forgetting the space at the end of the prompt string, so the cursor jams against the text.
Practice Exercise Ask the user for their birth year, then print how old they will turn this year (use 2026).
Solution
Line 1 converts the typed text to an int. Line 2 subtracts to get the age. Line 3 reports it. If the user types 2000 , output is You
turn 26 this year.
Syntax
Example 1 — Basic
1 text_number = "100"
2 real_number = int(text_number)
3 print(real_number + 50)
Line 1: "100" looks like a number but is text — you cannot do math with it.
Line 2: int(...) reads the text and produces the integer 100 , stored in a new variable.
Line 3: now arithmetic works: 100 + 50 .
OUTPUT
150
Example 2 — Practical
1 score = 95
2 message = "Your score is " + str(score) + " points"
3 print(message)
OUTPUT
Real-world use: building messages, log lines, and labels almost always needs number→string conversion.
Common Mistakes • "5 apples" cannot become an int — int("5 apples") crashes. Only clean numeric text converts.
• int("3.5") fails; use float("3.5") first, then int(...) to truncate if needed.
• Forgetting that int(3.9) gives 3 — it truncates, it does not round.
Practice Exercise Ask for a price as text, apply 10% tax, and print the final price as a sentence.
Solution
Line 1 converts the typed text to a decimal. Line 2 adds 10% by multiplying by 1.10. Line 3 rounds to 2 decimals and converts the
number back to text so it can be joined into the sentence. Input 200 → Final price with tax: 220.0 .
MODULE 2
Operators
An operator is a symbol that performs an action on values, called operands. If variables are nouns, operators are the verbs. There
are four families you will use constantly: arithmetic (math), comparison (asking questions), logical (combining questions), and
assignment (storing results).
1 a = 17
2 b = 5
3 print(a + b, a - b, a * b)
4 print(a / b, a // b, a % b, a ** 2)
OUTPUT
22 12 85 3.4 3 2 289
1 number = 24
2 remainder = number % 2
3 print("Remainder when divided by 2:", remainder)
Line 2: % gives 0 for even numbers and 1 for odd. 24 is even, so remainder is 0. This single trick powers most “is it even / every 3rd row /
leap-year” logic.
OUTPUT
Remainder when divided by 2: 0
Common Mistakes • Expecting / to give a whole number — it always returns a float ( 10/2 is 5.0 ).
• Confusing // (drop remainder) with % (keep only remainder).
Practice Exercise Given seconds = 200 , print how many whole minutes and leftover seconds that is.
Solution
1 seconds = 200
2 minutes = seconds // 60
3 rest = seconds % 60
4 print(minutes, "min", rest, "sec")
// gives 3 whole minutes; % gives the leftover 20 seconds. Output: 3 min 20 sec .
1 x = 10
2 y = 7
3 print(x == y, x != y, x > y, x <= y)
Line 3: x == y → False (10 is not 7); x != y → True; x > y → True; x <= y → False. Note == (two equals) is a question; = (one
equals) is assignment — mixing them is the #1 beginner bug.
OUTPUT
False True True False
1 age = 20
2 can_vote = age >= 18
3 print("Eligible to vote:", can_vote)
Line 2: the comparison produces a boolean stored directly in can_vote . This is exactly how feature flags and permission checks are built.
OUTPUT
Eligible to vote: True
1 s1 = 88
2 s2 = 90
3 print("First >= second:", s1 >= s2)
The comparison evaluates to False (88 is not ≥ 90). Output: First >= second: False .
1 a = True
2 b = False
3 print(a and b, a or b, not a)
Line 3: a and b → False (b is False); a or b → True (a is True); not a → False (flip of True).
OUTPUT
False True False
1 age = 30
2 income = 45000
3 eligible = age >= 21 and income >= 30000
4 print("Loan eligible:", eligible)
Line 3: two comparisons each produce a boolean, then and requires both to be True. Age check is True, income check is True, so the
whole expression is True.
OUTPUT
Loan eligible: True
Common Mistakes • Writing age >= 21 and 30000 — each side of and must be a full condition, not a bare number.
• Using && / || (other languages) — Python uses the words and / or .
Practice Exercise A user may enter if they have a ticket or are on the guest list. Model both flags and print whether they may
enter.
Solution
1 has_ticket = False
2 on_guestlist = True
3 print("May enter:", has_ticket or on_guestlist)
or needs only one True side; the guest list is True, so the result is True.
1 score = 0
2 score += 10
3 score += 5
4 print(score)
Line 1: start at 0. Line 2: += adds 10 and re-stores → 10. Line 3: adds 5 → 15. The variable is updated in place each time.
OUTPUT
15
1 total = 0
2 total += 250 # bread
3 total += 99 # milk
4 total *= 1.05 # add 5% tax
5 print("Pay:", round(total, 2))
Lines 2–3: accumulate item prices. Line 4: *= multiplies the whole running total by 1.05, applying tax in place. Line 5: rounds for
display.
OUTPUT
Pay: 366.45
Common Mistakes • Forgetting to initialise the variable first ( total += 5 before total exists → error).
• Reading += as “equals plus” — it is “add, then store back.”
Practice Exercise Start a counter at 100, subtract 30, then halve it using compound operators, and print the result.
Solution
1 value = 100
2 value -= 30
3 value /= 2
4 print(value)
-= gives 70; /= divides by 2 giving 35.0 (division yields a float). Output: 35.0 .
MODULE 3
Conditions
A condition lets a program choose what to do. Up to now code ran top to bottom, every line, always. Conditions break that
straight line so a program can react: show a discount only if the cart is big enough, deny a login if the password is wrong. The key
idea is indentation — Python uses spaces (4 by convention) to mark which lines belong inside a decision.
3.1 if
Concept & Syntax
if runs a block only when its condition is True. Where used: input validation, gating features, alerts.
if condition:
# runs only when condition is True (note the 4-space indent)
Example 1
1 temperature = 38
2 if temperature > 37.5:
3 print("You have a fever.")
Line 2: the condition 38 > 37.5 is True, and the colon : opens the block.
Line 3: indented, so it belongs to the if ; it runs because the condition was True.
OUTPUT
You have a fever.
3.2 if / else
else provides the “otherwise” path — it runs when the if condition is False, so exactly one of the two blocks always runs.
Example 2 — practical
1 marks = 82
2 if marks >= 90:
3 print("Grade A")
4 elif marks >= 75:
5 print("Grade B")
6 else:
7 print("Grade C")
Line 2: 82 ≥ 90 is False, skip. Line 4: 82 ≥ 75 is True → print B and skip the rest. Order matters: Python stops at the first match.
OUTPUT
Grade B
Output explained: 82 fails the ≥90 test but passes ≥75, so grade B is assigned; since B is not F, the PASS branch runs.
Practice Exercise Extend the project: after the grade, also print “Distinction” if marks are 95 or above.
Solution
Place this after line 19. It is an independent if (not part of the grade ladder) so it runs in addition to the grade message
whenever marks reach 95.
MODULE 4
Loops
A loop repeats a block of code so you don't write it a thousand times. Whenever you hear “for each…”, “keep going until…”, or
“repeat N times,” you need a loop. They drive everything from printing a report's rows to retrying a network call.
Example 1
1 for i in range(5):
2 print("Count:", i)
OUTPUT
Count: 0 Count: 1 Count: 2 Count: 3 Count: 4
OUTPUT
Total: 250
1 count = 3
2 while count > 0:
3 print("Launch in", count)
4 count -= 1
5 print("Lift off!")
Line 2: the condition is checked before each pass; while count is positive, the body runs.
Line 4: crucial — we shrink count each time so the condition eventually becomes False. Without this the loop never ends.
OUTPUT
Launch in 3 Launch in 2 Launch in 1 Lift off!
OUTPUT
1 3 5
Common Mistakes • Infinite loop: forgetting to change the while condition variable.
• Off-by-one with range : range(1, 5) stops at 4, not 5.
• Indenting the “after loop” line so it accidentally runs every pass.
Line 1: ask how many numbers, convert to int so it can drive the loop.
Lines 2–5: set up accumulators. largest / smallest start as None so the very first number can become both (we have no real value to
compare against yet).
Line 7: loop exactly how_many times.
Line 8: read each number as a float.
Line 9: add to the running total.
Lines 10–11: update largest if it is still unset ( is None ) or the new number beats it. is None is the correct way to test for “no value
yet.”
Lines 12–13: mirror logic for the smallest.
Lines 14–15: count evens using the modulo trick from Module 2.
Line 17: average = sum ÷ count, computed after the loop.
Lines 18–23: print the full report; round(...,2) keeps the average tidy.
Practice Exercise Add a count of how many numbers were greater than the average. (Hint: you must store the numbers first.)
Solution
1 nums = []
2 for i in range(how_many):
3 [Link](float(input("Enter number: ")))
4 average = sum(nums) / len(nums)
5 above = 0
6 for n in nums:
7 if n > average:
8 above += 1
9 print("Above average:", above)
We must keep the numbers in a list (Module 6) because we can only know the average after seeing them all, then loop a second
time to count those above it.
MODULE 5
Strings
A string is text — a sequence of characters. Since almost everything a user types or sees is text, strings are the most-handled
data type in real software: names, messages, file contents, API responses, search queries. This module covers reaching into a
string (indexing/slicing), transforming it (methods), and building it (formatting).
5.1 Indexing
Each character has a position number called an index, starting at 0. Negative indexes count from the end ( -1 is the last
character).
1 word = "PYTHON"
2 print(word[0], word[2], word[-1])
Line 2: word[0] is 'P' (first), word[2] is 'T' (third — remember counting starts at 0), word[-1] is 'N' (last).
OUTPUT
P T N
5.2 Slicing
Slicing extracts a substring with text[start:stop] — from start up to but not including stop .
1 email = "john@[Link]"
2 print(email[0:4])
3 print(email[5:])
OUTPUT
john [Link]
5.3 Methods
A method is a built-in action attached to a value, called with a dot: [Link]() . Strings come with dozens; these are the
everyday ones.
Line 2: methods chain left to right: strip() removes the outer spaces, lower() lowercases, replace turns the underscore into a space.
Each method returns a new string the next one acts on.
Line 3: brackets prove no stray spaces remain.
OUTPUT
[john doe]
1 name = "Sara"
2 score = 91.5
3 print(f"{name} scored {score}% (rounded: {score:.0f}%)")
Line 3: {name} and {score} are replaced by their values; {score:.0f} formats the number with 0 decimals. Far cleaner than gluing
strings with + and str() .
OUTPUT
Common Mistakes • Expecting methods to change the original — strings are immutable; methods return a new string, so you
must capture it ( s = [Link]() ).
• Forgetting the f prefix → {name} prints literally.
• Index out of range: "hi"[5] crashes — valid indexes here are 0 and 1.
Output explained: length ≥ 8 (point), has an uppercase H (point), has digits (point), has '@' (point) → 4/4 → Strong.
Practice Exercise Add a rule: subtract a point and warn if the password contains the word "password" (any casing).
Solution
1 if "password" in [Link]():
2 score -= 1
3 print("Warning: avoid the word 'password'.")
[Link]() makes the check case-insensitive, and in tests whether the substring appears anywhere. Place this before the
strength mapping so the lowered score is reflected.
MODULE 6
Lists
A list stores many values in one ordered, changeable container. Instead of item1 , item2 , item3 , you keep them together and
loop over them. Lists are the workhorse collection of Python: a cart's items, a file's lines, rows from a database, search results —
all lists.
OUTPUT
apple ['mango', 'cherry']
OUTPUT
[3, 4] 3
1 numbers = [1, 2, 3, 4, 5, 6]
2 squares_of_even = [n*n for n in numbers if n % 2 == 0]
3 print(squares_of_even)
Line 2: read it as: “for each n in numbers, keep only those where n is even, and put n*n into the new list.” The if filters, the
expression transforms.
OUTPUT
[4, 16, 36]
Common Mistakes • .append() returns None ; never write fruits = [Link]("x") — it wipes your list.
• Index out of range when the list is shorter than you think; check len() .
• Two variables pointing at the same list: editing one edits “both.” Use new = old[:] to copy.
1 cart = []
2 while True:
3 print("\n1) Add 2) Remove 3) View 4) Checkout")
4 choice = input("Choose: ")
5
6 if choice == "1":
7 name = input("Item name: ")
8 price = float(input("Price: "))
9 [Link]([name, price])
10 print(f"Added {name}.")
11 elif choice == "2":
12 name = input("Item to remove: ")
13 for item in cart:
14 if item[0] == name:
15 [Link](item)
16 print(f"Removed {name}.")
17 break
18 elif choice == "3":
19 if not cart:
20 print("Cart is empty.")
21 for item in cart:
22 print(f" - {item[0]}: {item[1]}")
23 elif choice == "4":
24 total = sum(item[1] for item in cart)
25 print(f"Items: {len(cart)} | Total: {round(total, 2)}")
26 break
27 else:
28 print("Invalid choice.")
Practice Exercise Add option 5 to apply a discount code "SAVE10" that prints the total reduced by 10%.
Solution
We recompute the total, then multiply by 0.90 (i.e. keep 90%) only when the code matches. For the cart above, 170 → 153.0.
MODULE 7
7.1 Tuples
A tuple is like a list but immutable — once created it cannot change. Use it for data that should stay fixed: coordinates, RGB
colours, a database record. The fixedness is a safety feature.
1 point = (3, 5)
2 x, y = point # unpacking
3 print("x:", x, "y:", y)
4 # point[0] = 9 # would crash: tuples can't be changed
OUTPUT
x: 3 y: 5
7.2 Sets
A set holds only unique items and is unordered. It instantly removes duplicates and checks membership faster than a list. Use it
for tags, unique visitors, or “have I seen this already?”
Lines 1–2: the duplicate "ann" is automatically dropped — a set keeps one of each.
Line 3: membership test, very fast even with millions of items.
Line 5: set maths — & intersection (in both), | union (in either), - difference (in a but not b).
OUTPUT
{'ann', 'bob', 'cara'} True {2, 3} {1, 2, 3, 4} {1}
7.3 Dictionaries
A dictionary stores key → value pairs. Instead of remembering position numbers, you look things up by a meaningful label. This
is the single most important real-world structure: every JSON object, API response, and config file is a dictionary.
OUTPUT
Lee {'name': 'Lee', 'age': 21, 'city': 'Seoul'} name -> Lee age -> 21 city -> Seoul
Common Mistakes • Accessing a missing key ( student["phone"] ) → KeyError ; use [Link]("phone") to get None instead.
• Trying to change a tuple, or expecting a set to keep insertion order or allow indexing.
• Using a list as a dictionary key — keys must be immutable (string, number, tuple).
7.4 Project — Student Information System
Stores many students as a list of dictionaries — exactly how records arrive from a database or API. Demonstrates all three
structures working together.
1 students = []
2 subjects_seen = set()
3
4 while True:
5 action = input("\n[a]dd [l]ist [s]earch [q]uit: ").lower()
6 if action == "a":
7 record = {
8 "name": input("Name: "),
9 "roll": int(input("Roll no: ")),
10 "marks": float(input("Marks: ")),
11 "subject": input("Subject: ")
12 }
13 [Link](record)
14 subjects_seen.add(record["subject"])
15 elif action == "l":
16 for s in students:
17 print(f"{s['roll']:>3} | {s['name']:<10} | {s['marks']:>5} | {s['subject']}")
18 print("Subjects offered:", subjects_seen)
19 elif action == "s":
20 target = int(input("Roll no to find: "))
21 found = None
22 for s in students:
23 if s["roll"] == target:
24 found = s
25 break
26 print(found if found else "No such student.")
27 elif action == "q":
28 print(f"Saved {len(students)} students. Bye!")
29 break
Practice Exercise Add a "[t]op" command that prints the student with the highest marks.
Solution
max(..., key=...) finds the dictionary whose "marks" value is largest; the lambda (a tiny inline function, see Module 8) tells
max which field to compare.
MODULE 8
Functions
A function is a named, reusable block of code. You define the steps once, then call it by name whenever you need them.
Functions stop you from copying code, make programs readable, and let you fix a bug in one place. Every real codebase is mostly
functions calling functions.
def function_name(parameters):
# body
return value # optional: hands a result back
def defines a function. Parameters are the named inputs in the definition; arguments are the actual values you pass when
calling. return sends a result back to the caller; without it a function returns None .
Example 1 — basic
1 def greet(name):
2 return f"Hello, {name}!"
3
4 message = greet("Sam")
5 print(message)
OUTPUT
Hello, Sam!
Line 1: three parameters; tax=0.05 is a default — used when the caller omits it.
Lines 2–3: compute subtotal then add tax, returning the final figure.
Line 5: only two arguments given, so tax defaults to 5% → 315.0.
Line 6: tax passed as a keyword argument (18%), overriding the default → 354.0.
OUTPUT
315.0 354.0
8.2 Scope
Scope is where a variable is visible. Variables created inside a function are local — they exist only during that call and cannot be
seen outside. This isolation prevents functions from accidentally clobbering each other's data.
1 def calc():
2 result = 42 # local
3 return result
4 calc()
5 # print(result) # crashes: result is not visible here
8.3 Recursion
Recursion is a function that calls itself to solve a smaller version of the same problem. It needs a base case (a stopping point) or
it loops forever. Great for naturally nested problems: factorials, folders within folders, tree menus.
1 def factorial(n):
2 if n <= 1: # base case
3 return 1
4 return n * factorial(n - 1) # recursive step
5
6 print(factorial(5))
Lines 2–3: the base case — factorial of 0 or 1 is 1; this stops the chain.
Line 4: otherwise return n × factorial of n-1 . So factorial(5) = 5×4×3×2×1.
Line 6: the calls stack and unwind: 120.
OUTPUT
120
Common Mistakes • Confusing print with return — printing shows a value but doesn't hand it back for further use.
• Forgetting the base case in recursion → infinite calls, then RecursionError .
• Putting a required parameter after a default one in the definition (illegal).
Lines 1–3: three one-line functions, each returning a single arithmetic result.
Lines 4–7: division guards against dividing by zero, returning a message instead of crashing.
Lines 9–13: calculate maps each symbol to its function in a dictionary. table[op] retrieves the matching function and (a, b)
immediately calls it — a clean alternative to a long if/elif chain. Functions are values you can store, exactly like numbers.
Lines 15–18: menu loop; 'q' breaks out.
Line 19: .split() breaks "8 * 3" on spaces into three pieces, unpacked into left , op , right .
Line 20: convert the operands to floats and dispatch to the right function.
Line 21: print the result with an "=" sign.
OUTPUT
Expression (e.g. 8 * 3) or 'q': 8 * 3 = 24.0 Expression (e.g. 8 * 3) or 'q': 10 / 0 = Error: division by zero
Practice Exercise Add a power operation ^ using a new function and register it in the dispatch table.
Solution
Define power using ** , then add the "^" key. No other code changes — that is the payoff of the dispatch-table design. Now 2 ^
5 returns 32.0.
MODULE 9
Object-Oriented Programming
OOP is a way of organising code around things (objects) rather than loose functions and variables. A class is a blueprint; an
object is a real item built from that blueprint. If "Car" is the class, your specific red Toyota is an object. OOP bundles data and the
actions on that data together, which is how every large system — banking, games, web frameworks — stays manageable.
1 class Dog:
2 def __init__(self, name, breed):
3 [Link] = name
4 [Link] = breed
5
6 def bark(self):
7 return f"{[Link]} says Woof!"
8
9 d = Dog("Rex", "Labrador")
10 print([Link])
11 print([Link]())
Line 1: class Dog: defines the blueprint (class names use CapWords by convention).
Line 2: the constructor; self is always the first parameter and represents the object being built.
Lines 3–4: store the passed values on the object as [Link] and [Link] — these persist for the object's lifetime.
Lines 6–7: a method — a function inside a class. It can read the object's own data via self .
Line 9: create an object; Python calls __init__ with "Rex" and "Labrador". d now holds a Dog object.
Lines 10–11: access data with [Link] and call behaviour with [Link]() .
OUTPUT
Rex Rex says Woof!
9.2 Inheritance
Inheritance lets a new class reuse another class's code and add or change parts. The child inherits from the parent. It models
“is-a” relationships (a SavingsAccount is an Account) and avoids duplicating shared logic.
1 class Animal:
2 def __init__(self, name):
3 [Link] = name
4 def speak(self):
5 return "Some sound"
6
7 class Cat(Animal):
8 def speak(self):
9 return f"{[Link]} says Meow"
10
11 c = Cat("Milo")
12 print([Link], "->", [Link]())
Line 7: class Cat(Animal) means Cat inherits everything from Animal — including __init__ , so we don't rewrite it.
Lines 8–9: Cat overrides speak with its own version.
Line 12: [Link] comes from the inherited constructor; [Link]() uses the overridden method.
OUTPUT
Milo -> Milo says Meow
Common Mistakes • Forgetting self as the first method parameter, or forgetting to write self. when storing data.
• Calling a method without parentheses: [Link] shows the method object; [Link]() runs it.
• Touching “private” attributes directly instead of via the class's methods.
1 class Account:
2 def __init__(self, owner, balance=0):
3 [Link] = owner
4 self._balance = balance # encapsulated
5
6 def deposit(self, amount):
7 if amount <= 0:
8 print("Deposit must be positive.")
9 return
10 self._balance += amount
11 print(f"Deposited {amount}. Balance: {self._balance}")
12
13 def withdraw(self, amount):
14 if amount > self._balance:
15 print("Insufficient funds.")
16 return
17 self._balance -= amount
18 print(f"Withdrew {amount}. Balance: {self._balance}")
19
20 def show(self):
21 print(f"{[Link]}'s balance: {self._balance}")
22
23 class SavingsAccount(Account):
24 def __init__(self, owner, balance=0, rate=0.04):
25 super().__init__(owner, balance)
26 [Link] = rate
27
28 def add_interest(self):
29 interest = self._balance * [Link]
30 self._balance += interest
31 print(f"Interest {round(interest, 2)} added. Balance: {round(self._balance, 2)}")
32
33 acc = SavingsAccount("Nadia", 1000)
34 [Link](500)
35 [Link](200)
36 acc.add_interest()
37 [Link]()
Lines 1–4: the base class stores the owner and a balance. _balance with a leading underscore marks it as internal (encapsulation) —
outsiders should change it only through deposit / withdraw .
Lines 6–11: deposit validates the amount is positive (guard clause + early return ), then increases the balance and reports it.
Lines 13–18: withdraw refuses to overdraw, otherwise subtracts and reports.
Lines 20–21: show displays the current state.
Line 23: SavingsAccount(Account) inherits all of the above.
Lines 24–26: its constructor adds an interest rate . super().__init__(...) calls the parent constructor so we reuse the owner/balance
setup instead of duplicating it.
Lines 28–31: a new method unique to savings: compute interest on the balance and add it.
Lines 33–37: create a savings account, then deposit, withdraw, add interest, and show — note deposit / withdraw were inherited
unchanged, while add_interest is the subclass's own.
OUTPUT
Deposited 500. Balance: 1500 Withdrew 200. Balance: 1300 Interest 52.0 added. Balance: 1352.0 Nadia's balance: 1352.0
Output explained: 1000 + 500 − 200 = 1300; interest at 4% is 52; final balance 1352. The inherited methods and the new one
work together seamlessly.
Practice Exercise Add a CurrentAccount subclass that allows overdraft down to −500.
Solution
1 class CurrentAccount(Account):
2 def withdraw(self, amount):
3 if amount > self._balance + 500:
4 print("Overdraft limit reached.")
5 return
6 self._balance -= amount
7 print(f"Withdrew {amount}. Balance: {self._balance}")
By overriding withdraw , the current account permits the balance to fall to −500 (polymorphism: same method name, different
rule). All other behaviour is inherited from Account .
MODULE 10
1 try:
2 age = int(input("Enter age: "))
3 except ValueError:
4 print("That was not a whole number.")
5 else:
6 print(f"Next year you will be {age + 1}.")
7 finally:
8 print("Thanks for using the app.")
Line 2: the risky step — int() fails if the text isn't numeric.
Lines 3–4: if a ValueError is raised, this block handles it; the program continues instead of crashing.
Lines 5–6: else runs only when the try succeeded.
Lines 7–8: finally runs no matter what — success or failure.
Line 1: open in write mode; as f names the file object. Write mode creates the file (or wipes an existing one).
Lines 2–3: \n adds a newline so the lines don't run together.
Line 5: reopen in read mode.
Line 6: .read() returns the whole file as one string.
OUTPUT
Line one Line two
Common Mistakes • Using "w" when you meant "a" — write mode erases the existing file.
• Catching every error with a bare except: — name the specific exception so real bugs aren't hidden.
• Forgetting \n , so all writes land on one line.
Lines 1–2: a list for records and the number of students to process.
Lines 6–11: an input-validation loop — keep asking until float() succeeds. The break on line 9 exits only after a valid number; a
ValueError instead triggers the retry message. This is robust real-world input handling.
Line 12: a conditional expression assigns Pass/Fail in one line.
Line 13: store the student as a dictionary.
Line 15: open the report file for writing inside a with block so it always closes.
Lines 16–17: write a title; "=" * 30 repeats the character 30 times to draw a divider.
Lines 18–20: write one aligned line per student using f-string field widths.
Lines 21–23: compute and write the class average.
Line 25: confirm to the user; the data now lives on disk permanently.
Practice Exercise Also handle the case where the file can't be written (e.g. no permission) and tell the user instead of crashing.
Solution
1 try:
2 with open("[Link]", "w") as f:
3 [Link]("...")
4 except (PermissionError, OSError) as e:
5 print("Could not write the report:", e)
Wrapping the file block in try/except catches OS-level problems. Naming the exception as e lets us print the actual reason,
turning a crash into a clear message.
MODULE 11
11.1 Modules
import pulls in code from Python's standard library or installed packages, instantly giving you tested tools.
1 import random
2 from datetime import date
3
4 print([Link](1, 6)) # dice roll
5 print([Link]()) # today's date
Line 1: import the whole random module; use its tools as [Link] .
Line 2: import just one name ( date ) from a module so we can use it directly.
Line 4: randint(1, 6) returns a random whole number 1–6, inclusive.
Line 5: [Link]() returns the current date.
OUTPUT (EXAMPLE)
4 2026-06-04
11.2 JSON
JSON looks like a Python dictionary and is how APIs and config files store structured data. [Link] turns a Python object into
a JSON string; [Link] parses a JSON string back into Python.
1 import json
2 data = {"name": "Lee", "skills": ["python", "sql"]}
3 text = [Link](data) # Python -> JSON string
4 back = [Link](text) # JSON string -> Python
5 print(text)
6 print(back["skills"][0])
Line 3: serialise the dictionary to a text string suitable for saving or sending.
Line 4: parse it back into a real dictionary you can index.
Line 6: proves the round-trip worked — we read the first skill.
OUTPUT
{"name": "Lee", "skills": ["python", "sql"]} python
The requests library (install with pip install requests ) fetches data from web APIs. You send a request to a URL and get back a
response — usually JSON.
OUTPUT (EXAMPLE)
Paris: 18.3°C, wind 11.2 km/h
Common Mistakes • Assuming the response always has the keys you expect — APIs fail or change; check before indexing.
• Forgetting .json() and trying to index the raw response object.
• Not handling network errors — wrap calls in try/except [Link] in production.
Practice Exercise Wrap the network calls so a connection failure prints “Weather service unavailable” instead of crashing.
Solution
1 try:
2 print(get_weather("Paris"))
3 except [Link]:
4 print("Weather service unavailable.")
[Link] is the parent of all network errors (timeouts, DNS failures, connection drops), so one except catches
them all and the program stays alive.
MODULE 12
Python for AI
Python is the default language of modern AI because of its libraries. This module gives you working literacy in the five building
blocks: NumPy (fast numbers), Pandas (tables of data), the OpenAI API (calling a large language model), LLM concepts (how
chat models behave), and RAG (giving a model your own documents to answer from).
1 import numpy as np
2 arr = [Link]([10, 20, 30, 40])
3 print(arr * 2)
4 print([Link](), [Link]())
OUTPUT
[20 40 60 80] 25.0 40
1 import pandas as pd
2 df = [Link]({
3 "name": ["Ali", "Bea", "Cy"],
4 "score": [80, 35, 90]
5 })
6 print(df["score"].mean())
7 print(df[df["score"] >= 40])
Lines 2–5: build a DataFrame from a dictionary — each key becomes a column.
Line 6: select the score column and average it.
Line 7: boolean filtering — df["score"] >= 40 makes a True/False mask, and indexing with it keeps only the passing rows.
OUTPUT
68.33333333333333 name score 0 Ali 80 2 Cy 90
Lines 1–2: import the client and create it; it automatically reads your secret key from the environment (never hard-code keys).
Lines 4–5: request a chat completion and choose the model.
Lines 6–9: the message list. The system role shapes behaviour; the user role is the actual prompt.
Line 11: the reply text lives at choices[0].[Link] — the model can return several choices; we take the first.
OUTPUT (EXAMPLE)
A variable is a named label that points to a value stored in memory.
1 import os
2
3 def reply(history):
4 """Return the assistant's next message given the conversation."""
5 if not [Link]("OPENAI_API_KEY"):
6 last = history[-1]["content"].lower()
7 if "hello" in last or "hi" in last:
8 return "Hello! (offline mode) How can I help?"
9 return "I'm in offline mode, but I heard: " + last
10 from openai import OpenAI
11 client = OpenAI()
12 resp = [Link](
13 model="gpt-4o-mini", messages=history
14 )
15 return [Link][0].[Link]
16
17 history = [{"role": "system", "content": "You are a friendly assistant."}]
18 print("Chatbot ready. Type 'quit' to exit.")
19 while True:
20 user = input("You: ")
21 if [Link]() == "quit":
22 break
23 [Link]({"role": "user", "content": user})
24 answer = reply(history)
25 [Link]({"role": "assistant", "content": answer})
26 print("Bot:", answer)
Common Mistakes • Hard-coding the API key in the file (a security leak) — always read it from the environment.
• Not sending the conversation history, so the bot “forgets” everything each turn.
• Forgetting that API calls cost money and can fail — wrap them in try/except in production.
Practice Exercise Limit the bot's memory to the last 10 messages so very long chats don't grow forever.
Solution
Keep the system message (index 0) and only the most recent 10 turns by slicing. This caps the data sent to the model, controlling
cost and staying within context limits.
CAPSTONE
Folder Structure
ai_student_assistant/
├── [Link] # entry point: the menu loop and user interaction
├── [Link] # the Student class (OOP: data + behaviour)
├── [Link] # load/save students to a JSON file (persistence)
├── ai_helper.py # AI features: LLM answer + tiny RAG over notes
└── data/
├── [Link] # where records are saved between runs
└── [Link] # the knowledge base the RAG searches
[Link] — the only file the user interacts with; it orchestrates the others.
[Link] — defines what a student is; pure data and behaviour, no I/O.
[Link] — the only file that knows about reading/writing JSON.
ai_helper.py — the only file that knows about the LLM and retrieval.
data/ — holds files that persist, kept separate from code.
Lines 2–5: the constructor stores the three pieces of student data on the object.
Lines 7–12: grade is computed from marks. The @property decorator lets you read it like an attribute ( [Link] ) even though it's
calculated on the fly — so the grade is always correct and never stored stale.
Lines 14–15: to_dict converts the object into a plain dictionary so it can be saved as JSON (JSON can't store custom objects).
Lines 17–19: from_dict is a @staticmethod (a function that belongs to the class but needs no specific object) that rebuilds a Student from
a saved dictionary — the reverse of to_dict .
Lines 21–22: __str__ defines how the object looks when printed, giving a neat one-line row.
Lines 1–2: import JSON/OS tools and the Student class so we can rebuild objects.
Line 4: one place defines the file path (a constant) — change it here only.
Lines 6–8: on first run the file won't exist, so return an empty list instead of crashing.
Lines 9–11: read the JSON into a list of dictionaries, then a comprehension turns each dictionary back into a Student object.
Line 14: makedirs(..., exist_ok=True) creates the data folder if missing, harmlessly if it exists.
Lines 15–16: convert every student to a dictionary and write the list as nicely-indented JSON. indent=2 makes the saved file human-
readable.
Lines 3–7: load the knowledge base, one cleaned line per note (blank lines skipped). This is our document store.
Lines 9–18: the heart of RAG, written without heavy libraries so you can see the mechanism: (a) turn the question into a set of words; (b)
for each note, count shared words using set intersection & as a similarity score; (c) sort highest-first; (d) return the top k relevant notes.
A real system replaces this word-overlap score with vector embeddings, but the retrieve-then-rank loop is identical.
Lines 20–22: answer first retrieves context, then joins it into a text block — the “augmentation” step.
Lines 23–24: offline fallback so the project runs without a key: it simply shows the retrieved notes.
Lines 25–34: with a key, build a prompt that grounds the model — “Answer using ONLY the context” — then send it. This is what stops the
model from inventing facts.
Line 35: return the generated answer.
File 4 — [Link] (Entry Point)
1 from models import Student
2 from storage import load_students, save_students
3 from ai_helper import answer
4
5 def main():
6 students = load_students()
7 print("=== AI Student Assistant ===")
8 while True:
9 choice = input("\n[a]dd [l]ist [r]eport [ask] [q]uit: ").lower()
10 if choice == "a":
11 name = input("Name: ")
12 roll = int(input("Roll: "))
13 marks = float(input("Marks: "))
14 [Link](Student(name, roll, marks))
15 save_students(students)
16 print("Saved.")
17 elif choice == "l":
18 for s in students:
19 print(s)
20 elif choice == "r":
21 if students:
22 avg = sum([Link] for s in students) / len(students)
23 top = max(students, key=lambda s: [Link])
24 print(f"Students: {len(students)} | Average: {round(avg, 2)}")
25 print(f"Top scorer: {[Link]} ({[Link]})")
26 elif choice == "ask":
27 q = input("Ask about your notes: ")
28 print(answer(q))
29 elif choice == "q":
30 save_students(students)
31 print("Goodbye!")
32 break
33
34 if __name__ == "__main__":
35 main()
Lines 1–3: import the pieces from the other modules — this is where everything comes together.
Line 6: load any previously saved students at startup, so data persists across runs.
Lines 8–9: the menu loop and the user's choice.
Lines 10–16: ADD — collect and convert fields, create a Student object, append it, and immediately save_students so nothing is lost on a
crash.
Lines 17–19: LIST — printing each object triggers the __str__ method we defined, giving formatted rows for free.
Lines 20–25: REPORT — compute the average and find the top scorer with max(..., key=...) ; [Link] uses the computed property.
Lines 26–28: ASK — send the question to the AI helper, which retrieves relevant notes and answers from them (the RAG feature).
Lines 29–32: QUIT — save once more, then break.
Lines 34–35: the if __name__ == "__main__" guard runs main() only when this file is executed directly (not when imported) — standard
Python project hygiene.
How to run it 1) Create the folder structure above. 2) Put a few facts (one per line) in data/[Link] . 3) From inside the project
folder run python [Link] . 4) Optionally set OPENAI_API_KEY and pip install openai to enable real LLM answers — without it,
the assistant still runs in offline mode.
Capstone Challenges 1) Add a search command to find a student by roll number.
2) Add a delete command and re-save.
3) Replace the word-overlap RAG score with real embeddings (e.g. sentence-transformers ) and observe better retrieval.
4) Export the report to a .txt file using what you learned in Module 10.
You've reached the end. You now have the core of a working Python developer's toolkit — fundamentals, data structures,
functions, OOP, files, APIs, and the on-ramp to AI. The only step left is the most important one: build things. Re-type every project,
break it, extend it, and start a project of your own.