■
Python
Complete Learning Guide — Edition 2
Basic → Confident Coder · Real Projects · Deep Dives · Extra Knowledge
13 Chapters 80+ Examples Real Projects Deep Dives Extra Modules
Table of Contents
Getting Started
01
Install, first program, how Python runs
Variables & Data Types
02
Numbers, strings, booleans, type conversion
Control Flow
03
if/elif/else, for, while, break, continue
Functions
04
Def, args, defaults, *args/**kwargs, lambda
Data Structures
05
Lists, tuples, sets, dicts + comprehensions
Strings In Depth
06
Slicing, methods, f-strings, regex intro
File Handling
07
Read/write/CSV/JSON + context managers
Error Handling
08
try/except/finally, custom exceptions
Object-Oriented Python
09
Classes, inheritance, dunder methods
Modules & the Standard Library
10
os, math, random, datetime, collections
Advanced Functions
11
Decorators, generators, iterators, closures
List/Dict Comprehensions+
12
Comprehensions, zip, map, filter, walrus
Practical Projects
13
Number game, password gen, data analyser, OOP bank
CHAPTER 01
Getting Started
Install Python & write your first real programs
■ Quick Summary
■ Python is free, cross-platform, and beginner-friendly
■ Download from [Link] — tick 'Add to PATH' on Windows
■ Run scripts with: python [Link]
■ print() displays output; input() reads keyboard input
■ Indentation (4 spaces) is mandatory — it defines code blocks
What is Python & Why Learn It?
Python is a high-level, interpreted language created by Guido van Rossum in 1991. It is used in web
development (Django, Flask), data science (pandas, NumPy), AI & machine learning (TensorFlow,
PyTorch), automation, game dev, and more. Its clean syntax makes it the world's most popular first
language.
■ Key Concept: Python ranks #1 on the TIOBE index and is the top language for data science and AI.
Installing & Running Python
1. Go to [Link]/downloads and grab the latest version.
2. On Windows: check Add Python to PATH during setup.
3. Open a terminal and type python --version to confirm.
4. Write code in any editor (VS Code, PyCharm, IDLE, Notepad).
Your First Programs
■ [Link] — classic first program
print("Hello, World!")
print("Python is awesome!")
Hello, World!
Python is awesome!
■ greet_user.py — interactive input
name = input("Enter your name: ")
age = int(input("Enter your age: "))
print(f"Hi {name}! In 10 years you will be {age + 10}.")
Enter your name: Alice
Enter your age: 22
Hi Alice! In 10 years you will be 32.
■ math_intro.py — Python as a calculator
print(2 + 3) # 5
print(10 / 3) # 3.333...
print(10 // 3) # 3 (floor division)
print(10 % 3) # 1 (remainder)
print(2 ** 8) # 256 (power)
print(abs(-42)) # 42
print(round(3.7)) # 4
5
3.3333333333333335
3
1
256
42
4
Comments & Code Style
■ [Link]
# Single-line comment — Python ignores this
"""
Multi-line string used as a docstring / block comment.
Great for describing what a function does.
"""
x = 10 # inline comment — keep these brief
# PEP 8 style: use snake_case for variables
user_name = "Alice"
MAX_SCORE = 100 # UPPER_CASE for constants
■ Tip: Run python -m py_compile [Link] to check for syntax errors before running.
CHAPTER 02
Variables & Data Types
Store, label and transform every kind of data
■ Quick Summary
■ Variables are labels pointing to values — no type declaration needed
■ Core types: int, float, str, bool, NoneType
■ Use type() to inspect a variable's type
■ Convert between types with int(), float(), str(), bool()
■ Multiple assignment: a, b, c = 1, 2, 3
Variables — Deep Dive
Python uses dynamic typing: you never declare a type — Python infers it. Variables are just names
that point to objects in memory. The same name can point to different types at different times.
■ variables_deep.py
score = 0 # int
score = score + 10 # still int
score = "ten" # now a string — Python allows this!
# Multiple assignment on one line
x, y, z = 10, 20, 30
a = b = c = 0 # all point to the same 0
# Swap without a temp variable (Python magic!)
x, y = y, x
print(x, y) # 20 10
20 10
Numbers
■ [Link]
i = 1_000_000 # underscores for readability
f = 3.14159
c = 2 + 3j # complex number
print(type(i)) # <class "int">
print(type(f)) # <class "float">
print(type(c)) # <class "complex">
# Useful built-ins
print(max(3, 7, 1)) # 7
print(min(3, 7, 1)) # 1
print(sum([10, 20, 30])) # 60
print(divmod(17, 5)) # (3, 2) → quotient & remainder
<class 'int'>
<class 'float'>
<class 'complex'>
7
1
60
(3, 2)
Booleans & None
■ [Link]
is_raining = True
has_umbrella = False
# Booleans are just ints (True=1, False=0)
print(True + True) # 2
print(True * 10) # 10
# Falsy values — all evaluate to False in a condition
falsy = [0, 0.0, "", [], {}, None, False]
for val in falsy:
print(f"{repr(val):10} → {bool(val)}")
# None means "no value"
result = None
print(result is None) # True (use "is", not "==")
2
10
0 → False
0.0 → False
'' → False
...
Type Conversion — Real Example
■ type_conversion.py — temperature converter
celsius_str = input("Enter temperature in Celsius: ")
celsius = float(celsius_str)
fahrenheit = celsius * 9/5 + 32
kelvin = celsius + 273.15
print(f"{celsius}°C = {fahrenheit:.1f}°F = {kelvin:.2f}K")
Enter temperature in Celsius: 100
100.0°C = 212.0°F = 373.15K
■■ Warning: Always convert input() strings to int/float before doing arithmetic — input always returns a
string.
CHAPTER 03
Control Flow
Decisions, loops, and branching logic
■ Quick Summary
■ if/elif/else lets your program make decisions
■ for loops iterate over sequences; while loops run until a condition is False
■ break exits a loop immediately; continue skips to the next iteration
■ range(start, stop, step) generates number sequences
■ else on a loop runs only if the loop completed without break
if / elif / else
■ bmi_calculator.py — real-world example
weight = float(input("Weight (kg): "))
height = float(input("Height (m): "))
bmi = weight / (height ** 2)
if bmi < 18.5:
category = "Underweight"
elif bmi < 25.0:
category = "Normal weight"
elif bmi < 30.0:
category = "Overweight"
else:
category = "Obese"
print(f"BMI: {bmi:.1f} — {category}")
Weight (kg): 70
Height (m): 1.75
BMI: 22.9 — Normal weight
Ternary (One-Line) if
■ [Link]
age = 20
status = "adult" if age >= 18 else "minor"
print(status) # adult
# Clamp a number between 0 and 100
score = 115
score = max(0, min(score, 100))
print(score) # 100
adult
100
for Loops — Deep Dive
■ for_loops.py
# enumerate gives index AND value
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
# zip pairs two lists together
names = ["Alice", "Bob", "Carol"]
scores = [95, 87, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}/100")
# range with step
print(list(range(0, 20, 5))) # [0, 5, 10, 15]
1. apple
2. banana
3. cherry
Alice: 95/100
Bob: 87/100
Carol: 92/100
[0, 5, 10, 15]
while Loop — Guess the Number
■ while_guess.py — mini game
import random
secret = [Link](1, 10)
attempts = 0
while True:
guess = int(input("Guess (1-10): "))
attempts += 1
if guess < secret:
print("Too low!")
elif guess > secret:
print("Too high!")
else:
print(f"Correct in {attempts} attempts!")
break
Guess (1-10): 5
Too high!
Guess (1-10): 3
Too low!
Guess (1-10): 4
Correct in 3 attempts!
Loop Patterns
■ loop_patterns.py
# Find first even number — loop else
numbers = [3, 7, 11, 4, 9]
for n in numbers:
if n % 2 == 0:
print(f"First even: {n}")
break
else:
print("No even numbers found")
# Skip multiples of 3, print rest
for i in range(1, 11):
if i % 3 == 0:
continue
print(i, end=" ")
First even: 4
1 2 4 5 7 8 10
CHAPTER 04
Functions
Reusable, organised, testable blocks of code
■ Quick Summary
■ def name(params): body — defines a function
■ return sends a value back to the caller
■ Default parameters make arguments optional
■ *args collects extra positional args as a tuple
■ **kwargs collects extra keyword args as a dict
■ lambda: anonymous one-liner functions
■ Scope: variables inside functions are local
Defining Functions — Deep Dive
■ functions_deep.py
def greet(name, greeting="Hello"):
"""Return a personalised greeting string."""
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Good morning"))# Good morning, Bob!
print(greet(greeting="Hi","Carol"))# SyntaxError: positional after keyword
# Multiple return values (returns a tuple)
def min_max(numbers):
return min(numbers), max(numbers)
lo, hi = min_max([5, 2, 8, 1, 9])
print(f"Min={lo}, Max={hi}") # Min=1, Max=9
Hello, Alice!
Good morning, Bob!
Min=1, Max=9
*args and **kwargs — Real Example
■ args_kwargs.py — flexible logger
def log(*messages, level="INFO", sep=" | "):
prefix = f"[{level}]"
print(prefix, [Link](str(m) for m in messages))
log("Server started") # [INFO] Server started
log("404", "Not found", level="ERROR") # [ERROR] 404 | Not found
log("a","b","c", level="DEBUG", sep=" - ")# [DEBUG] a - b - c
def make_profile(**info):
for k, v in [Link]():
print(f" {k:12}: {v}")
make_profile(name="Alice", age=25, role="Developer")
[INFO] Server started
[ERROR] 404 | Not found
[DEBUG] a - b - c
name : Alice
age : 25
role : Developer
Lambda Functions
■ [Link]
# Sort students by grade (descending)
students = [
{"name": "Alice", "grade": 88},
{"name": "Bob", "grade": 95},
{"name": "Carol", "grade": 72},
]
[Link](key=lambda s: s["grade"], reverse=True)
for s in students:
print(f" {s['name']:8} {s['grade']}")
# Lambda with map and filter
nums = [1, 2, 3, 4, 5, 6]
doubled = list(map(lambda x: x*2, nums))
evens = list(filter(lambda x: x%2==0, nums))
print(doubled) # [2, 4, 6, 8, 10, 12]
print(evens) # [2, 4, 6]
Bob 95
Alice 88
Carol 72
[2, 4, 6, 8, 10, 12]
[2, 4, 6]
Scope — LEGB Rule
■ [Link]
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
print(x) # enclosing
outer()
print(x) # global
# Use global keyword to modify a global
counter = 0
def increment():
global counter
counter += 1
increment(); increment()
print(counter) # 2
local
enclosing
global
2
■■ Warning: Avoid using global — prefer returning values from functions and reassigning at the call
site.
CHAPTER 05
Data Structures
Lists, Tuples, Sets & Dictionaries
■ Quick Summary
■ List [ ] — ordered, mutable, allows duplicates
■ Tuple ( ) — ordered, immutable, slightly faster than list
■ Set { } — unordered, mutable, NO duplicates
■ Dict {k:v} — key→value mapping, ordered (Python 3.7+)
■ Comprehensions create collections in one readable line
Lists — Full Guide
■ lists_full.py
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# Slicing
print(numbers[2:5]) # [4, 1, 5]
print(numbers[::-1]) # reversed
# Methods
[Link]() # in-place sort
[Link](7)
[Link](0, 0)
[Link](1) # removes first 1
# List as a stack (LIFO)
stack = []
[Link]("page1")
[Link]("page2")
print([Link]()) # page2
# Flatten nested list
nested = [[1,2],[3,4],[5,6]]
flat = [x for row in nested for x in row]
print(flat) # [1, 2, 3, 4, 5, 6]
[4, 1, 5]
[6, 2, 9, 5, 1, 4, 1, 3]
page2
[1, 2, 3, 4, 5, 6]
Dictionaries — Deep Dive
■ dicts_deep.py
# Word frequency counter
text = "the cat sat on the mat the cat"
freq = {}
for word in [Link]():
freq[word] = [Link](word, 0) + 1
for word, count in sorted([Link](), key=lambda x: -x[1]):
print(f" {word:6}: {'*' * count} ({count})")
# Dict comprehension
squares = {x: x**2 for x in range(1, 6)}
print(squares)
the : *** (3)
cat : ** (2)
sat : * (1)
on : * (1)
mat : * (1)
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
Sets — Practical Uses
■ [Link]
# Remove duplicates while preserving concepts
emails = ["a@[Link]","b@[Link]","a@[Link]","c@[Link]"]
unique = list(set(emails))
print(f"{len(emails)} emails → {len(unique)} unique")
# Set operations
python_devs = {"Alice","Bob","Carol"}
js_devs = {"Bob","Dave","Carol"}
print(python_devs & js_devs) # intersection (both)
print(python_devs | js_devs) # union (all)
print(python_devs - js_devs) # difference (python only)
3 emails → 3 unique
{'Bob', 'Carol'}
{'Alice', 'Bob', 'Carol', 'Dave'}
{'Alice'}
Tuples & Unpacking
■ [Link]
# RGB colour as a tuple
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
r, g, b = RED
print(f"Red channel: {r}") # 255
# Named tuple — like a lightweight class
from collections import namedtuple
Point = namedtuple("Point", ["x","y"])
p = Point(3, 4)
distance = (p.x**2 + p.y**2) ** 0.5
print(f"Distance from origin: {distance}") # 5.0
Red channel: 255
Distance from origin: 5.0
CHAPTER 06
Strings In Depth
Slicing, methods, formatting & regex intro
■ Quick Summary
■ Strings are immutable sequences of characters
■ Slicing: s[start:stop:step] — negative indices count from end
■ Key methods: strip, split, join, replace, find, count, upper, lower
■ f-strings support format specs: {value:.2f}, {value:>10}, {value:,}
■ re module: search, match, findall, sub for pattern matching
Slicing Mastery
■ [Link]
s = "Hello, Python World!"
# 0123456789...
print(s[7:13]) # Python
print(s[-6:]) # World!
print(s[::2]) # Hlo yhnWrd
print(s[::-1]) # !dlroW nohtyP ,olleH
# Check palindrome
def is_palindrome(word):
w = [Link]().replace(" ","")
return w == w[::-1]
print(is_palindrome("racecar")) # True
print(is_palindrome("hello")) # False
print(is_palindrome("A man a plan a canal Panama")) # True
Python
World!
Hlo yhnWrd
!dlroW nohtyP ,olleH
True
False
True
String Methods — Practical
■ str_methods.py — data cleaning
# Simulate cleaning messy CSV data
raw = [" Alice ", "BOB", "carol smith", "DAVE jones"]
cleaned = [[Link]().title() for name in raw]
print(cleaned)
# Split and rejoin
csv_line = "Alice,28,London,Developer"
fields = csv_line.split(",")
print(fields[0], "works as a", fields[3])
# Useful checks
print("abc123".isalnum()) # True
print("12345".isdigit()) # True
print("hello".startswith("he")) # True
print(",".join(["a","b","c"])) # a,b,c
['Alice', 'Bob', 'Carol Smith', 'Dave Jones']
Alice works as a Developer
True
True
True
a,b,c
Advanced f-String Formatting
■ [Link]
price = 12345.678
ratio = 0.8756
name = "Alice"
print(f"Price: ${price:>12,.2f}") # right-aligned, comma
print(f"Ratio: {ratio:.1%}") # percentage
print(f"Name: {name:*^20}") # centred, padded with *
print(f"Hex: {255:#010x}") # hex with 0x prefix
print(f"Binary: {42:08b}") # 8-bit binary
Price: $ 12,345.68
Ratio: 87.6%
Name: *******Alice*******
Hex: 0x000000ff
Binary: 00101010
Regex — Intro
■ regex_intro.py
import re
# Validate an email address
pattern = r"^[\w.+-]+@[\w-]+\.[a-z]{2,}$"
emails = ["user@[Link]", "bad@", "ok@[Link]"]
for e in emails:
status = "valid" if [Link](pattern, e) else "invalid"
print(f" {e:25} → {status}")
# Extract all numbers from a string
text = "Order 42 items at $3.99 each, total $167.58"
numbers = [Link](r"\d+\.?\d*", text)
print("Numbers found:", numbers)
user@[Link] → valid
bad@ → invalid
ok@[Link] → valid
Numbers found: ['42', '3.99', '167.58']
CHAPTER 07
File Handling
Read, write, CSV, JSON & context managers
■ Quick Summary
■ Always use 'with open(...)' — auto-closes the file safely
■ Modes: 'r' read, 'w' write (overwrites), 'a' append, 'rb' binary read
■ csv module handles comma-separated files cleanly
■ json module reads/writes JSON with loads/dumps
■ os and pathlib help manage paths, dirs, and file existence
Reading & Writing Text Files
■ text_files.py
# Write a file
lines = ["Alice,25,Engineer","Bob,30,Designer","Carol,28,Manager"]
with open("[Link]", "w") as f:
for line in lines:
[Link](line + "\n")
# Read it back and process
with open("[Link]", "r") as f:
for line in f:
name, age, role = [Link]().split(",")
print(f"{name:8} (age {age}) — {role}")
Alice (age 25) — Engineer
Bob (age 30) — Designer
Carol (age 28) — Manager
JSON Files — Config & Data
■ json_files.py
import json
# Save settings to JSON
settings = {
"theme": "dark",
"volume": 75,
"hotkeys": ["Ctrl+S","Ctrl+Z","Ctrl+Y"]
}
with open("[Link]", "w") as f:
[Link](settings, f, indent=2)
# Load it back
with open("[Link]", "r") as f:
loaded = [Link](f)
print(loaded["theme"]) # dark
print(loaded["hotkeys"]) # ["Ctrl+S", ...]
dark
["Ctrl+S", "Ctrl+Z", "Ctrl+Y"]
CSV — Student Grade Report
■ csv_grades.py
import csv
data = [
["Name","Math","Science","English"],
["Alice",92,88,95],["Bob",78,85,80],["Carol",90,92,87]
]
with open("[Link]", "w", newline="") as f:
[Link](f).writerows(data)
with open("[Link]") as f:
reader = [Link](f)
for row in reader:
avg = (int(row["Math"])+int(row["Science"])+int(row["English"]))/3
print(f"{row['Name']:8} avg: {avg:.1f}")
Alice avg: 91.7
Bob avg: 81.0
Carol avg: 89.7
File Existence & Path Management
■ file_paths.py
import os
from pathlib import Path
p = Path("data/reports")
[Link](parents=True, exist_ok=True) # create dirs
report = p / "[Link]" # build path safely
report.write_text("Report content here")
print([Link]()) # True
print([Link]) # summary
print([Link]) # .txt
print([Link]) # data/reports
True
summary
.txt
data/reports
CHAPTER 08
Error Handling
Write robust, crash-proof programs
■ Quick Summary
■ try: put risky code here; except: handle the error
■ Catch specific exceptions — avoid bare 'except:'
■ finally: always runs (cleanup, close connections, etc.)
■ raise: throw your own exceptions intentionally
■ Custom exception classes make error types descriptive
try / except / finally — Deep Dive
■ error_handling.py — safe division utility
def safe_divide(a, b):
try:
result = a / b
except TypeError as e:
return f"Type error: {e}"
except ZeroDivisionError:
return "Cannot divide by zero"
else:
return round(result, 4) # runs only if NO exception
finally:
print(f"Attempted: {a} / {b}") # ALWAYS runs
print(safe_divide(10, 3))
print(safe_divide(10, 0))
print(safe_divide("x", 2))
Attempted: 10 / 3
3.3333
Attempted: 10 / 0
Cannot divide by zero
Attempted: x / 2
Type error: unsupported operand...
Custom Exceptions — Bank Account
■ custom_exceptions.py
class BankError(Exception): pass
class InsufficientFunds(BankError):
def __init__(self, balance, amount):
super().__init__(f"Need ${amount:.2f}, only ${balance:.2f} available")
[Link] = amount - balance
class InvalidAmount(BankError):
def __init__(self, amount):
super().__init__(f"Amount must be positive, got: {amount}")
def withdraw(balance, amount):
if amount <= 0: raise InvalidAmount(amount)
if amount > balance: raise InsufficientFunds(balance, amount)
return balance - amount
for amt in [50, 200, -10]:
try:
new_bal = withdraw(100, amt)
print(f"Withdrew ${amt}. Balance: ${new_bal}")
except BankError as e:
print(f"Error: {e}")
Withdrew $50. Balance: $50
Error: Need $200.00, only $100.00 available
Error: Amount must be positive, got: -10
Input Validation Loop
■ validated_input.py
def get_int(prompt, min_val=None, max_val=None):
while True:
try:
val = int(input(prompt))
if min_val is not None and val < min_val:
raise ValueError(f"Must be >= {min_val}")
if max_val is not None and val > max_val:
raise ValueError(f"Must be <= {max_val}")
return val
except ValueError as e:
print(f" Invalid: {e}. Try again.")
age = get_int("Enter age (1-120): ", 1, 120)
print(f"Your age: {age}")
Enter age (1-120): abc
Invalid: invalid literal... Try again.
Enter age (1-120): -5
Invalid: Must be >= 1. Try again.
Enter age (1-120): 25
Your age: 25
CHAPTER 09
Object-Oriented Python
Classes, inheritance & dunder methods
■ Quick Summary
■ class defines a blueprint; objects are instances of a class
■ __init__ is the constructor — runs when you create an object
■ self refers to the current instance
■ Inheritance: class Child(Parent) — reuse and extend
■ Dunder methods (__str__, __len__, __add__) customise Python operators
■ @property turns a method into a readable attribute
Classes — Complete Example
■ classes_full.py — Student class
class Student:
school = "Python Academy" # class attribute (shared)
def __init__(self, name, grades):
[Link] = name
[Link] = grades
@property
def average(self):
return sum([Link]) / len([Link])
@property
def grade_letter(self):
avg = [Link]
return "A" if avg>=90 else "B" if avg>=80 else "C" if avg>=70 else "F"
def __str__(self):
return f"{[Link]} | avg: {[Link]:.1f} | {self.grade_letter}"
def __repr__(self):
return f"Student({[Link]!r}, {[Link]})"
alice = Student("Alice", [92, 88, 95, 90])
bob = Student("Bob", [72, 68, 75, 80])
print(alice) # uses __str__
print([Link]) # property — no ()
print([Link])
Alice | avg: 91.2 | A
91.25
Python Academy
Inheritance — Shape Hierarchy
■ [Link]
import math
class Shape:
def area(self): raise NotImplementedError
def perimeter(self): raise NotImplementedError
def describe(self):
print(f"{type(self).__name__}: area={[Link]():.2f}, "
f"perimeter={[Link]():.2f}")
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return [Link] * self.r**2
def perimeter(self): return 2 * [Link] * self.r
class Rectangle(Shape):
def __init__(self, w, h): self.w=w; self.h=h
def area(self): return self.w * self.h
def perimeter(self): return 2*(self.w+self.h)
shapes = [Circle(5), Rectangle(4, 6)]
for s in shapes:
[Link]()
Circle: area=78.54, perimeter=31.42
Rectangle: area=24.00, perimeter=20.00
Dunder Methods — Vector Class
■ [Link]
class Vector:
def __init__(self, x, y):
self.x = x; self.y = y
def __add__(self, other):
return Vector(self.x+other.x, self.y+other.y)
def __mul__(self, scalar):
return Vector(self.x*scalar, self.y*scalar)
def __abs__(self):
return (self.x**2+self.y**2)**0.5
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(1, 4)
print(v1 + v2) # Vector(3, 7)
print(v1 * 3) # Vector(6, 9)
print(abs(v1)) # 3.605...
Vector(3, 7)
Vector(6, 9)
3.605551275
CHAPTER 10
Modules & the Standard Library
os, math, random, datetime, collections
■ Quick Summary
■ import module or from module import name
■ math: sqrt, ceil, floor, pi, factorial, log, trig functions
■ random: randint, choice, shuffle, sample, random
■ datetime: today, now, timedelta, strftime
■ os/pathlib: file/directory operations
■ collections: Counter, defaultdict, deque, namedtuple
math Module
■ math_module.py
import math
print([Link]) # 3.14159...
print(math.e) # 2.71828...
print([Link](144)) # 12.0
print([Link](10)) # 3628800
print([Link](1000, 10)) # 3.0
print([Link](4.2)) # 5
print([Link](4.9)) # 4
# Hypotenuse of a right triangle
print([Link](3, 4)) # 5.0
3.141592653589793
2.718281828459045
12.0
3628800
3.0
5
4
5.0
random Module — Dice & Card Games
■ random_module.py
import random
# Roll dice
def roll(sides=6, count=2):
return [[Link](1, sides) for _ in range(count)]
dice = roll(6, 2)
print(f"Rolled: {dice} Total: {sum(dice)}")
# Shuffle a deck
suits = ["♠","♥","♦","♣"]
ranks = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
deck = [f"{r}{s}" for s in suits for r in ranks]
[Link](deck)
hand = deck[:5]
print("Your hand:", " ".join(hand))
# Random password
import string
chars = string.ascii_letters + [Link] + "!@#$"
pwd = "".join([Link](chars, k=12))
print("Password:", pwd)
Rolled: [4, 6] Total: 10
Your hand: 7♠ Q♥ 3♦ A♣ 9♠
Password: xK3!mB9qPz#w
datetime Module
■ datetime_module.py
from datetime import datetime, timedelta, date
now = [Link]()
today = [Link]()
print(f"Now: {[Link]('%Y-%m-%d %H:%M:%S')}")
print(f"Today: {today}")
# Age calculator
birthday = date(2000, 6, 15)
age_days = ([Link]() - birthday).days
print(f"Age: {age_days // 365} years ({age_days} days)")
# Deadline in 30 days
deadline = [Link]() + timedelta(days=30)
print(f"Deadline: {[Link]('%B %d, %Y')}")
Now: 2026-05-24 14:32:11
Today: 2026-05-24
Age: 25 years (9474 days)
Deadline: June 23, 2026
collections Module
■ collections_module.py
from collections import Counter, defaultdict, deque
# Counter — frequency analysis
words = "the quick brown fox jumps over the lazy dog the".split()
freq = Counter(words)
print(freq.most_common(3))
# defaultdict — no KeyError on missing keys
groups = defaultdict(list)
students = [("Math","Alice"),("Science","Bob"),("Math","Carol")]
for subject, name in students:
groups[subject].append(name)
print(dict(groups))
# deque — efficient queue
q = deque(["task1","task2","task3"])
[Link]("urgent")
print([Link]()) # urgent
[('the', 3), ('quick', 1), ('brown', 1)]
{'Math': ['Alice', 'Carol'], 'Science': ['Bob']}
urgent
CHAPTER 11
Advanced Functions
Decorators, generators, iterators & closures
■ Quick Summary
■ Closures: inner functions that remember their enclosing scope
■ Decorators: functions that wrap other functions to add behaviour
■ Generators: use yield to produce values one at a time (memory-efficient)
■ Iterators: any object with __iter__ and __next__
■ @[Link] preserves the wrapped function's metadata
Closures
■ [Link]
# A closure remembers the variable from its outer scope
def make_multiplier(factor):
def multiply(x):
return x * factor # "factor" is remembered
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(10)) # 20
print(triple(10)) # 30
# Closure as a counter
def make_counter(start=0):
count = [start] # mutable so we can modify it
def counter():
count[0] += 1
return count[0]
return counter
c = make_counter()
print(c(), c(), c()) # 1 2 3
20
30
1 2 3
Decorators
■ [Link]
import time, functools
def timer(func):
"""Measure and print execution time."""
@[Link](func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} took {end-start:.4f}s")
return result
return wrapper
@timer
def slow_sum(n):
return sum(range(n))
print(slow_sum(1_000_000))
slow_sum took 0.0231s
499999500000
■ decorator_with_args.py — access control
def require_role(role):
def decorator(func):
@[Link](func)
def wrapper(user, *args, **kwargs):
if [Link]("role") != role:
raise PermissionError(f"Requires role: {role}")
return func(user, *args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def delete_user(user, target):
return f"{user['name']} deleted {target}"
admin = {"name": "Alice", "role": "admin"}
guest = {"name": "Bob", "role": "guest"}
print(delete_user(admin, "Dave"))
try:
delete_user(guest, "Dave")
except PermissionError as e:
print(f"Blocked: {e}")
Alice deleted Dave
Blocked: Requires role: admin
Generators
■ [Link]
# Generator function — uses yield
def fibonacci(limit):
a, b = 0, 1
while a < limit:
yield a
a, b = b, a + b
print(list(fibonacci(100)))
# Generator expression (like list comp but lazy)
squares = (x**2 for x in range(1_000_000)) # no memory used yet!
print(next(squares)) # 0
print(next(squares)) # 1
# Infinite counter generator
def counter(start=0, step=1):
n = start
while True:
yield n
n += step
evens = counter(0, 2)
print([next(evens) for _ in range(5)]) # [0,2,4,6,8]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
0
1
[0, 2, 4, 6, 8]
CHAPTER 12
Comprehensions & Functional Tools
zip, map, filter, walrus operator
■ Quick Summary
■ List comp: [expr for x in iterable if condition]
■ Dict comp: {k: v for k, v in items}
■ Set comp: {expr for x in iterable}
■ zip pairs iterables element-by-element
■ map/filter are lazy — wrap in list() to see all values
■ Walrus operator := assigns and returns in one expression
Comprehensions — All Forms
■ [Link]
# List comprehension with condition
primes = [n for n in range(2, 50)
if all(n % d != 0 for d in range(2, n))]
print(primes)
# Dict comprehension — invert a dictionary
country_code = {"US":"United States","GB":"United Kingdom","DE":"Germany"}
code_country = {v: k for k, v in country_code.items()}
print(code_country["Germany"]) # DE
# Set comprehension — unique vowels
sentence = "the quick brown fox"
vowels = {ch for ch in sentence if ch in "aeiou"}
print(sorted(vowels))
# Nested comprehension — multiplication table
table = [[r*c for c in range(1,6)] for r in range(1,6)]
for row in table:
print(row)
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
DE
['e','i','o','u']
[1,2,3,4,5]
[2,4,6,8,10]
...
zip, map, filter
■ functional_tools.py
# zip — combine two lists
names = ["Alice","Bob","Carol"]
scores = [92, 78, 88]
grades = ["A","C","B"]
report = list(zip(names, scores, grades))
for name, score, grade in report:
print(f" {name:8} {score} {grade}")
# map — apply function to every item
temps_c = [0, 20, 37, 100]
temps_f = list(map(lambda c: c*9/5+32, temps_c))
print(temps_f)
# filter — keep items matching condition
words = ["python","java","go","rust","javascript","c"]
long = list(filter(lambda w: len(w) > 4, words))
print(long)
Alice 92 A
Bob 78 C
Carol 88 B
[32.0, 68.0, 98.6, 212.0]
['python', 'javascript']
Walrus Operator :=
■ [Link]
# Without walrus — call len() twice
data = [1,2,3,4,5,6,7,8,9,10]
if len(data) > 5:
print(f"Long list: {len(data)} items")
# With walrus — compute once, use twice
if (n := len(data)) > 5:
print(f"Long list: {n} items")
# Walrus in while loop — process lines
import io
stream = [Link]("line1\nline2\nline3\n")
while chunk := [Link]():
print([Link]())
Long list: 10 items
Long list: 10 items
line1
line2
line3
CHAPTER 13
Practical Projects
Number game, password gen, data analyser, OOP bank
Project 1 — Advanced Number Guessing Game
■ number_game.py
import random
def play_game(max_num=100, max_attempts=7):
secret = [Link](1, max_num)
attempts = []
print(f"Guess a number between 1 and {max_num}.")
print(f"You have {max_attempts} attempts.\n")
for attempt in range(1, max_attempts + 1):
remaining = max_attempts - attempt
guess = int(input(f"Attempt {attempt}: "))
[Link](guess)
if guess == secret:
print(f"Correct! You won in {attempt} attempt(s)!")
return True, attempts
elif abs(guess - secret) <= 5:
hint = "Very warm" if guess < secret else "Very warm"
elif abs(guess - secret) <= 15:
hint = "Warm"
else:
hint = "Cold"
direction = "higher" if guess < secret else "lower"
print(f" {hint}! Go {direction}. {remaining} left.")
print(f"Game over! The number was {secret}.")
return False, attempts
play_game()
Guess a number 1-100. You have 7 attempts.
Attempt 1: 50
Cold! Go higher. 6 left.
Attempt 2: 75
Warm! Go lower. 5 left.
...
Project 2 — Password Generator
■ password_generator.py
import random, string
def generate_password(length=16, use_upper=True,
use_digits=True, use_symbols=True):
pool = string.ascii_lowercase
must_have = [[Link](string.ascii_lowercase)]
if use_upper:
pool += string.ascii_uppercase
must_have.append([Link](string.ascii_uppercase))
if use_digits:
pool += [Link]
must_have.append([Link]([Link]))
if use_symbols:
pool += "!@#$%^&*"
must_have.append([Link]("!@#$%^&*"))
remaining = [[Link](pool) for _ in range(length-len(must_have))]
password = must_have + remaining
[Link](password)
return "".join(password)
for i in range(5):
print(generate_password(16))
xK3!mB9qPz#wRt2Y
Aj@7vNpL!cQm3Xs9
...
Project 3 — Student Data Analyser
■ data_analyser.py
def analyse_grades(students):
"""Analyse a list of (name, grades_list) tuples."""
results = []
for name, grades in students:
avg = sum(grades) / len(grades)
[Link]({
"name": name,
"average": round(avg, 1),
"highest": max(grades),
"lowest": min(grades),
"grade": "A" if avg>=90 else "B" if avg>=80 else "C" if avg>=70 else "F
"
})
[Link](key=lambda r: r["average"], reverse=True)
print(f"{"NAME":<12}{"AVG":>6}{"HIGH":>6}{"LOW":>6} GRADE")
print("-" * 38)
for r in results:
print(f"{r['name']:<12}{r['average']:>6}{r['highest']:>6}{r['lowest']:>6} {r
['grade']}")
all_avgs = [r["average"] for r in results]
print(f"\nClass average: {sum(all_avgs)/len(all_avgs):.1f}")
data = [
("Alice", [92,88,95,91]), ("Bob", [72,68,75,80]),
("Carol", [88,92,85,90]), ("Dave", [60,55,70,65]),
]
analyse_grades(data)
NAME AVG HIGH LOW GRADE
--------------------------------------
Carol 88.8 92 85 B
Alice 91.5 95 88 A
Bob 73.8 80 68 C
Dave 62.5 70 55 F
Class average: 79.1
Project 4 — OOP Bank Account System
■ bank_system.py
from datetime import datetime
class Transaction:
def __init__(self, kind, amount, balance):
[Link] = kind
[Link] = amount
[Link] = balance
[Link] = [Link]().strftime("%H:%M:%S")
def __str__(self):
return f"[{[Link]}] {[Link]} ${[Link]:>8.2f} bal=${[Link]
e:.2f}"
class BankAccount:
def __init__(self, owner, balance=0):
[Link] = owner
self._balance = balance
self._history = []
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount <= 0: raise ValueError("Deposit must be positive")
self._balance += amount
self._history.append(Transaction("DEPOSIT", amount, self._balance))
def withdraw(self, amount):
if amount > self._balance: raise ValueError("Insufficient funds")
self._balance -= amount
self._history.append(Transaction("WITHDRAW", amount, self._balance))
def statement(self):
print(f"\n=== {[Link]}'s Account ===")
for t in self._history: print(t)
print(f"Current balance: ${self._balance:.2f}")
acc = BankAccount("Alice", 1000)
[Link](500)
[Link](200)
[Link](1200)
[Link]()
=== Alice's Account ===
[14:32:01] DEPOSIT $ 500.00 bal=$1500.00
[14:32:01] WITHDRAW $ 200.00 bal=$1300.00
[14:32:01] DEPOSIT $ 1200.00 bal=$2500.00
Current balance: $2500.00
■ Congratulations — You've Completed the Guide!
You now know variables, control flow, functions, OOP, modules, decorators, generators,
comprehensions, and file handling. Keep building — every project makes you stronger.
Next steps: explore pandas, Flask/Django, pytest, asyncio, and type hints. Practice daily on
[Link] or [Link].