Introduction to Python
Python is the world's most popular programming language. It's readable, versatile, and used everywhere โ from web apps and data science to AI, automation, and embedded systems. This is where your journey begins.
- Why Python is the best first language
- Python's history and the Zen of Python
- Where Python is used in the real world
- Write and understand your first program
- Use Python's print() and basic math
- Understand comments and code style
Python was created by Guido van Rossum in 1991 (named after Monty Python, not the snake!). Its philosophy is simple: code should be readable and elegant. Python uses English-like keywords and enforces clean indentation, making programs look almost like plain English.
Python dominates many industries: AI/ML (TensorFlow, PyTorch), Web Development (Django, Flask, FastAPI), Data Science (NumPy, Pandas), Automation, Cybersecurity, and even Space exploration (NASA uses Python).
Every programming language starts with "Hello, World!" โ but Python's version is just one line. Notice how clean and readable it is compared to Java or C++.
# This is a comment โ Python ignores lines starting with # # The print() function outputs text to the console print("Hello, World!") print("Welcome to Aawiskar e-Siksha!") # Python can do math directly print(2 + 2) # โ 4 print(10 ** 3) # โ 1000 (** is power/exponent) print(17 % 5) # โ 2 (% is remainder/modulo) # Variables store data for later use name = "Pythonista" year = 2025 print(f"Hello {name}! Python in {year}.") # f-string! # The Zen of Python โ type this in IDLE: # import this
import this in Python and read the 19 guiding principles โ "Beautiful is better than ugly", "Readability counts", "Errors should never pass silently". These define Python's philosophy.Setup & First Program
Get Python running on your computer and understand the different ways to write and run Python code. We'll cover installation, the REPL, scripts, and VS Code setup.
- Install Python 3 on Windows/Mac/Linux
- Use the Python REPL (interactive mode)
- Create and run .py script files
- Set up VS Code with Python extension
- Understand pip (package manager)
- Run Python via terminal/command prompt
Step 1 โ Install: Download Python 3 from python.org. On Windows, check "Add Python to PATH" during install. On Mac: use brew install python3. On Ubuntu: sudo apt install python3.
Step 2 โ Verify: Open a terminal and type python3 --version. You should see something like Python 3.12.0.
Step 3 โ REPL: Type python3 in terminal to enter interactive mode. The >>> prompt means Python is waiting for your code. Type exit() to leave.
# Check Python version $ python3 --version Python 3.12.0 # Start interactive mode (REPL) $ python3 >>> print("Hello!") Hello! >>> 2 + 2 4 >>> exit() # Run a script file $ python3 hello.py # Install a package with pip $ pip3 install requests
# Save this as hello.py and run: python3 hello.py import sys print("=== My First Python Script ===") print(f"Python version: {sys.version_info[0]}.{sys.version_info[1]}") print("Everything is working!") # Python reads top-to-bottom, like a recipe print("This runs first") print("Then this") print("Finally this")
Variables & Data Types
Variables are named containers that store data. Python is dynamically typed โ you never declare the type, Python figures it out automatically. Understanding types is fundamental to everything that follows.
- Create variables and assign values
- The 5 basic data types: int, float, str, bool, None
- Use type() to inspect any variable
- Convert between types (type casting)
- Variable naming rules and conventions
- Multiple assignment and variable swapping
Python has 5 basic data types. Everything in Python is an object, and each type has specific behaviors and methods available to it.
| Type | Example | Description |
|---|---|---|
| int | 42, -7, 0, 1_000_000 | Whole numbers (no decimal) |
| float | 3.14, -0.5, 1e10 | Numbers with decimal points |
| str | "hello", 'Python', """multi""" | Text, any characters |
| bool | True, False | Boolean โ only two values |
| NoneType | None | Represents "nothing" / absence of value |
# Integer โ whole numbers age = 18 score = -42 population = 1_400_000_000 # underscores improve readability # Float โ decimal numbers pi = 3.14159 gravity = 9.81 tiny = 1.5e-10 # scientific notation: 0.00000000015 # String โ text (single, double, or triple quotes) name = "Aawiskar" city = 'Kathmandu' bio = """I am a student learning Python at Aawiskar.""" # Boolean โ only True or False (capital!) is_student = True has_wifi = False # None โ absence of a value result = None # type() tells you the type of any value print(type(age)) # <class 'int'> print(type(pi)) # <class 'float'> print(type(name)) # <class 'str'> print(type(is_student)) # <class 'bool'> # Type conversion (casting) x = int("42") # "42" โ 42 y = float(10) # 10 โ 10.0 z = str(3.14) # 3.14 โ "3.14" b = bool(0) # 0 โ False (0 is falsy!) b2 = bool("hello") # "hello" โ True (non-empty is truthy) # Multiple assignment a, b, c = 1, 2, 3 x = y = z = 0 # all three = 0 # Variable swap (Python magic!) a, b = b, a # no temp variable needed! print(f"a={a}, b={b}")
snake_case. Python is case-sensitive: Name and name are different variables. Avoid reserved words like if, for, print.Numbers & Math
Python is a powerful calculator. Master all arithmetic operations, understand integer division vs true division, use the math module for advanced functions, and learn about Python's arbitrary precision integers.
| Operator | Name | Example | Result |
|---|---|---|---|
| + | Addition | 10 + 3 | 13 |
| - | Subtraction | 10 - 3 | 7 |
| * | Multiplication | 10 * 3 | 30 |
| / | True Division | 10 / 3 | 3.3333... |
| // | Floor Division | 10 // 3 | 3 |
| % | Modulo (Remainder) | 10 % 3 | 1 |
| ** | Power/Exponent | 10 ** 3 | 1000 |
# Basic operations print(15 + 7) # 22 print(15 - 7) # 8 print(15 * 7) # 105 print(15 / 7) # 2.142857... (always float!) print(15 // 7) # 2 (floor division, chops decimal) print(15 % 7) # 1 (15 = 2*7 + 1, so remainder is 1) print(2 ** 10) # 1024 # Order of operations โ PEMDAS applies! result = 2 + 3 * 4 # 14, not 20 (multiplication first) result2 = (2 + 3) * 4 # 20 (brackets override) # Practical uses of modulo print(10 % 2 == 0) # True โ 10 is even print(7 % 2 == 0) # False โ 7 is odd # Built-in math functions print(abs(-42)) # 42 (absolute value) print(round(3.7)) # 4 print(round(3.14159, 2)) # 3.14 print(max(3, 7, 2)) # 7 print(min(3, 7, 2)) # 2 print(pow(2, 8)) # 256 (same as 2**8) # math module for advanced operations import math print(math.sqrt(144)) # 12.0 print(math.pi) # 3.141592653589793 print(math.floor(3.9)) # 3 (round down) print(math.ceil(3.1)) # 4 (round up)
Strings Deep Dive
Strings are sequences of characters. Python's string system is incredibly powerful โ slicing, indexing, 30+ methods, f-strings, and more. Almost every real program uses strings extensively.
A string is a sequence of characters. You can access individual characters using indexing (starting at 0) and extract portions using slicing with [start:stop:step].
# Creating strings s1 = "Hello, World!" s2 = 'Single quotes work too' s3 = """Triple quotes span multiple lines and preserve line breaks.""" # Indexing: positions start at 0 word = "Python" # P y t h o n # 0 1 2 3 4 5 (positive) # -6 -5 -4 -3 -2 -1 (negative) print(word[0]) # P print(word[-1]) # n (last character) print(word[1:4]) # yth (indices 1,2,3) print(word[:3]) # Pyt (from start) print(word[3:]) # hon (to end) print(word[::2]) # Pto (every 2nd character) print(word[::-1]) # nohtyP (reversed!) # String operations print("Hello" + " World") # concatenation print("Ha" * 3) # HaHaHa print(len("Python")) # 6 print("py" in "Python") # False (case-sensitive) print("Py" in "Python") # True # Essential string METHODS (strings are immutable โ methods return new strings) text = " Hello, Python World! " print(text.strip()) # "Hello, Python World!" (remove whitespace) print(text.upper()) # " HELLO, PYTHON WORLD! " print(text.lower()) # " hello, python world! " print(text.title()) # " Hello, Python World! " (Title Case) print(text.replace("Python", "Aawiskar")) print(text.count("o")) # 2 print(text.find("Python")) # index of first match, or -1 print(text.split(",")) # split into list print(text.startswith(" H")) # True print(text.endswith("! ")) # True # F-STRINGS โ the modern way to embed variables name = "Sita" score = 95.5 print(f"Student: {name}, Score: {score:.1f}%") # 95.5 โ 95.5% print(f"2 to the 10th: {2**10}") # expressions work! print(f"Name: {name.upper()!r}") # methods work too # Escape characters print("Line1\nLine2\nLine3") # \n = newline print("Tab\there") # \t = tab print("She said \"hi\"") # \" = literal quote print(r"C:\new\file") # raw string โ no escape
All Operators
Python has 7 categories of operators. Beyond basic arithmetic, you'll use comparison, logical, assignment, identity, membership, and bitwise operators constantly in real programs.
# COMPARISON OPERATORS โ always return True or False x, y = 10, 20 print(x == y) # False (equal to) print(x != y) # True (not equal) print(x < y) # True (less than) print(x <= y) # True (less than or equal) print(x > y) # False print(x >= y) # False # LOGICAL OPERATORS print(True and False) # False (both must be True) print(True or False) # True (at least one True) print(not True) # False (inverts) # Chaining comparisons (Python unique!) age = 25 print(18 <= age <= 60) # True (reads like math!) # ASSIGNMENT OPERATORS (shortcuts) n = 10 n += 5 # same as: n = n + 5 โ 15 n -= 3 # same as: n = n - 3 โ 12 n *= 2 # โ 24 n //= 5 # โ 4 n **= 3 # โ 64 print(n) # IDENTITY OPERATORS a = [1, 2, 3] b = a # b points to SAME object as a c = [1, 2, 3] # c is a DIFFERENT object with same values print(a == c) # True (same values) print(a is c) # False (different objects in memory!) print(a is b) # True (same object) # MEMBERSHIP OPERATORS fruits = ["apple", "banana", "cherry"] print("banana" in fruits) # True print("mango" not in fruits) # True print("Py" in "Python") # True (works with strings too) # WALRUS OPERATOR := (Python 3.8+) # Assigns AND returns a value in one step if (n := len(fruits)) > 2: print(f"Long list: {n} items") # Long list: 3 items
Control Flow
Control flow makes programs intelligent โ deciding WHICH code runs based on conditions. Python's indentation-based syntax makes it crystal clear. This is the foundation of all program logic.
# Basic if/elif/else marks = 82 if marks >= 90: print("A+ โ Distinction!") elif marks >= 75: print("A โ Excellent!") # this runs elif marks >= 60: print("B โ Good") elif marks >= 40: print("C โ Pass") else: print("F โ Fail. Try again!") # Compound conditions age = 19 has_license = True has_car = False if age >= 18 and has_license: print("Can drive legally") if has_car or age >= 18: print("Has some independence") # TERNARY OPERATOR โ one-liner if/else status = "Adult" if age >= 18 else "Minor" result = "Pass" if marks >= 40 else "Fail" # TRUTHY and FALSY values โ Python's hidden feature # These are ALL falsy (evaluate to False in conditions): falsy_values = [0, 0.0, "", [], {}, (), None, False] # EVERYTHING ELSE is truthy! name = "" if name: # falsy if empty string print(f"Hi {name}") else: print("Name is empty!") # Nested if x = 15 if x > 0: if x % 2 == 0: print("Positive even") else: print("Positive odd") # prints this # match/case (Python 3.10+) โ like switch/case day = "Monday" match day: case "Saturday" | "Sunday": print("Weekend!") case "Monday": print("Back to school") # prints this case _: print("Weekday")
While Loops
A while loop repeats code as long as a condition remains True. Master break, continue, and while-else to write powerful, controlled loops that handle real-world repetition scenarios.
# Basic while loop โ countdown count = 5 while count > 0: print(f"T-minus {count}") count -= 1 # ALWAYS update the condition variable! print("Blastoff! ๐") # break โ exit the loop immediately while True: # infinite loop โ needs break! answer = input("Type 'quit' to exit: ") if answer == "quit": break print(f"You typed: {answer}") print("Loop exited") # continue โ skip this iteration, go to next i = 0 while i < 10: i += 1 if i % 2 == 0: # skip even numbers continue print(i, end=" ") # 1 3 5 7 9 # while-else โ else runs only if loop completes without break num = 7 divisor = 2 while divisor < num: if num % divisor == 0: print(f"{num} is NOT prime (divisible by {divisor})") break divisor += 1 else: print(f"{num} IS prime!") # this runs since no break # Accumulator pattern โ common while loop use total = 0 count = 0 while count < 5: total += count * count # sum of squares: 0+1+4+9+16 count += 1 print(f"Sum of squares 0-4: {total}") # 30
For Loops & Range
For loops iterate over sequences โ lists, strings, ranges, and more. Mastering for loops with enumerate(), zip(), and range() unlocks clean, Pythonic iteration patterns.
# range(stop) โ 0 to stop-1 for i in range(5): print(i, end=" ") # 0 1 2 3 4 # range(start, stop, step) for i in range(1, 11, 2): # odd numbers 1-9 print(i, end=" ") for i in range(10, 0, -2): # count down by 2 print(i, end=" ") # Iterating over sequences fruits = ["apple", "banana", "cherry"] for fruit in fruits: print(f"I like {fruit}") for letter in "Python": # iterate over string characters print(letter, end="-") # enumerate() โ get index AND value together for i, fruit in enumerate(fruits): print(f"{i+1}. {fruit}") # 1. apple, 2. banana, 3. cherry # enumerate with custom start index for i, fruit in enumerate(fruits, start=1): print(f"{i}. {fruit}") # zip() โ iterate multiple lists together names = ["Alice", "Bob", "Carol"] scores = [85, 92, 78] for name, score in zip(names, scores): print(f"{name}: {score}") # Nested loops โ multiplication table for i in range(1, 6): for j in range(1, 6): print(f"{i*j:3}", end="") # :3 = minimum width 3 print() # for-else โ else runs when loop completes without break target = 7 numbers = [1, 4, 8, 2] for n in numbers: if n == target: print(f"Found {target}!") break else: print(f"{target} not in list") # this runs # _ as throwaway variable (common pattern) for _ in range(3): print("Repeated 3 times")
Functions Basics
Functions are the building blocks of clean code. Define once, use everywhere. Learn parameters, return values, default arguments, *args, **kwargs, and writing great docstrings.
# Basic function def greet(name): """Return a greeting message.""" # docstring return f"Hello, {name}!" print(greet("Aarav")) # Hello, Aarav! print(greet("Sita")) # Hello, Sita! # Default parameters โ used when argument not provided def power(base, exp=2): return base ** exp print(power(3)) # 9 (exp defaults to 2) print(power(2, 10)) # 1024 # Multiple return values (returns a tuple) def min_max(numbers): return min(numbers), max(numbers) lo, hi = min_max([3, 7, 1, 9, 2]) print(f"Min: {lo}, Max: {hi}") # Min: 1, Max: 9 # *args โ variable number of positional arguments def add_all(*numbers): return sum(numbers) print(add_all(1, 2, 3)) # 6 print(add_all(10, 20, 30, 40)) # 100 # **kwargs โ variable keyword arguments (dict) def create_profile(**info): for key, val in info.items(): print(f" {key}: {val}") print("Profile:") create_profile(name="Aarav", age=18, city="Kathmandu") # Keyword arguments (order doesn't matter) def describe(name, subject, score): print(f"{name} scored {score} in {subject}") describe(score=95, name="Sita", subject="Python") # Functions as objects (first-class functions) def apply(func, value): return func(value) print(apply(str, 42)) # "42" print(apply(abs, -7)) # 7
Scope & Closures
Scope determines where variables are accessible. The LEGB rule โ Local, Enclosing, Global, Built-in โ explains Python's variable lookup order. Closures capture variables from outer functions.
x = "global" # Global scope def outer(): x = "enclosing" # Enclosing scope def inner(): x = "local" # Local scope print(x) # "local" (L wins) inner() print(x) # "enclosing" outer() print(x) # "global" # global keyword โ modify global variable inside function count = 0 def increment(): global count count += 1 increment(); increment() print(count) # 2 # CLOSURE โ inner function remembers outer variables def make_multiplier(n): def multiplier(x): return x * n # n is "captured" from outer scope return multiplier # return the inner function! double = make_multiplier(2) triple = make_multiplier(3) print(double(5)) # 10 print(triple(5)) # 15 # Counter using closure def make_counter(): count = [0] # list trick to allow mutation def counter(): count[0] += 1 return count[0] return counter c = make_counter() print(c(), c(), c()) # 1 2 3
Lists Basics
Lists are Python's most versatile data structure โ ordered, mutable, allow duplicates, and can hold mixed types. Master creating, accessing, slicing, and modifying lists.
A list is an ordered collection enclosed in square brackets []. Items are accessed by index (starts at 0). Negative indices count from the end.
# Creating lists fruits = ["apple", "banana", "cherry"] nums = [10, 20, 30, 40, 50] mixed = [1, "hello", 3.14, True] # mixed types OK # Indexing print(fruits[0]) # "apple" (first) print(fruits[2]) # "cherry" (third) print(fruits[-1]) # "cherry" (last) print(fruits[-2]) # "banana" (second from end) # Slicing [start:stop:step] print(nums[1:4]) # [20, 30, 40] print(nums[:3]) # [10, 20, 30] (from start) print(nums[2:]) # [30, 40, 50] (to end) print(nums[::2]) # [10, 30, 50] (every other) print(nums[::-1]) # [50, 40, 30, 20, 10] (reversed!) # Modifying (lists are mutable) fruits[1] = "mango" print(fruits) # ["apple", "mango", "cherry"] # Useful built-ins print(len(nums)) # 5 print(min(nums)) # 10 print(max(nums)) # 50 print(sum(nums)) # 150 print(30 in nums) # True (membership test)
List Methods
Python lists come with powerful built-in methods for adding, removing, sorting, and transforming elements. These are the workhorses of everyday Python coding.
nums = [3, 1, 4, 1, 5] # Adding elements nums.append(9) # [3,1,4,1,5,9] โ add to end nums.insert(0, 0) # [0,3,1,4,1,5,9] โ insert at index nums.extend([2, 6]) # adds multiple items # Removing elements nums.remove(1) # removes FIRST occurrence of 1 popped = nums.pop() # removes & returns last item popped2 = nums.pop(0) # removes & returns item at index 0 nums.clear() # empties the list [] # Searching colors = ["red", "green", "blue", "green"] print(colors.index("green")) # 1 (first occurrence) print(colors.count("green")) # 2 (how many times) # Sorting scores = [42, 15, 78, 3, 99] scores.sort() # ascending in-place scores.sort(reverse=True) # descending in-place original = sorted(scores) # returns NEW sorted list scores.reverse() # reverses in-place # Copying (avoid aliasing!) a = [1, 2, 3] b = a # ALIAS! b is the same object as a c = a.copy() # SAFE copy d = a[:] # also a safe copy via slice b.append(99) # this ALSO modifies a! print(a) # [1, 2, 3, 99] โ surprise! print(c) # [1, 2, 3] โ unchanged
Tuples & Packing
Tuples are immutable sequences โ once created, their values cannot change. They are faster than lists and used for data that should not be modified: coordinates, RGB colors, database records.
# Creating tuples โ parentheses (optional but conventional) point = (3, 7) rgb = (255, 128, 0) single = (42,) # trailing comma needed for 1-element tuple! also = 1, 2, 3 # parentheses optional # Accessing (same as lists โ indexing, slicing) print(point[0]) # 3 print(rgb[-1]) # 0 # TUPLE UNPACKING โ pythonic and powerful x, y = point print(x, y) # 3 7 r, g, b = rgb print(f"Red={r} Green={g} Blue={b}") # Swap variables without temp variable! a, b = 10, 20 a, b = b, a print(a, b) # 20 10 # Extended unpacking with * first, *rest = (1, 2, 3, 4, 5) print(first) # 1 print(rest) # [2, 3, 4, 5] *head, last = (1, 2, 3, 4, 5) print(head, last) # [1,2,3,4] 5 # Tuples are immutable โ this would raise TypeError: # point[0] = 99 # TypeError: 'tuple' object does not support item assignment # But tuples can contain mutable objects: t = ([1,2], [3,4]) t[0].append(99) # this works! modifying the list inside print(t) # ([1, 2, 99], [3, 4])
Sets & Operations
Sets are unordered collections of unique elements. They are blazingly fast for membership testing and excel at mathematical set operations: union, intersection, difference.
# Creating sets s = {1, 2, 3, 3, 2} # duplicates removed automatically print(s) # {1, 2, 3} empty = set() # empty set โ {} creates a dict! from_list = set([1,2,2,3]) # de-duplicate a list! # Add / Remove s.add(4) s.discard(99) # safe โ no error if missing s.remove(1) # raises KeyError if missing # Set math operations a = {1, 2, 3, 4} b = {3, 4, 5, 6} print(a | b) # Union: {1,2,3,4,5,6} print(a & b) # Intersection: {3,4} print(a - b) # Difference: {1,2} (in a, not in b) print(a ^ b) # Symmetric diff: {1,2,5,6} # Subset / superset checks print({1,2}.issubset(a)) # True print(a.issuperset({1,2})) # True # Ultra-fast membership testing big = set(range(1000000)) print(999999 in big) # True โ instant! # frozenset โ immutable set (hashable, usable as dict key) fs = frozenset({1, 2, 3})
Dictionaries Deep Dive
Dictionaries are key-value stores โ the most used data structure in real-world Python. Config files, JSON APIs, database records, and caches all map naturally to dicts.
# Creating dictionaries person = {"name": "Aarav", "age": 22, "city": "Kathmandu"} empty = {} via_func = dict(name="Sita", age=25) # Accessing values print(person["name"]) # "Aarav" print(person.get("age")) # 22 (safe โ no KeyError) print(person.get("phone", "N/A")) # "N/A" (default) # Adding / Updating person["email"] = "aarav@example.com" person["age"] = 23 # update existing person.update({"city": "Pokhara", "phone": "9841..."}) # Removing removed = person.pop("email") # returns value, removes key person.popitem() # removes last inserted pair # Iterating for key in person: print(key, "->", person[key]) for key, val in person.items(): # pythonic! print(f" {key}: {val}") print(list(person.keys())) # all keys print(list(person.values())) # all values # setdefault โ set a key only if it doesn't exist person.setdefault("country", "Nepal") print(person["country"]) # Nepal # Dict comprehension squares = {x: x**2 for x in range(1, 6)} print(squares) # {1:1, 2:4, 3:9, 4:16, 5:25}
Nested Data Structures
Real-world data is hierarchical โ APIs return JSON with nested dicts and lists, databases have tables of records. Learn to navigate complex nested structures confidently.
# List of dicts โ classic "table" pattern students = [ {"name": "Aarav", "grade": 88, "subjects": ["Math", "Python"]}, {"name": "Sita", "grade": 95, "subjects": ["Science", "Python"]}, {"name": "Raju", "grade": 72, "subjects": ["History"]}, ] # Access nested data print(students[0]["name"]) # Aarav print(students[1]["subjects"][0]) # Science # Iterate and process for s in students: print(f"{s['name']}: {s['grade']} โ {', '.join(s['subjects'])}") # Filter with comprehension top = [s["name"] for s in students if s["grade"] >= 90] print("Top students:", top) # Sort by grade descending ranked = sorted(students, key=lambda s: s["grade"], reverse=True) print("Ranked:", [s["name"] for s in ranked]) # Dict of lists courses = {"Math": ["Aarav", "Sita"], "Science": ["Sita", "Raju"]} print(courses["Math"][1]) # Sita # Safe access with .get() on nested dicts config = {"db": {"host": "localhost", "port": 5432}} port = config.get("db", {}).get("port", 3306) print(f"Port: {port}") # 5432
Comprehensions & Generators
Comprehensions are Python's most elegant feature โ concise one-liners that replace for-loops. List, dict, set, and generator comprehensions make code shorter, faster, and more readable.
# LIST COMPREHENSION [expr for item in iterable if condition] squares = [x**2 for x in range(10)] evens = [x for x in range(20) if x % 2 == 0] upper = [w.upper() for w in ["hello", "world"]] flat = [n for row in [[1,2],[3,4]] for n in row] # flatten! print(squares[:5]) # [0, 1, 4, 9, 16] print(flat) # [1, 2, 3, 4] # Ternary in comprehension labels = ["even" if x%2==0 else "odd" for x in range(6)] print(labels) # ['even','odd','even','odd','even','odd'] # DICT COMPREHENSION {k: v for ...} word_len = {w: len(w) for w in ["cat", "elephant", "bee"]} inverted = {v: k for k, v in {"a":1, "b":2}.items()} print(word_len) # {'cat': 3, 'elephant': 8, 'bee': 3} # SET COMPREHENSION {expr for ...} unique_lens = {len(w) for w in ["cat", "dog", "elephant"]} print(unique_lens) # {3, 8} # GENERATOR EXPRESSION (lazy โ doesn't build list in memory) total = sum(x**2 for x in range(1000000)) # memory-efficient! gen = (x**2 for x in range(5)) print(next(gen)) # 0 (one at a time) print(next(gen)) # 1
String Formatting
Python provides multiple ways to format strings. f-strings (Python 3.6+) are the modern standard โ fast, readable, and powerful with format specifiers for alignment, precision, and number formatting.
name = "Aarav"; score = 87.654; items = 1234567 # Basic f-string print(f"Hello, {name}!") print(f"2+2 = {2+2}") # expressions inside {} print(f"{name.upper()}") # method calls work too # Precision and rounding print(f"{score:.2f}") # 87.65 (2 decimal places) print(f"{score:10.2f}") # " 87.65" (width 10) print(f"{score:.0f}") # 88 (rounded) # Number formatting print(f"{items:,}") # 1,234,567 (comma separator) print(f"{items:_}") # 1_234_567 print(f"{0.0012:.2e}") # 1.20e-03 (scientific) print(f"{255:#x}") # 0xff (hex) print(f"{0.85:.1%}") # 85.0% (percentage) # Alignment print(f"{'left':10}|") # "left |" print(f"{'right':>10}|") # " right|" print(f"{'center':^10}|") # " center |" print(f"{'fill':*^10}|") # "**fill****|" # Debug with = specifier (Python 3.8+) x = 42 print(f"{x=}") # x=42 (great for debugging!)
File Handling
Reading and writing files is essential for any real application โ logs, configs, data exports. Python makes file I/O clean and safe with the with statement and supports text, CSV, and JSON formats out of the box.
# WRITING a file with open("notes.txt", "w") as f: # "w" = write (overwrites) f.write("Line 1\n") f.write("Line 2\n") f.writelines(["Line 3\n", "Line 4\n"]) # file automatically closed after 'with' block # READING a file with open("notes.txt", "r") as f: # "r" = read content = f.read() # entire file as string print(content) with open("notes.txt") as f: lines = f.readlines() # list of lines (with \n) lines = [l.strip() for l in lines] # clean with open("notes.txt") as f: for line in f: # most memory-efficient print(line.strip()) # File modes: "r"=read, "w"=write, "a"=append, "rb"/"wb"=binary with open("notes.txt", "a") as f: # append f.write("Line 5\n") # Working with CSV import csv with open("data.csv", "w", newline="") as f: writer = csv.writer(f) writer.writerow(["Name", "Score"]) writer.writerows([["Alice", 95], ["Bob", 87]]) # Working with JSON import json data = {"users": ["Alice", "Bob"], "count": 2} with open("data.json", "w") as f: json.dump(data, f, indent=2) with open("data.json") as f: loaded = json.load(f) print(loaded["users"]) # ['Alice', 'Bob']
Error Handling
Errors happen. Professional code anticipates them. Python's try/except/else/finally structure lets you handle errors gracefully, show helpful messages, and keep programs running.
# Basic try/except try: x = int("abc") # raises ValueError except ValueError: print("Not a valid number!") # Catching multiple exception types try: result = 10 / 0 except (ZeroDivisionError, TypeError) as e: print(f"Error: {e}") # e is the exception object # Full structure with else and finally try: num = int("42") result = 100 / num except ValueError: print("Invalid number") except ZeroDivisionError: print("Cannot divide by zero") else: print(f"Success: {result}") # runs if NO exception finally: print("This ALWAYS runs") # cleanup code # Custom exceptions class AgeError(ValueError): pass def register(age): if age < 0 or age > 150: raise AgeError(f"Invalid age: {age}") print(f"Registered! Age: {age}") try: register(-5) except AgeError as e: print(f"Custom error caught: {e}") # Common built-in exceptions: # ValueError, TypeError, IndexError, KeyError, # FileNotFoundError, ZeroDivisionError, AttributeError
Modules & Imports
Modules let you organize code into reusable files and tap into Python's massive standard library. Understanding imports is key to writing clean, maintainable Python projects.
# Different import styles import math # full module from math import pi, sqrt # specific names from math import * # everything (avoid!) import random as rng # alias # math module print(math.pi) # 3.14159... print(sqrt(144)) # 12.0 print(math.floor(3.8)) # 3 print(math.ceil(3.1)) # 4 # random module print(rng.randint(1, 100)) # random integer 1-100 print(rng.choice(["a", "b", "c"])) # random element lst = [1,2,3,4,5]; rng.shuffle(lst); print(lst) # Creating your own module: save as myutils.py # def greet(name): return f"Hello, {name}!" # Then: from myutils import greet # __name__ guard โ code runs only when script is called directly if __name__ == "__main__": print("Running as main script") # Useful stdlib modules to know: # os, sys, pathlib, json, csv, re, datetime, # collections, itertools, functools, random, math import sys print(sys.version) print(sys.platform)
Classes & Objects
Object-Oriented Programming (OOP) lets you model real-world entities as objects with attributes (data) and methods (behavior). Classes are blueprints; objects are instances.
class Dog: # Class attribute โ shared by ALL instances species = "Canis familiaris" def __init__(self, name, breed, age): # constructor # Instance attributes โ unique per dog self.name = name self.breed = breed self.age = age self.tricks = [] def bark(self): # instance method return f"{self.name} says: Woof!" def learn_trick(self, trick): self.tricks.append(trick) return f"{self.name} learned {trick}!" def info(self): return f"{self.name} ({self.breed}, {self.age}yrs)" # Creating instances dog1 = Dog("Rex", "Labrador", 3) dog2 = Dog("Buddy", "Poodle", 5) print(dog1.bark()) # Rex says: Woof! print(dog2.info()) # Buddy (Poodle, 5yrs) print(dog1.learn_trick("sit")) # Rex learned sit! print(Dog.species) # Canis familiaris print(dog1.species) # also works # isinstance check print(isinstance(dog1, Dog)) # True
Methods & Dunder
Python classes support instance, class, and static methods. Dunder (double-underscore) methods let your objects behave like built-in types โ support len(), print(), +, ==, and more.
class Vector: def __init__(self, x, y): self.x = x; self.y = y def __repr__(self): # developer repr return f"Vector({self.x}, {self.y})" def __str__(self): # print() output return f"({self.x}, {self.y})" def __add__(self, other): # v1 + v2 return Vector(self.x+other.x, self.y+other.y) def __len__(self): # len(v) return 2 def __eq__(self, other): # v1 == v2 return self.x==other.x and self.y==other.y # Class method โ alternative constructor @classmethod def origin(cls): return cls(0, 0) # Static method โ utility, no self needed @staticmethod def is_valid(x, y): return isinstance(x, (int,float)) and isinstance(y, (int,float)) v1 = Vector(1, 2) v2 = Vector(3, 4) print(v1) # (1, 2) โ __str__ print(repr(v1)) # Vector(1, 2) โ __repr__ print(v1 + v2) # (4, 6) โ __add__ print(len(v1)) # 2 โ __len__ print(v1 == v2) # False โ __eq__ print(Vector.origin()) # (0, 0) โ classmethod
Inheritance & super()
Inheritance lets a child class reuse code from a parent class. Override methods to specialize behavior. super() calls the parent's version. Python supports multiple inheritance.
class Animal: # Parent / Base class def __init__(self, name, sound): self.name = name self.sound = sound def speak(self): return f"{self.name} says {self.sound}!" def __repr__(self): return f"{self.__class__.__name__}({self.name!r})" class Dog(Animal): # Child class def __init__(self, name, breed): super().__init__(name, "Woof") # call parent __init__ self.breed = breed def fetch(self): # new method return f"{self.name} fetches the ball!" class Cat(Animal): def __init__(self, name, indoor=True): super().__init__(name, "Meow") self.indoor = indoor def speak(self): # METHOD OVERRIDING base = super().speak() return base + " (elegantly)" d = Dog("Rex", "Labrador") c = Cat("Whiskers") print(d.speak()) # Rex says Woof! print(c.speak()) # Whiskers says Meow! (elegantly) print(d.fetch()) # Rex fetches the ball! # isinstance and issubclass print(isinstance(d, Dog)) # True print(isinstance(d, Animal)) # True! (Dog IS an Animal) print(issubclass(Dog, Animal)) # True
Encapsulation & Properties
Encapsulation controls access to an object's data. Python uses naming conventions (_private) and the @property decorator to create managed attributes with validation.
class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self._balance = balance # _ prefix = "private by convention" @property def balance(self): # GETTER โ accessed like attribute return self._balance @balance.setter def balance(self, amount): # SETTER โ validates on assignment if amount < 0: raise ValueError("Balance cannot be negative") self._balance = amount def deposit(self, amount): if amount <= 0: raise ValueError("Amount must be positive") self._balance += amount return f"Deposited {amount}. Balance: {self._balance}" def withdraw(self, amount): if amount > self._balance: raise ValueError("Insufficient funds") self._balance -= amount return f"Withdrew {amount}. Balance: {self._balance}" acc = BankAccount("Alice", 1000) print(acc.balance) # 1000 (uses getter) acc.balance = 2000 # uses setter โ validates! print(acc.deposit(500)) # Deposited 500. Balance: 2500 print(acc.withdraw(200)) # Withdrew 200. Balance: 2300 try: acc.balance = -100 # triggers setter validation except ValueError as e: print(f"Error: {e}")
Polymorphism & Duck Typing
Polymorphism means "many forms" โ the same interface works for different types. Python's duck typing means "if it walks like a duck and quacks like a duck, it is a duck." No interfaces needed.
# Polymorphism through method overriding class Shape: def area(self): return 0 def describe(self): return f"{self.__class__.__name__}: area = {self.area():.2f}" class Circle(Shape): def __init__(self, r): self.r = r def area(self): return 3.14159 * self.r ** 2 class Rectangle(Shape): def __init__(self, w, h): self.w=w; self.h=h def area(self): return self.w * self.h class Triangle(Shape): def __init__(self, b, h): self.b=b; self.h=h def area(self): return 0.5 * self.b * self.h shapes = [Circle(5), Rectangle(4,6), Triangle(3,8)] for s in shapes: # same interface, different behavior print(s.describe()) total = sum(s.area() for s in shapes) print(f"Total area: {total:.2f}") # Duck typing โ no inheritance required! class File: def read(self): return "file contents" class Socket: def read(self): return "network data" def process(readable): # works with ANY object that has .read() print(f"Got: {readable.read()}") process(File()) process(Socket())
Decorators & Wrappers
Decorators are functions that wrap other functions, adding behavior without modifying source code. They're used for logging, timing, authentication, caching, and more.
import time from functools import wraps # Basic decorator def timer(func): @wraps(func) # preserves func metadata def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() 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(1000000)) # runs, prints time # Decorator with arguments โ decorator factory def repeat(n): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def greet(name): print(f"Hello, {name}!") greet("Aarav") # prints 3 times # Memoization decorator (manual cache) def memoize(func): cache = {} @wraps(func) def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper @memoize def fib(n): if n <= 1: return n return fib(n-1) + fib(n-2) print(fib(35)) # fast due to caching!
Generators & yield
Generators produce values one at a time using yield, never building the entire sequence in memory. Essential for processing large files, infinite sequences, and streaming data.
# Generator function โ uses yield instead of return def count_up(start, stop): current = start while current <= stop: yield current # pauses and returns value current += 1 gen = count_up(1, 5) print(next(gen)) # 1 print(next(gen)) # 2 for n in gen: print(n, end=" ") # 3 4 5 # Infinite generator def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b fib = fibonacci() first10 = [next(fib) for _ in range(10)] print(first10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] # yield from โ delegate to sub-generator def chain(*iterables): for it in iterables: yield from it print(list(chain([1,2], [3,4], [5]))) # [1,2,3,4,5] # Memory comparison # List: builds entire thing in memory big_list = [x**2 for x in range(10000)] # ~80KB # Generator: uses ~100 bytes regardless of size big_gen = (x**2 for x in range(10000)) print(sum(big_gen)) # consumes lazily
Lambda & Functional Python
Lambda functions are one-line anonymous functions. Combined with map(), filter(), and sorted(), they enable a functional programming style for clean data transformations.
# Lambda โ anonymous function lambda args: expression square = lambda x: x**2 add = lambda x, y: x + y greet = lambda name: f"Hello, {name}!" print(square(5)) # 25 print(add(3, 4)) # 7 print(greet("Aarav")) # Hello, Aarav! # sorted() with key function words = ["banana", "apple", "cherry", "date"] print(sorted(words, key=lambda w: len(w))) # by length print(sorted(words, key=lambda w: w[-1])) # by last char students = [("Alice",90),("Bob",75),("Carol",88)] print(sorted(students, key=lambda s: s[1], reverse=True)) # map() โ apply function to every element nums = [1, 2, 3, 4, 5] doubled = list(map(lambda x: x*2, nums)) strings = list(map(str, nums)) print(doubled) # [2, 4, 6, 8, 10] # filter() โ keep elements where function returns True evens = list(filter(lambda x: x%2==0, nums)) print(evens) # [2, 4] # reduce() โ accumulate from functools import reduce product = reduce(lambda a,b: a*b, nums) print(product) # 120 (1*2*3*4*5)
Regular Expressions
Regular expressions (regex) are patterns for searching, validating, and transforming text. Essential for web scraping, data cleaning, log parsing, and form validation.
import re text = "Contact us: support@example.com or info@test.org" # re.search() โ find first match anywhere in string m = re.search(r"\d+", "Hello 42 World") print(m.group()) # "42" print(m.start()) # 6 (position) # re.findall() โ all non-overlapping matches emails = re.findall(r"[\w.]+@[\w.]+\.\w+", text) print(emails) # ['support@example.com', 'info@test.org'] nums = re.findall(r"\d+", "I have 3 cats and 12 dogs") print(nums) # ['3', '12'] # re.sub() โ replace matches clean = re.sub(r"\s+", " ", "Hello World\n !") print(clean) # "Hello World !" # Groups โ capturing parts of patterns date = "2024-01-15" m = re.match(r"(\d{4})-(\d{2})-(\d{2})", date) print(m.group(1), m.group(2), m.group(3)) # 2024 01 15 # Named groups m = re.match(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})", date) print(m.groupdict()) # {'year':'2024','month':'01','day':'15'} # Compile for reuse email_re = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}") print(email_re.match("test@email.com") is not None) # True
datetime Module
The datetime module handles dates, times, and durations. From scheduling apps to analytics, date/time manipulation is a core real-world Python skill.
from datetime import datetime, date, timedelta # Current time now = datetime.now() today = date.today() print(now) # 2024-01-15 14:30:22.123456 print(today) # 2024-01-15 # Creating specific dates birthday = date(2000, 6, 15) event = datetime(2024, 12, 31, 23, 59, 59) # Formatting (strftime โ string from time) print(now.strftime("%d %B %Y")) # "15 January 2024" print(now.strftime("%Y-%m-%d %H:%M")) # "2024-01-15 14:30" print(now.strftime("%A, %B %d")) # "Monday, January 15" # Parsing (strptime โ string parse time) dt = datetime.strptime("25/12/2024", "%d/%m/%Y") print(dt.year, dt.month, dt.day) # 2024 12 25 # Arithmetic with timedelta tomorrow = today + timedelta(days=1) last_week = today - timedelta(weeks=1) diff = event - now print(f"Days until event: {diff.days}") # Age calculator birth = date(2000, 3, 15) age = (today - birth).days // 365 print(f"Age: {age} years")
os & sys Modules
The os and sys modules let Python interact with the operating system โ list directories, run commands, read environment variables, and access command-line arguments.
import os, sys from pathlib import Path # modern path handling # Current directory print(os.getcwd()) # /home/user/projects print(os.listdir(".")) # files in current dir # Path operations path = os.path.join("data", "2024", "report.csv") print(path) # data/2024/report.csv print(os.path.exists(path)) # True/False print(os.path.basename(path)) # report.csv print(os.path.dirname(path)) # data/2024 print(os.path.splitext("report.csv")) # ('report', '.csv') # Create / remove os.makedirs("output/reports", exist_ok=True) # os.remove("file.txt") # delete file # os.rmdir("empty_folder") # delete empty folder # Environment variables home = os.environ.get("HOME", "/home/user") print(f"Home: {home}") os.environ["MY_API_KEY"] = "secret123" # sys โ Python runtime info print(sys.version) # Python version string print(sys.platform) # 'linux', 'darwin', 'win32' print(sys.argv) # command-line arguments list # pathlib (modern, object-oriented approach) p = Path("data") / "file.txt" # / operator for joining! print(p.stem) # "file" print(p.suffix) # ".txt" print(p.parent) # data
JSON Module
JSON is the universal language of APIs. Python's json module converts between Python dicts/lists and JSON strings effortlessly. Master serialization and deserialization for web development.
import json # Python dict โ JSON string (serialize) data = { "name": "Aarav", "age": 22, "skills": ["Python", "JavaScript"], "active": True, "score": None } json_str = json.dumps(data) # compact json_pretty = json.dumps(data, indent=2) # pretty json_sorted = json.dumps(data, sort_keys=True) # sorted keys print(json_pretty) # JSON string โ Python dict (deserialize) back = json.loads(json_str) print(back["name"]) # Aarav print(type(back["skills"])) # list # JSON type mapping: # JSON null โ Python None # JSON true โ Python True # JSON array โ Python list # JSON objectโ Python dict # Custom encoder for non-serializable types from datetime import date class DateEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, date): return obj.isoformat() return super().default(obj) event = {"name": "Launch", "date": date(2024, 1, 15)} print(json.dumps(event, cls=DateEncoder)) # {"name": "Launch", "date": "2024-01-15"}
collections Module
The collections module provides specialized data structures that solve common problems elegantly: Counter for frequency counts, defaultdict to avoid KeyError, deque for efficient queues.
from collections import Counter, defaultdict, deque, OrderedDict, namedtuple # Counter โ frequency counting words = ["apple","banana","apple","cherry","apple","banana"] c = Counter(words) print(c) # Counter({'apple': 3, 'banana': 2, ...}) print(c.most_common(2)) # [('apple', 3), ('banana', 2)] print(c["cherry"]) # 1 print(c["missing"]) # 0 (no KeyError!) letter_count = Counter("mississippi") print(letter_count) # defaultdict โ no KeyError for missing keys groups = defaultdict(list) data = [("Math","Alice"),("Science","Bob"),("Math","Carol")] for subject, student in data: groups[subject].append(student) # auto-creates empty list print(dict(groups)) # {'Math':['Alice','Carol'],'Science':['Bob']} word_lengths = defaultdict(int) for w in words: word_lengths[w] += 1 # deque โ fast append/pop from both ends dq = deque([1,2,3]) dq.appendleft(0) # [0,1,2,3] โ O(1)! dq.append(4) # [0,1,2,3,4] dq.popleft() # removes 0 โ O(1)! dq.rotate(1) # [4,1,2,3] โ rotate right print(dq) # namedtuple โ lightweight class for data records Point = namedtuple("Point", ["x", "y"]) p = Point(3, 7) print(p.x, p.y) # 3 7 print(p[0], p[1]) # also works like tuple
itertools Module
itertools provides building blocks for efficient looping โ combinations, permutations, infinite counters, grouping, and chaining iterables. Beloved by competitive programmers and data engineers.
import itertools as it # chain โ combine iterables print(list(it.chain([1,2],[3,4],[5]))) # [1,2,3,4,5] # combinations and permutations cards = ["A", "K", "Q"] print(list(it.combinations(cards, 2))) # [('A','K'),('A','Q'),('K','Q')] print(list(it.permutations(cards, 2))) # all ordered pairs print(list(it.combinations_with_replacement([1,2], 2))) # product โ cartesian product print(list(it.product([0,1], repeat=2))) # all 2-bit combos # count, cycle, repeat โ infinite iterators counter = it.count(10, 5) # 10,15,20,25... print([next(counter) for _ in range(4)]) # [10,15,20,25] cycler = it.cycle(["Mon","Tue","Wed"]) print([next(cycler) for _ in range(7)]) # Mon..Wed repeating print(list(it.repeat("hi", 3))) # ['hi','hi','hi'] # groupby โ group consecutive elements data = ["Alice","Alice","Bob","Bob","Alice"] for key, group in it.groupby(data): print(key, "->", list(group)) # takewhile / dropwhile nums = [1,3,5,6,8,9] print(list(it.takewhile(lambda x: x%2!=0, nums))) # [1,3,5] print(list(it.dropwhile(lambda x: x%2!=0, nums))) # [6,8,9]
NumPy Basics
NumPy is the foundation of scientific Python. It provides fast N-dimensional arrays and mathematical operations vectorized over entire arrays โ 100x faster than plain Python loops.
import numpy as np # Creating arrays a = np.array([1, 2, 3, 4, 5]) b = np.array([10, 20, 30, 40, 50]) matrix = np.array([[1,2,3],[4,5,6],[7,8,9]]) # Array creation functions zeros = np.zeros((3, 4)) # 3x4 of zeros ones = np.ones((2, 3)) # 2x3 of ones rng = np.arange(0, 10, 2) # [0,2,4,6,8] linsp = np.linspace(0, 1, 5)# [0,.25,.5,.75,1] rand = np.random.rand(3, 3)# 3x3 random floats 0-1 # Vectorized operations โ no loops needed! print(a + b) # [11,22,33,44,55] print(a * 2) # [2,4,6,8,10] print(np.sqrt(a)) # [1, 1.41, 1.73, 2, 2.24] print(a[a > 2]) # [3,4,5] โ boolean indexing # Statistics print(np.mean(a)) # 3.0 print(np.std(a)) # standard deviation print(np.median(a)) # 3.0 print(np.sum(matrix)) # 45 # Shape and reshape print(matrix.shape) # (3, 3) flat = matrix.reshape(1, 9) # 1x9 print(matrix.T) # transpose # Dot product (matrix multiplication) print(np.dot(a, b)) # 550 (1ร10+2ร20+...)
pip install numpy.Pandas Basics
Pandas is the #1 data analysis library. DataFrames let you load, clean, filter, group, and analyze tabular data โ like Excel but programmable, reproducible, and scalable to millions of rows.
import pandas as pd # Create DataFrame from dict df = pd.DataFrame({ "name": ["Alice", "Bob", "Carol", "Dave"], "age": [25, 30, 28, 35], "score": [88, 92, 76, 95], "city": ["Ktm", "Pkr", "Ktm", "Brt"] }) # Explore print(df.head(2)) # first 2 rows print(df.shape) # (4, 4) print(df.dtypes) # column types print(df.describe()) # stats summary # Selecting data print(df["name"]) # column as Series print(df[["name","score"]]) # multiple columns print(df.iloc[0]) # first row by position print(df.loc[0, "age"]) # single value # Filtering high = df[df["score"] > 85] ktm = df[df["city"] == "Ktm"] both = df[(df["age"] < 30) & (df["score"] > 80)] # Groupby โ SQL GROUP BY equivalent avg_by_city = df.groupby("city")["score"].mean() print(avg_by_city) # Adding columns df["grade"] = df["score"].apply(lambda s: "A" if s>=90 else "B") df["senior"] = df["age"] > 29 # Sorting print(df.sort_values("score", ascending=False)) # Reading from CSV/JSON (common in real projects) # df = pd.read_csv("data.csv") # df.to_csv("output.csv", index=False)
Matplotlib Visualization
Matplotlib is Python's core visualization library. Create publication-quality charts, plots, and graphs โ line, bar, scatter, histogram, pie โ with full control over every element.
import matplotlib.pyplot as plt import numpy as np # Line plot x = np.linspace(0, 2 * np.pi, 100) plt.figure(figsize=(10, 4)) plt.plot(x, np.sin(x), label='sin(x)', color='royalblue', lw=2) plt.plot(x, np.cos(x), label='cos(x)', color='tomato', lw=2) plt.title('Trigonometric Functions') plt.xlabel('x'); plt.ylabel('y') plt.legend(); plt.grid(True, alpha=0.3) plt.savefig('plot.png', dpi=150) plt.show() # Bar chart categories = ['Python', 'JavaScript', 'Java', 'C++', 'Rust'] popularity = [30, 25, 18, 12, 8] plt.figure(figsize=(8, 5)) plt.bar(categories, popularity, color='#a855f7', edgecolor='white') plt.title('Language Popularity') plt.ylabel('Usage %') plt.show() # Scatter plot x = np.random.randn(100) y = x * 2 + np.random.randn(100) plt.scatter(x, y, alpha=0.6, c='purple') plt.title('Scatter Plot'); plt.show() # Subplots โ multiple charts in one figure fig, axes = plt.subplots(1, 2, figsize=(12, 4)) axes[0].hist(np.random.randn(1000), bins=30, color='purple') axes[1].pie(popularity, labels=categories, autopct='%1.0f%%') plt.tight_layout() plt.show()
requests Library
The requests library makes HTTP as simple as possible. Call REST APIs, download files, authenticate, handle sessions โ everything you need to connect Python to the internet.
import requests # GET request res = requests.get("https://api.github.com/users/python") print(res.status_code) # 200 print(res.headers["Content-Type"]) data = res.json() # parse JSON response print(data["public_repos"]) # GET with parameters params = {"q": "python", "sort": "stars", "per_page": 5} res = requests.get("https://api.github.com/search/repositories", params=params) repos = res.json()["items"] for r in repos: print(f"{r['full_name']:40} โญ{r['stargazers_count']:,}") # POST request โ send data payload = {"title": "Test Post", "body": "Hello", "userId": 1} res = requests.post("https://jsonplaceholder.typicode.com/posts", json=payload) print(res.status_code) # 201 Created print(res.json()) # Error handling try: res = requests.get("https://api.example.com/data", timeout=5) res.raise_for_status() # raises HTTPError for 4xx/5xx data = res.json() except requests.exceptions.Timeout: print("Request timed out") except requests.exceptions.HTTPError as e: print(f"HTTP error: {e}") except requests.exceptions.ConnectionError: print("Connection failed") # Headers and authentication headers = {"Authorization": "Bearer MY_TOKEN"} res = requests.get("https://api.example.com/profile", headers=headers)
Flask Introduction
Flask is a lightweight web framework for Python. With just a few lines you can serve web pages, build REST APIs, handle forms, and create full web applications.
from flask import Flask, jsonify, request app = Flask(__name__) # Route decorator maps URL to function @app.route("/") def home(): return "<h1>Welcome to Flask!</h1>" @app.route("/hello/<name>") def hello(name): return f"Hello, {name}!" # REST API endpoint students = [ {"id":1, "name":"Alice", "score":95}, {"id":2, "name":"Bob", "score":82}, ] @app.route("/api/students") def get_students(): return jsonify(students) @app.route("/api/students/<int:student_id>") def get_student(student_id): s = next((s for s in students if s["id"]==student_id), None) return jsonify(s) if s else (jsonify({"error":"not found"}), 404) @app.route("/api/students", methods=["POST"]) def add_student(): data = request.get_json() new_s = {"id": len(students)+1, **data} students.append(new_s) return jsonify(new_s), 201 if __name__ == "__main__": app.run(debug=True) # http://localhost:5000
Flask Routes & Templates
Flask uses Jinja2 templating to render dynamic HTML. Learn blueprints for modular apps, template inheritance to avoid repetition, and form handling for user input.
# app.py from flask import Flask, render_template, request, redirect, url_for, flash app = Flask(__name__) app.secret_key = "super-secret" @app.route("/") def index(): users = ["Alice", "Bob", "Carol"] return render_template("index.html", users=users, title="Home") @app.route("/login", methods=["GET","POST"]) def login(): if request.method == "POST": username = request.form["username"] password = request.form["password"] if username == "admin" and password == "secret": flash("Login successful!", "success") return redirect(url_for("index")) flash("Invalid credentials", "error") return render_template("login.html") # templates/index.html (Jinja2) """ <!-- extends base.html for inheritance --> {% extends "base.html" %} {% block content %} <h1>{{ title }}</h1> {% for user in users %} <p>{{ loop.index }}. {{ user }}</p> {% endfor %} {% if users|length > 2 %} <p>Many users!</p> {% endif %} {% endblock %} """ # Jinja2 features: {{ var }}, {% if %}, {% for %}, # filters: {{ name|upper }}, {{ list|join(', ') }}
SQLite Basics
SQLite is a serverless database built into Python. Perfect for local storage, small apps, and learning SQL โ no installation needed. python's sqlite3 module is in the standard library.
import sqlite3 # Connect (creates file if doesn't exist) conn = sqlite3.connect("students.db") # conn = sqlite3.connect(":memory:") # in-memory (no file) cur = conn.cursor() # Create table cur.execute(""" CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, grade INTEGER, city TEXT DEFAULT 'Unknown' ) """) # Insert (use ? placeholders to prevent SQL injection!) cur.execute("INSERT INTO students (name, grade, city) VALUES (?, ?, ?)", ("Alice", 90, "Kathmandu")) # Insert many rows at once students = [("Bob",85,"Pokhara"), ("Carol",92,"Biratnagar")] cur.executemany("INSERT INTO students (name, grade, city) VALUES (?, ?, ?)", students) conn.commit() # save changes # SELECT โ query cur.execute("SELECT * FROM students") rows = cur.fetchall() for row in rows: print(row) # (1, 'Alice', 90, 'Kathmandu') # With column names cur.execute("SELECT name, grade FROM students WHERE grade > 88") print(cur.fetchall()) # UPDATE and DELETE cur.execute("UPDATE students SET grade=95 WHERE name='Alice'") cur.execute("DELETE FROM students WHERE grade < 80") conn.commit() conn.close() # always close!
SQL Operations
Advanced SQL queries โ JOINs, aggregations, subqueries, and indices โ are what separate beginner from professional database work. These patterns apply to SQLite, PostgreSQL, and MySQL.
import sqlite3 conn = sqlite3.connect(":memory:") conn.row_factory = sqlite3.Row # access columns by name! cur = conn.cursor() # Setup tables cur.executescript(""" CREATE TABLE students (id INT PRIMARY KEY, name TEXT, city_id INT); CREATE TABLE cities (id INT PRIMARY KEY, name TEXT, country TEXT); INSERT INTO cities VALUES (1,'Kathmandu','Nepal'),(2,'Pokhara','Nepal'); INSERT INTO students VALUES (1,'Alice',1),(2,'Bob',2),(3,'Carol',1); """) # INNER JOIN โ match rows from both tables cur.execute(""" SELECT s.name, c.name AS city, c.country FROM students s JOIN cities c ON s.city_id = c.id """) for row in cur.fetchall(): print(dict(row)) # Aggregation: GROUP BY + COUNT + AVG cur.execute(""" SELECT city_id, COUNT(*) as student_count FROM students GROUP BY city_id HAVING COUNT(*) > 1 """) print(cur.fetchall()) # Using pandas for SQL-like operations in Python import sqlite3 # pd.read_sql_query("SELECT ...", conn) # run SQL, get DataFrame conn.close()
Virtual Environments & Packaging
Virtual environments isolate project dependencies โ each project has its own packages without conflicts. Essential for professional Python development and deploying to production.
# Create a virtual environment $ python3 -m venv myenv # Activate it $ source myenv/bin/activate # Mac/Linux $ myenv\Scripts\activate # Windows # Your prompt changes to: (myenv) $ # Install packages INSIDE the venv $ pip install flask requests pandas # See what's installed $ pip list $ pip freeze > requirements.txt # save for sharing # Re-create environment from requirements.txt $ pip install -r requirements.txt # Deactivate when done $ deactivate # pyproject.toml โ modern project config # [project] # name = "myproject" # version = "1.0.0" # dependencies = ["flask>=3.0", "requests>=2.31"] # Package your code to upload to PyPI (pip install yourpackage) # pip install build # python -m build # pip install twine; twine upload dist/*
requirements.txt or pyproject.toml with your code.Project: Calculator App
Build a fully-functional CLI calculator with operator precedence, history, and a clean UI. This project reinforces functions, error handling, and string formatting.
# Run this in the sandbox below! def calculate(expr): """Safely evaluate a math expression.""" allowed = set("0123456789+-*/()%. \t") if not all(c in allowed for c in expr): raise ValueError("Invalid characters in expression") return eval(expr) def run_calculator(): history = [] print("=" * 40) print(" PYTHON CALCULATOR") print(" Type 'hist' for history, 'quit' to exit") print("=" * 40) while True: try: inp = input("\n> ").strip() if not inp: continue if inp.lower() == "quit": print("Goodbye!"); break if inp.lower() == "hist": print("\nHistory:") for i, (ex, res) in enumerate(history[-5:], 1): print(f" {i}. {ex} = {res}") continue result = calculate(inp) print(f" = {result}") history.append((inp, result)) except ZeroDivisionError: print(" Error: Division by zero") except ValueError as e: print(f" Error: {e}") except SyntaxError: print(" Error: Invalid expression") run_calculator()
Project: Number Guessing Game
A complete number guessing game with difficulty levels, lives, hints, and a leaderboard. Uses random, loops, conditionals, and functions in a fun real-world context.
import random def play_game(difficulty="medium"): levels = {"easy":(1,50,10), "medium":(1,100,7), "hard":(1,200,5)} low, high, lives = levels.get(difficulty, levels["medium"]) secret = random.randint(low, high) attempts = 0 print(f"\n๐ฒ Guess my number ({low}-{high}) | Lives: {lives}") while lives > 0: try: guess = int(input(f" Lives left: {lives} > ")) attempts += 1 lives -= 1 if guess == secret: print(f"\n๐ Correct! Found in {attempts} attempts!") score = (1000 // attempts) + lives * 50 print(f"โญ Score: {score}") return score elif guess < secret: diff = secret - guess hint = "๐ฅ Very close!" if diff <= 5 else "๐ Too low" print(f" {hint}") else: diff = guess - secret hint = "๐ฅ Very close!" if diff <= 5 else "๐ Too high" print(f" {hint}") except ValueError: print(" Please enter a valid number"); lives += 1 print(f"\n๐ Game over! The number was {secret}") return 0 print("Welcome to the Number Guessing Game!") play_game("medium")
Project: CLI Todo App
A full-featured CLI todo list manager with persistent storage (JSON file), priorities, due dates, and filtering. Combines file I/O, JSON, OOP, and datetime in one real-world app.
class TodoApp: def __init__(self): self.tasks = [] self.next_id = 1 def add(self, title, priority="medium"): task = {"id": self.next_id, "title": title, "priority": priority, "done": False} self.tasks.append(task) self.next_id += 1 print(f"โ Added: [{task['id']}] {title}") def complete(self, task_id): for t in self.tasks: if t["id"] == task_id: t["done"] = True print(f"โ Completed: {t['title']}") return print(f"Task {task_id} not found") def show(self, filter_done=False): icons = {"high":"๐ด", "medium":"๐ก", "low":"๐ข"} tasks = [t for t in self.tasks if not t["done"]] if not filter_done else self.tasks if not tasks: print("No tasks!"); return print("\n๐ TASKS:") for t in tasks: status = "[โ]" if t["done"] else "[ ]" icon = icons[t["priority"]] print(f" {status} [{t['id']}] {icon} {t['title']}") app = TodoApp() app.add("Learn Python", "high") app.add("Build a project", "high") app.add("Read documentation", "medium") app.add("Exercise", "low") app.show() app.complete(1) print("\nAfter completing task 1:") app.show()
Project: Quiz Application
Build a CLI quiz app that loads questions from JSON, tracks scores, shows progress, and provides a final report. Great for practising data structures, file I/O, and user interaction.
import random QUESTIONS = [ {"q":"What is Python's mascot?", "opts":["Snake","Dragon","Eagle","Tiger"], "ans":0}, {"q":"Which keyword defines a function?", "opts":["func","define","def","function"], "ans":2}, {"q":"What does len([1,2,3]) return?", "opts":["[1,2,3]","3","2","Error"], "ans":1}, {"q":"Which is mutable?", "opts":["tuple","string","list","frozenset"], "ans":2}, {"q":"Output of 2**10?", "opts":["20","100","1024","512"], "ans":2}, ] def run_quiz(): qs = random.sample(QUESTIONS, len(QUESTIONS)) score, wrong = 0, [] print("\n" + "="*40) print(" PYTHON QUIZ โ 5 Questions") print("="*40) for i, q in enumerate(qs, 1): print(f"\nQ{i}: {q['q']}") for j, opt in enumerate(q["opts"], 1): print(f" {j}. {opt}") try: ans = int(input("Your answer (1-4): ")) - 1 if ans == q["ans"]: print("โ Correct!") score += 1 else: print(f"โ Wrong! Correct: {q['opts'][q['ans']]}") wrong.append(q["q"]) except: print("Invalid input. Skipped.") wrong.append(q["q"]) print("\n" + "="*40) print(f"FINAL SCORE: {score}/{len(qs)} ({score*100//len(qs)}%)") if score == len(qs): print("๐ Perfect score! Python Master!") elif score >= len(qs) * 0.7: print("โญ Great job!") print("="*40) run_quiz()
Project: Text Analyzer
Analyze text files โ count words, characters, find most common words, calculate readability scores. Uses file I/O, string methods, collections.Counter, and data visualization.
from collections import Counter text = """Python is amazing. Python is powerful. Python is easy to learn. Many developers love Python.""" # Basic stats chars = len(text) words = len(text.split()) lines = len(text.split('\n')) sentences = text.count('.') + text.count('!') + text.count('?') print("=== TEXT ANALYSIS ===") print(f"Characters: {chars}") print(f"Words: {words}") print(f"Lines: {lines}") print(f"Sentences: {sentences}") print(f"Avg word length: {sum(len(w) for w in text.split()) / words:.1f}") # Word frequency words_clean = text.lower().replace('.', '').split() counter = Counter(words_clean) print("\n=== TOP 5 WORDS ===") for word, count in counter.most_common(5): print(f" {word:15} {'โ' * count} {count}") # Character frequency letter_count = Counter(c.lower() for c in text if c.isalpha()) print(f"\n=== MOST COMMON LETTERS ===") for letter, count in letter_count.most_common(3): print(f" {letter}: {count}")
Project: Weather Dashboard
Fetch live weather data from a public API, parse JSON responses, and display formatted weather information. Real-world API integration practice.
import requests def get_weather(city="Kathmandu"): # Free API โ no key needed for basic usage url = f"https://wttr.in/{city}?format=j1" try: res = requests.get(url, timeout=5) res.raise_for_status() data = res.json() current = data["current_condition"][0] print(f"\n๐ค๏ธ WEATHER FOR {city.upper()}") print("=" * 35) print(f"๐ก๏ธ Temperature: {current['temp_C']}ยฐC") print(f"โ๏ธ Condition: {current['weatherDesc'][0]['value']}") print(f"๐จ Wind: {current['windspeedKmph']} km/h") print(f"๐ง Humidity: {current['humidity']}%") print(f"๐๏ธ Visibility: {current['visibility']} km") except requests.exceptions.RequestException as e: print(f"Error fetching weather: {e}") get_weather("Kathmandu") get_weather("Pokhara")
Project: Web Scraper
Extract data from websites programmatically. Learn HTML parsing, data cleaning, and ethical scraping practices. Powerful for automation and data collection.
# Example: Scraping concept (run locally with beautifulsoup4) # pip install beautifulsoup4 requests from bs4 import BeautifulSoup import requests url = "https://quotes.toscrape.com/" res = requests.get(url) soup = BeautifulSoup(res.content, "html.parser") # Extract all quotes quotes = soup.find_all("span", class_="text") authors = soup.find_all("small", class_="author") print("=== QUOTES ===") for q, a in zip(quotes[:5], authors[:5]): print(f"\n{q.text}") print(f" โ {a.text}") # Pure Python simulation: print("\n=== SIMULATED SCRAPING ===\") html = """<div class="quote"> <span class="text">"Code is poetry"</span> <span class="author">Unknown</span> </div>""" import re quotes_sim = re.findall(r'class="text">(.*?)</span>', html) print(quotes_sim[0])
Project: Contact Book
Build a complete contact manager with SQLite database โ add, search, update, delete contacts with phone validation and backup features. Full CRUD application.
class ContactBook: def __init__(self): self.contacts = [] def add(self, name, phone, email=""): contact = {"name": name, "phone": phone, "email": email} self.contacts.append(contact) print(f"โ Added: {name}") def search(self, query): results = [c for c in self.contacts if query.lower() in c["name"].lower() or query in c["phone"]] if results: print(f"\n๐ Found {len(results)} contact(s):") for c in results: print(f" ๐ {c['name']:20} {c['phone']:15} {c['email']}") else: print("โ No contacts found") def list_all(self): if not self.contacts: print("๐ญ No contacts yet") return print(f"\n๐ ALL CONTACTS ({len(self.contacts)})") print("-" * 55) for i, c in enumerate(self.contacts, 1): print(f"{i:2}. {c['name']:20} {c['phone']:15} {c['email']}") book = ContactBook() book.add("Alice Smith", "9841123456", "alice@email.com") book.add("Bob Johnson", "9851234567", "bob@email.com") book.add("Carol Williams", "9861345678") book.list_all() book.search("Alice") book.search("9851")
Capstone: Your Choice!
Build something YOU want. A Flask web app? A data analysis project? A game? A CLI tool? This is where you apply everything you've learned to create something meaningful.
- Personal blog with Flask + SQLite (posts, comments, tags)
- Expense tracker with data visualization (Pandas + Matplotlib)
- Password manager (encryption, CLI, database)
- Discord/Slack bot (API integration, commands)
- Web scraper + dashboard (collect data, visualize trends)
- Snake/Tetris game (Pygame or terminal-based)
- REST API for a todo/notes app (Flask + authentication)
- Data analysis: Analyze CSV datasets and generate reports
- Automated news aggregator (RSS feeds, email digest)
- File organizer (auto-sort downloads by type/date)
# Example capstone starter: Blog API with Flask from flask import Flask, jsonify, request from datetime import datetime app = Flask(__name__) posts = [] next_id = 1 @app.route("/posts", methods=["GET"]) def get_posts(): return jsonify(posts) @app.route("/posts", methods=["POST"]) def create_post(): global next_id data = request.get_json() post = { "id": next_id, "title": data["title"], "content": data["content"], "created_at": datetime.now().isoformat() } posts.append(post) next_id += 1 return jsonify(post), 201 if __name__ == "__main__": app.run(debug=True) # Extend this with: database, auth, comments, likes, etc!