Your Ultimate Roadmap to Mastering Python in 2026

1. Introduction
Discover why Python is the perfect starting point for your programming journey and how it powers the technologies you use every day.
What is Python?
Python is a high-level, interpreted programming language created by Guido van Rossum in 1991. It’s designed to be easy to read and write, making it perfect for beginners while being powerful enough for professional developers.
Think of Python as the Swiss Army knife of programming languages – versatile, reliable, and packed with tools for almost every task imaginable.
Why Python is So Popular
Python consistently ranks as the #1 most popular programming language according to TIOBE Index and Stack Overflow surveys. Here’s why:Table
| Feature | Benefit |
|---|---|
| Simple Syntax | Reads like English, easy to learn |
| Huge Community | Millions of developers ready to help |
| Extensive Libraries | 400,000+ packages for any task |
| Cross-Platform | Runs on Windows, macOS, Linux |
| Versatility | Web, AI, data science, automation, IoT |
Real-World Applications of Python
Python isn’t just for beginners – it powers the tech world:
- 🤖 Artificial Intelligence & Machine Learning – TensorFlow, PyTorch, scikit-learn
- 🌐 Web Development – Instagram, Pinterest, Spotify backends
- 📊 Data Science – Netflix recommendations, Google Analytics
- 🤖 Automation & Scripting – System administration, testing
- 📱 Internet of Things (IoT) – Raspberry Pi projects, smart devices
- 🎮 Game Development – Pygame, AI opponents
- 🔒 Cybersecurity – Penetration testing, forensics
2. Getting Started
Set up your development environment and write your first lines of Python code in under 10 minutes.
Installing Python
Step 1: Visit python.org and download Python 3.11 or later.
Step 2: During installation, check “Add Python to PATH” (Windows users).
Step 3: Verify installation by opening terminal/command prompt:
python --version
# Output: Python 3.11.x
Setting Up Your IDE
An Integrated Development Environment (IDE) makes coding easier.
Option 1: VS Code (Recommended for Beginners)
- Free, lightweight, highly customizable
- Extensions: Python, Pylance, Python Indent
Option 2: PyCharm (Professional Choice)
- Built specifically for Python
- Smart code completion and debugging
- Community Edition is free
Option 3: Jupyter Notebook (For Data Science)
- Interactive coding environment
- Perfect for experimentation and visualization
Your First Python Program
Create a file named hello.py:
Python
Copy
# hello.py - Your first Python program
print("Hello, World!")
print("Welcome to Python programming!")
# Run this in terminal:
# python hello.py
Output:
Hello, World!
Welcome to Python programming!
💡 Pro Tip: The
print()function displays output to the screen. It’s your best friend for debugging!
Practice Problem: Personal Greeter
Problem: Write a program that asks for the user’s name and age, then prints a personalized greeting.
Solution:
# Personal Greeter Program
name = input("Enter your name: ")
age = input("Enter your age: ")
print(f"Hello, {name}! You are {age} years old.")
print("Welcome to the world of Python programming!")
# Bonus: Calculate next year's age
next_age = int(age) + 1
print(f"Next year, you'll be {next_age}!")
3. Python Basics
Master the fundamental building blocks of Python programming – variables, data types, and basic operations.
Variables and Data Types
Variables are containers for storing data. Python automatically detects the data type.
# Variables and Data Types
# String (text)
name = "Alice"
message = 'Python is awesome!'
# Integer (whole numbers)
age = 25
year = 2025
# Float (decimal numbers)
height = 5.6
price = 19.99
# Boolean (True/False)
is_student = True
has_license = False
# None (represents nothing)
data = None
# Check the type
print(type(name)) # <class 'str'>
print(type(age)) # <class 'int'>
print(type(height)) # <class 'float'>
Practice Problem: Variable Swap
Problem: Write a program to swap two variables without using a temporary variable.
Solution:
# Variable Swap Challenge
a = 5
b = 10
print(f"Before: a = {a}, b = {b}")
# Pythonic way to swap
a, b = b, a
print(f"After: a = {a}, b = {b}")
# Output: Before: a = 5, b = 10
# After: a = 10, b = 5
Input and Output
# Getting user input
name = input("Enter your name: ")
age = int(input("Enter your age: ")) # Convert string to integer
# Output with formatting
print(f"Hello, {name}! You are {age} years old.")
print("Next year, you'll be", age + 1)
# Alternative formatting methods
print("Hello, {}! You are {} years old.".format(name, age))
print("Hello, %s! You are %d years old." % (name, age))
Practice Problem: Simple Calculator
Problem: Create a program that takes two numbers as input and displays their sum, difference, product, and quotient.
Solution:
# Simple Calculator
num1 = float(input("Enter first number: "))
num2 = float(input("Enter second number: "))
print(f"Sum: {num1 + num2}")
print(f"Difference: {num1 - num2}")
print(f"Product: {num1 * num2}")
print(f"Quotient: {num1 / num2 if num2 != 0 else 'Cannot divide by zero'}")
Operators
# Arithmetic Operators
a, b = 10, 3
print(a + b) # Addition: 13
print(a - b) # Subtraction: 7
print(a * b) # Multiplication: 30
print(a / b) # Division: 3.33
print(a // b) # Floor Division: 3
print(a % b) # Modulus (remainder): 1
print(a ** b) # Exponentiation: 1000
# Comparison Operators
print(a == b) # Equal to: False
print(a != b) # Not equal: True
print(a > b) # Greater than: True
# Logical Operators
x, y = True, False
print(x and y) # False (both must be True)
print(x or y) # True (at least one True)
print(not x) # False (inverse)
Practice Problem: Odd or Even Checker
Problem: Write a program that determines if a number is odd or even using the modulus operator.
Solution:
# Odd or Even Checker
number = int(input("Enter a number: "))
if number % 2 == 0:
print(f"{number} is even!")
else:
print(f"{number} is odd!")
# Bonus: Check if divisible by both 2 and 3
if number % 2 == 0 and number % 3 == 0:
print(f"{number} is divisible by both 2 and 3!")
Comments
# This is a single-line comment
"""
This is a multi-line comment.
Use it for detailed explanations
or documentation.
"""
# TODO: This highlights tasks to complete
# FIXME: This indicates bugs to fix
# NOTE: This provides important information
# Best practice: Comments explain WHY, not WHAT
# Bad: x = x + 1 # Add 1 to x
# Good: x = x + 1 # Compensate for zero-based indexing
⚠️ Common Mistake: Using
=(assignment) instead of==(comparison) in conditions.
4. Control Structures
Learn how to make decisions and repeat actions – the logic that brings your programs to life.
Control structures direct the flow of your program.
Conditional Statements (if, elif, else)
# Simple if-else
age = 18
if age >= 18:
print("You are an adult.")
else:
print("You are a minor.")
# if-elif-else chain
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
print(f"Your grade is: {grade}")
# Ternary operator (shorthand)
status = "Adult" if age >= 18 else "Minor"
Practice Problem: BMI Calculator
Problem: Create a BMI calculator that categorizes the result as Underweight, Normal, Overweight, or Obese.
Solution:
# BMI Calculator with Categories
weight = float(input("Enter weight in kg: "))
height = float(input("Enter height in meters: "))
bmi = weight / (height ** 2)
print(f"Your BMI is: {bmi:.2f}")
if bmi < 18.5:
category = "Underweight"
elif bmi < 25:
category = "Normal weight"
elif bmi < 30:
category = "Overweight"
else:
category = "Obese"
print(f"Category: {category}")
Loops
For Loop – Iterate over sequences:
# Loop through a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(f"I love {fruit}")
# Loop with range()
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(1, 6): # 1, 2, 3, 4, 5
print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8 (step by 2)
print(i)
# Loop through string
for char in "Python":
print(char)
While Loop – Execute while condition is true:
# Countdown example
count = 5
while count > 0:
print(count)
count -= 1 # Decrement
print("Blast off!")
# User input validation
password = ""
while password != "secret":
password = input("Enter password: ")
print("Access granted!")
Practice Problem: Multiplication Table
Problem: Generate a multiplication table for a given number using both for and while loops.
Solution:
# Multiplication Table Generator
number = int(input("Enter a number: "))
print("Using for loop:")
for i in range(1, 11):
print(f"{number} x {i} = {number * i}")
print("\nUsing while loop:")
i = 1
while i <= 10:
print(f"{number} x {i} = {number * i}")
i += 1
Loop Control Statements
# break - Exit loop immediately
for num in range(10):
if num == 5:
break # Stop at 5
print(num) # Prints 0, 1, 2, 3, 4
# continue - Skip current iteration
for num in range(5):
if num == 2:
continue # Skip 2
print(num) # Prints 0, 1, 3, 4
# else clause (executes if loop wasn't broken)
for i in range(3):
print(i)
else:
print("Loop completed successfully!")
# pass - Do nothing (placeholder)
for i in range(5):
pass # TODO: Implement later
Practice Problem: Prime Number Checker
Problem: Check if a number is prime using a loop with optimization.
Solution:
# Prime Number Checker
num = int(input("Enter a number: "))
if num < 2:
print(f"{num} is not prime")
else:
is_prime = True
for i in range(2, int(num**0.5) + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
print(f"{num} is prime!")
else:
print(f"{num} is not prime!")
💡 Real-World Analogy: A
forloop is like a playlist playing each song once. Awhileloop is like a jukebox playing until you run out of coins!
5. Functions
Write reusable, organized code blocks that make your programs modular and maintainable.
Functions are reusable blocks of code that perform specific tasks.
Defining Functions
# Basic function
def greet():
print("Hello, World!")
greet() # Call the function
# Function with parameters
def greet_person(name):
print(f"Hello, {name}!")
greet_person("Alice") # Hello, Alice!
greet_person("Bob") # Hello, Bob!
# Function with return value
def add_numbers(a, b):
return a + b
result = add_numbers(5, 3)
print(result) # 8
# Multiple return values
def get_stats(numbers):
return min(numbers), max(numbers), sum(numbers)/len(numbers)
minimum, maximum, average = get_stats([1, 2, 3, 4, 5])
Practice Problem: Temperature Converter
Problem: Create functions to convert between Celsius, Fahrenheit, and Kelvin.
Solution:
# Temperature Converter Functions
def celsius_to_fahrenheit(c):
return (c * 9/5) + 32
def fahrenheit_to_celsius(f):
return (f - 32) * 5/9
def celsius_to_kelvin(c):
return c + 273.15
# Test the functions
temp_c = 25
print(f"{temp_c}°C = {celsius_to_fahrenheit(temp_c)}°F")
print(f"{temp_c}°C = {celsius_to_kelvin(temp_c)}K")
# Bonus: Universal converter
def convert_temperature(value, from_unit, to_unit):
if from_unit == to_unit:
return value
# Convert to Celsius first
if from_unit == "F":
value = fahrenheit_to_celsius(value)
elif from_unit == "K":
value = value - 273.15
# Convert from Celsius to target
if to_unit == "F":
return celsius_to_fahrenheit(value)
elif to_unit == "K":
return celsius_to_kelvin(value)
return value
print(convert_temperature(98.6, "F", "C")) # 37.0
Default Parameters and Keyword Arguments
# Default parameters
def power(base, exponent=2):
return base ** exponent
print(power(3)) # 9 (3²)
print(power(2, 3)) # 8 (2³)
# Keyword arguments
def create_user(name, age, city="Unknown"):
return f"{name}, {age}, from {city}"
print(create_user(age=25, name="Alice")) # Alice, 25, from Unknown
print(create_user("Bob", 30, "NYC")) # Positional arguments
Lambda Functions (Anonymous Functions)
# Lambda syntax: lambda arguments: expression
# Simple lambda
square = lambda x: x ** 2
print(square(5)) # 25
# Lambda with multiple arguments
multiply = lambda x, y: x * y
print(multiply(4, 5)) # 20
# Common use: Sorting
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
students.sort(key=lambda x: x[1]) # Sort by score
print(students) # [('Charlie', 78), ('Alice', 85), ('Bob', 92)]
# With map() and filter()
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16, 25]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]
Practice Problem: Sorting with Lambda
Problem: Sort a list of products by price (low to high) and then by name alphabetically.
Solution:
# Product Sorting with Lambda
products = [
{"name": "Laptop", "price": 999.99, "category": "Electronics"},
{"name": "Mouse", "price": 29.99, "category": "Electronics"},
{"name": "Desk", "price": 299.99, "category": "Furniture"},
{"name": "Chair", "price": 199.99, "category": "Furniture"},
{"name": "Monitor", "price": 299.99, "category": "Electronics"}
]
# Sort by price, then by name
sorted_products = sorted(products, key=lambda x: (x["price"], x["name"]))
for product in sorted_products:
print(f"{product['name']}: ${product['price']}")
⚠️ Common Mistake: Forgetting to use
returnin functions. Without it, functions returnNone.
6. Data Structures
Master Python’s built-in containers for organizing and manipulating data efficiently.
Python provides powerful built-in data structures for organizing data.
Lists (Ordered, Mutable)
# Creating lists
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]
# Accessing elements
print(fruits[0]) # First element: "apple"
print(fruits[-1]) # Last element: "cherry"
print(fruits[1:3]) # Slice: ["banana", "cherry"]
# Modifying lists
fruits.append("date") # Add to end
fruits.insert(1, "apricot") # Insert at index
fruits.remove("banana") # Remove by value
popped = fruits.pop() # Remove and return last item
fruits.sort() # Sort in place
fruits.reverse() # Reverse in place
# List comprehensions (powerful one-liners)
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
Practice Problem: List Operations
Problem: Create a program that manages a shopping list with add, remove, display, and clear functions.
Solution:
# Shopping List Manager
shopping_list = []
def add_item(item):
shopping_list.append(item)
print(f"Added: {item}")
def remove_item(item):
if item in shopping_list:
shopping_list.remove(item)
print(f"Removed: {item}")
else:
print(f"{item} not found!")
def display_list():
print("\nShopping List:")
for i, item in enumerate(shopping_list, 1):
print(f"{i}. {item}")
print(f"Total items: {len(shopping_list)}")
def clear_list():
shopping_list.clear()
print("List cleared!")
# Interactive menu
while True:
print("\n1. Add 2. Remove 3. Display 4. Clear 5. Exit")
choice = input("Choice: ")
if choice == "1":
add_item(input("Item: "))
elif choice == "2":
remove_item(input("Item to remove: "))
elif choice == "3":
display_list()
elif choice == "4":
clear_list()
elif choice == "5":
break
Tuples (Ordered, Immutable)
# Tuples cannot be changed after creation
coordinates = (10, 20)
rgb_color = (255, 128, 0)
single_item = (42,) # Note the comma!
# Use cases: Data that shouldn't change
days_of_week = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
# Unpacking
x, y = coordinates
print(x, y) # 10 20
# Tuple as dictionary key (lists can't be keys)
locations = {
(0, 0): "Origin",
(10, 20): "Point A"
}
Practice Problem: Coordinate Geometry
Problem: Calculate the distance between two points using tuples and the distance formula.
Solution:
# Distance Calculator using Tuples
import math
def calculate_distance(point1, point2):
"""Calculate Euclidean distance between two points."""
x1, y1 = point1
x2, y2 = point2
distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
return distance
# Test
point_a = (0, 0)
point_b = (3, 4)
print(f"Distance: {calculate_distance(point_a, point_b)}") # 5.0
# Bonus: Check if points form a right triangle
def is_right_triangle(p1, p2, p3):
"""Check if three points form a right triangle."""
a = calculate_distance(p1, p2)
b = calculate_distance(p2, p3)
c = calculate_distance(p1, p3)
sides = sorted([a, b, c])
return math.isclose(sides[0]**2 + sides[1]**2, sides[2]**2)
print(is_right_triangle((0,0), (3,0), (0,4))) # True (3-4-5 triangle)
Dictionaries (Key-Value Pairs)
# Creating dictionaries
student = {
"name": "Alice",
"age": 20,
"grades": [85, 90, 88]
}
# Accessing values
print(student["name"]) # Alice
print(student.get("age")) # 20 (safe access)
print(student.get("gender", "N/A")) # N/A (default if key missing)
# Modifying
student["age"] = 21 # Update value
student["major"] = "CS" # Add new key
del student["grades"] # Remove key
# Dictionary methods
print(student.keys()) # dict_keys(['name', 'age', 'major'])
print(student.values()) # dict_values(['Alice', 21, 'CS'])
print(student.items()) # dict_items([('name', 'Alice'), ...])
# Dictionary comprehension
squares_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
Practice Problem: Word Frequency Counter
Problem: Count the frequency of each word in a sentence using a dictionary.
Solution:
# Word Frequency Counter
def count_words(text):
"""Count frequency of each word in text."""
words = text.lower().split()
frequency = {}
for word in words:
# Remove punctuation
word = word.strip(".,!?;:\"'")
frequency[word] = frequency.get(word, 0) + 1
return frequency
# Test
sentence = "The quick brown fox jumps over the lazy dog. The fox was quick!"
result = count_words(sentence)
print("Word frequencies:")
for word, count in sorted(result.items(), key=lambda x: x[1], reverse=True):
print(f"{word}: {count}")
# Bonus: Find most common word
most_common = max(result.items(), key=lambda x: x[1])
print(f"\nMost common word: '{most_common[0]}' ({most_common[1]} times)")
Sets (Unordered, Unique Elements)
# Creating sets
unique_numbers = {1, 2, 3, 3, 3} # Duplicates removed: {1, 2, 3}
empty_set = set() # {} creates empty dict, not set!
# Set 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}
print(a ^ b) # Symmetric difference: {1, 2, 5, 6}
# Practical use: Removing duplicates
numbers = [1, 2, 2, 3, 3, 3, 4]
unique = list(set(numbers)) # [1, 2, 3, 4]
Practice Problem: Set Operations
Problem: Find students who are enrolled in both Math and Science classes, and those who are only in one.
Solution:
# Student Enrollment Analysis
math_students = {"Alice", "Bob", "Charlie", "David", "Eve"}
science_students = {"Bob", "David", "Frank", "Grace", "Alice"}
# Students in both classes (intersection)
both = math_students & science_students
print(f"Both Math and Science: {both}")
# Students only in Math
only_math = math_students - science_students
print(f"Only Math: {only_math}")
# Students only in Science
only_science = science_students - math_students
print(f"Only Science: {only_science}")
# All unique students (union)
all_students = math_students | science_students
print(f"Total unique students: {len(all_students)}")
# Students in exactly one class (symmetric difference)
exactly_one = math_students ^ science_students
print(f"Exactly one class: {exactly_one}")
💡 When to Use What:
- List: Ordered collection that changes (shopping list)
- Tuple: Fixed data that shouldn’t change (coordinates)
- Dictionary: Lookup by key (phone book)
- Set: Unique items, membership testing (lottery numbers)
7. Object-Oriented Programming (OOP)
Organize your code into reusable, logical structures that model real-world entities.
OOP organizes code into objects that combine data and behavior.
Classes and Objects
# Defining a class
class Dog:
# Class attribute (shared by all instances)
species = "Canis familiaris"
def __init__(self, name, age):
# Instance attributes (unique to each object)
self.name = name
self.age = age
# Instance method
def bark(self):
return f"{self.name} says woof!"
def describe(self):
return f"{self.name} is {self.age} years old"
# Creating objects (instances)
buddy = Dog("Buddy", 3)
bella = Dog("Bella", 5)
print(buddy.bark()) # Buddy says woof!
print(bella.describe()) # Bella is 5 years old
Practice Problem: Bank Account Class
Problem: Create a BankAccount class with deposit, withdraw, and balance check methods.
Solution:
# Bank Account Class
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # Protected attribute
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"Deposited ${amount}. New balance: ${self._balance}")
return True
print("Invalid deposit amount!")
return False
def withdraw(self, amount):
if 0 < amount <= self._balance:
self._balance -= amount
print(f"Withdrew ${amount}. New balance: ${self._balance}")
return True
print("Insufficient funds or invalid amount!")
return False
def get_balance(self):
return self._balance
def __str__(self):
return f"Account of {self.owner}, Balance: ${self._balance}"
# Test
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(account)
Constructors and Special Methods
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # Protected attribute (convention)
# Property getter
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False
def withdraw(self, amount):
if 0 < amount <= self._balance:
self._balance -= amount
return True
return False
# String representation
def __str__(self):
return f"Account of {self.owner}, Balance: ${self._balance}"
def __repr__(self):
return f"BankAccount('{self.owner}', {self._balance})"
# Usage
account = BankAccount("Alice", 1000)
account.deposit(500)
print(account) # Account of Alice, Balance: $1500
Practice Problem: Book Library Class
Problem: Create a Book class and a Library class to manage book collections.
Solution:
# Book and Library Classes
class Book:
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
self.is_borrowed = False
def borrow(self):
if not self.is_borrowed:
self.is_borrowed = True
return True
return False
def return_book(self):
self.is_borrowed = False
def __str__(self):
status = "Borrowed" if self.is_borrowed else "Available"
return f"'{self.title}' by {self.author} [{status}]"
class Library:
def __init__(self, name):
self.name = name
self.books = {}
def add_book(self, book):
self.books[book.isbn] = book
print(f"Added: {book.title}")
def find_book(self, isbn):
return self.books.get(isbn)
def list_available(self):
available = [b for b in self.books.values() if not b.is_borrowed]
print(f"\nAvailable books in {self.name}:")
for book in available:
print(f" {book}")
# Test
lib = Library("City Library")
book1 = Book("Python Guide", "John Smith", "123")
book2 = Book("Data Science", "Jane Doe", "456")
lib.add_book(book1)
lib.add_book(book2)
lib.list_available()
book1.borrow()
print(book1)
lib.list_available()
Inheritance
# Base class
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement")
def move(self):
return f"{self.name} is moving"
# Derived classes
class Cat(Animal):
def speak(self):
return f"{self.name} says meow!"
def purr(self):
return f"{self.name} is purring"
class Dog(Animal):
def speak(self):
return f"{self.name} says woof!"
# Usage
whiskers = Cat("Whiskers")
print(whiskers.speak()) # Whiskers says meow!
print(whiskers.move()) # Whiskers is moving (inherited)
Practice Problem: Employee Management System
Problem: Create a base Employee class with Manager and Developer subclasses.
Solution:
# Employee Management with Inheritance
class Employee:
def __init__(self, name, emp_id, salary):
self.name = name
self.emp_id = emp_id
self.salary = salary
def get_details(self):
return f"{self.name} (ID: {self.emp_id}), Salary: ${self.salary}"
def calculate_bonus(self):
return self.salary * 0.05 # 5% base bonus
class Manager(Employee):
def __init__(self, name, emp_id, salary, team_size):
super().__init__(name, emp_id, salary)
self.team_size = team_size
def calculate_bonus(self): # Override
return self.salary * 0.10 # 10% bonus
def get_details(self):
base = super().get_details()
return f"{base}, Team Size: {self.team_size}"
class Developer(Employee):
def __init__(self, name, emp_id, salary, programming_language):
super().__init__(name, emp_id, salary)
self.programming_language = programming_language
def get_details(self):
base = super().get_details()
return f"{base}, Language: {self.programming_language}"
# Test
emp1 = Manager("Alice", "M001", 80000, 5)
emp2 = Developer("Bob", "D001", 60000, "Python")
print(emp1.get_details())
print(f"Bonus: ${emp1.calculate_bonus()}")
print(emp2.get_details())
print(f"Bonus: ${emp2.calculate_bonus()}")
Encapsulation and Polymorphism
# Encapsulation: Hiding internal details
class SecureAccount:
def __init__(self):
self.__password = None # Private attribute (name mangling)
self._pin = None # Protected attribute
def set_password(self, pwd):
self.__password = self._hash(pwd)
def _hash(self, pwd): # Protected method
return hash(pwd)
# Polymorphism: Same interface, different behavior
def animal_concert(animals):
for animal in animals:
print(animal.speak()) # Works with any Animal subclass
zoo = [Dog("Rex"), Cat("Whiskers"), Dog("Buddy")]
animal_concert(zoo)
# Rex says woof!
# Whiskers says meow!
# Buddy says woof!
💡 OOP Analogy: A class is like a blueprint for a house. An object is the actual house built from that blueprint. Inheritance is like a blueprint that adds a garage to the base house design.
8. File Handling
Learn to persist data by reading from and writing to files – essential for real-world applications.
Python makes working with files simple and intuitive.
Reading and Writing Files
# Writing to a file
with open("data.txt", "w") as file:
file.write("Hello, World!\n")
file.write("Line 2\n")
file.write("Line 3\n")
# File automatically closed after 'with' block
# Reading entire file
with open("data.txt", "r") as file:
content = file.read()
print(content)
# Reading line by line
with open("data.txt", "r") as file:
for line in file:
print(line.strip()) # strip() removes newline
# Reading into list
with open("data.txt", "r") as file:
lines = file.readlines()
print(lines) # ['Hello, World!\n', 'Line 2\n', 'Line 3\n']
# Appending to file
with open("data.txt", "a") as file:
file.write("New line appended\n")
Practice Problem: Contact Manager with File Storage
Problem: Create a contact manager that saves and loads contacts from a file.
Solution:
# Contact Manager with File Storage
import json
class ContactManager:
def __init__(self, filename="contacts.json"):
self.filename = filename
self.contacts = {}
self.load_contacts()
def load_contacts(self):
try:
with open(self.filename, "r") as file:
self.contacts = json.load(file)
except FileNotFoundError:
self.contacts = {}
def save_contacts(self):
with open(self.filename, "w") as file:
json.dump(self.contacts, file, indent=2)
def add_contact(self, name, phone, email):
self.contacts[name] = {
"phone": phone,
"email": email
}
self.save_contacts()
print(f"Contact {name} added!")
def get_contact(self, name):
return self.contacts.get(name)
def list_contacts(self):
for name, info in self.contacts.items():
print(f"{name}: {info['phone']}, {info['email']}")
# Test
manager = ContactManager()
manager.add_contact("Alice", "555-1234", "alice@email.com")
manager.add_contact("Bob", "555-5678", "bob@email.com")
manager.list_contacts()
# Verify persistence
new_manager = ContactManager()
print("\nLoaded from file:")
new_manager.list_contacts()
Working with Different File Types
# CSV files
import csv
# Writing CSV
with open("data.csv", "w", newline="") as file:
writer = csv.writer(file)
writer.writerow(["Name", "Age", "City"])
writer.writerow(["Alice", 25, "NYC"])
writer.writerow(["Bob", 30, "LA"])
# Reading CSV
with open("data.csv", "r") as file:
reader = csv.reader(file)
for row in reader:
print(row)
# JSON files
import json
data = {
"name": "Alice",
"age": 25,
"skills": ["Python", "JavaScript"]
}
# Writing JSON
with open("data.json", "w") as file:
json.dump(data, file, indent=4)
# Reading JSON
with open("data.json", "r") as file:
loaded_data = json.load(file)
print(loaded_data["name"]) # Alice
Practice Problem: Student Grade Logger
Problem: Create a system that logs student grades to CSV and generates a summary report.
Solution:
# Student Grade Logger
import csv
from datetime import datetime
class GradeLogger:
def __init__(self, filename="grades.csv"):
self.filename = filename
self.ensure_file_exists()
def ensure_file_exists(self):
try:
with open(self.filename, "r"):
pass
except FileNotFoundError:
with open(self.filename, "w", newline="") as file:
writer = csv.writer(file)
writer.writerow(["Date", "Student", "Subject", "Grade"])
def add_grade(self, student, subject, grade):
with open(self.filename, "a", newline="") as file:
writer = csv.writer(file)
writer.writerow([
datetime.now().strftime("%Y-%m-%d"),
student,
subject,
grade
])
def generate_report(self, student):
grades = []
with open(self.filename, "r") as file:
reader = csv.DictReader(file)
for row in reader:
if row["Student"] == student:
grades.append(float(row["Grade"]))
if grades:
avg = sum(grades) / len(grades)
print(f"\nReport for {student}:")
print(f" Grades: {grades}")
print(f" Average: {avg:.2f}")
print(f" Highest: {max(grades)}")
print(f" Lowest: {min(grades)}")
else:
print(f"No grades found for {student}")
# Test
logger = GradeLogger()
logger.add_grade("Alice", "Math", 85)
logger.add_grade("Alice", "Science", 92)
logger.add_grade("Alice", "Math", 88)
logger.generate_report("Alice")
⚠️ Best Practice: Always use
withstatement for file operations. It ensures files are properly closed even if errors occur.
9. Error Handling
Write robust programs that gracefully handle unexpected situations and recover from failures.
Errors happen. Good programmers handle them gracefully.
Try, Except, Finally
# Basic error handling
try:
number = int(input("Enter a number: "))
result = 10 / number
print(f"Result: {result}")
except ValueError:
print("That's not a valid number!")
except ZeroDivisionError:
print("Cannot divide by zero!")
except Exception as e:
print(f"An error occurred: {e}")
finally:
print("This always executes (cleanup code)")
# Real-world example: File handling with error recovery
def read_config(filename):
try:
with open(filename, "r") as file:
return file.read()
except FileNotFoundError:
print(f"Config file {filename} not found. Using defaults.")
return "{}"
except PermissionError:
print(f"Permission denied for {filename}")
return None
Practice Problem: Robust Input Validator
Problem: Create a function that safely gets a valid integer within a specified range from the user.
Solution:
# Robust Input Validator
def get_valid_integer(prompt, min_val=None, max_val=None):
"""
Get a valid integer from user with optional range validation.
"""
while True:
try:
value = int(input(prompt))
if min_val is not None and value < min_val:
print(f"Value must be at least {min_val}")
continue
if max_val is not None and value > max_val:
print(f"Value must be at most {max_val}")
continue
return value
except ValueError:
print("Please enter a valid integer!")
except KeyboardInterrupt:
print("\nInput cancelled.")
return None
# Test
age = get_valid_integer("Enter your age (1-120): ", 1, 120)
if age is not None:
print(f"Valid age entered: {age}")
score = get_valid_integer("Enter test score (0-100): ", 0, 100)
if score is not None:
print(f"Valid score entered: {score}")
Custom Exceptions
# Creating custom exceptions
class ValidationError(Exception):
"""Raised when input validation fails"""
pass
class InsufficientFundsError(Exception):
"""Raised when account balance is too low"""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(f"Balance ${balance} is less than ${amount}")
# Using custom exceptions
def withdraw(amount, balance):
if amount <= 0:
raise ValidationError("Amount must be positive")
if amount > balance:
raise InsufficientFundsError(balance, amount)
return balance - amount
# Handling custom exceptions
try:
new_balance = withdraw(1000, 500)
except ValidationError as e:
print(f"Invalid input: {e}")
except InsufficientFundsError as e:
print(f"Transaction failed: {e}")
Practice Problem: ATM System with Custom Exceptions
Problem: Create an ATM class that uses custom exceptions for various error conditions.
Solution:
# ATM System with Custom Exceptions
class ATMError(Exception):
"""Base ATM exception"""
pass
class InvalidPINError(ATMError):
def __init__(self, attempts_remaining):
self.attempts_remaining = attempts_remaining
super().__init__(f"Invalid PIN. {attempts_remaining} attempts remaining")
class DailyLimitError(ATMError):
def __init__(self, limit):
self.limit = limit
super().__init__(f"Daily withdrawal limit of ${limit} exceeded")
class CardBlockedError(ATMError):
pass
class ATM:
def __init__(self, correct_pin, balance, daily_limit=1000):
self.correct_pin = correct_pin
self.balance = balance
self.daily_limit = daily_limit
self.daily_withdrawn = 0
self.pin_attempts = 3
self.blocked = False
def verify_pin(self, pin):
if self.blocked:
raise CardBlockedError("Card is blocked. Contact bank.")
if pin != self.correct_pin:
self.pin_attempts -= 1
if self.pin_attempts <= 0:
self.blocked = True
raise CardBlockedError("Too many failed attempts. Card blocked.")
raise InvalidPINError(self.pin_attempts)
self.pin_attempts = 3 # Reset on success
return True
def withdraw(self, pin, amount):
self.verify_pin(pin)
if self.daily_withdrawn + amount > self.daily_limit:
raise DailyLimitError(self.daily_limit)
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
self.daily_withdrawn += amount
return self.balance
# Test
atm = ATM("1234", 5000)
try:
print(f"New balance: ${atm.withdraw('1234', 200)}")
print(f"New balance: ${atm.withdraw('0000', 100)}") # Wrong PIN
except ATMError as e:
print(f"ATM Error: {e}")
💡 Error Handling Strategy: Be specific with exceptions. Catching generic
Exceptionhides bugs. Always handle expected errors and let unexpected ones propagate (so you can fix them).
Leverage Python’s vast ecosystem of pre-built tools to solve complex problems without reinventing the wheel.
Python’s power comes from its vast ecosystem of libraries.
Importing Modules
# Import entire module
import math
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.14159...
# Import specific functions
from math import sqrt, pow
print(sqrt(16)) # 4.0
# Import with alias
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Import all (generally not recommended)
from math import *
# Custom module (create mymodule.py)
# mymodule.py:
def greet(name):
return f"Hello, {name}!"
# main.py:
import mymodule
print(mymodule.greet("Alice"))
Practice Problem: Math Utilities Module
Problem: Create a custom math utilities module and use it in a main program.
Solution:
# math_utils.py (save as separate file)
"""
Custom math utilities module.
"""
def factorial(n):
"""Calculate factorial of n."""
if n < 0:
raise ValueError("Factorial not defined for negative numbers")
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
def is_prime(n):
"""Check if number is prime."""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
def fibonacci(n):
"""Generate first n Fibonacci numbers."""
if n <= 0:
return []
elif n == 1:
return [0]
fibs = [0, 1]
while len(fibs) < n:
fibs.append(fibs[-1] + fibs[-2])
return fibs
# main.py (separate file)
import math_utils
print(f"5! = {math_utils.factorial(5)}")
print(f"Is 17 prime? {math_utils.is_prime(17)}")
print(f"First 10 Fibonacci: {math_utils.fibonacci(10)}")
Popular Libraries Overview
# NumPy - Numerical computing
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr.mean()) # 3.0
print(arr * 2) # [2 4 6 8 10]
matrix = np.array([[1, 2], [3, 4]])
print(matrix.shape) # (2, 2)
# Pandas - Data manipulation
import pandas as pd
data = {
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35],
'Salary': [50000, 60000, 75000]
}
df = pd.DataFrame(data)
print(df.describe()) # Statistics summary
# Matplotlib - Data visualization
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [1, 4, 9, 16, 25]
plt.plot(x, y, marker='o')
plt.title("Square Numbers")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid(True)
plt.show()
Practice Problem: Data Analysis with Pandas
Problem: Analyze a sales dataset using Pandas to find top products and monthly trends.
Solution:
# Sales Data Analysis with Pandas
import pandas as pd
import matplotlib.pyplot as plt
# Create sample data
data = {
'Date': ['2025-01-15', '2025-01-20', '2025-02-10', '2025-02-25',
'2025-03-05', '2025-03-15', '2025-01-12', '2025-02-18'],
'Product': ['Laptop', 'Mouse', 'Laptop', 'Keyboard',
'Monitor', 'Mouse', 'Keyboard', 'Monitor'],
'Quantity': [2, 10, 1, 5, 3, 8, 2, 4],
'Price': [999.99, 29.99, 999.99, 79.99, 299.99, 29.99, 79.99, 299.99]
}
df = pd.DataFrame(data)
df['Date'] = pd.to_datetime(df['Date'])
df['Revenue'] = df['Quantity'] * df['Price']
df['Month'] = df['Date'].dt.month_name()
# Analysis
print("=== SALES ANALYSIS ===")
print(f"\nTotal Revenue: ${df['Revenue'].sum():.2f}")
print("\nTop Products by Revenue:")
product_revenue = df.groupby('Product')['Revenue'].sum().sort_values(ascending=False)
print(product_revenue)
print("\nMonthly Revenue:")
monthly = df.groupby('Month')['Revenue'].sum()
print(monthly)
# Visualization
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# Product revenue chart
product_revenue.plot(kind='bar', ax=axes[0], color='skyblue')
axes[0].set_title('Revenue by Product')
axes[0].set_ylabel('Revenue ($)')
# Monthly trend
monthly.plot(kind='line', ax=axes[1], marker='o', color='green')
axes[1].set_title('Monthly Revenue Trend')
axes[1].set_ylabel('Revenue ($)')
plt.tight_layout()
plt.savefig('sales_analysis.png')
print("\nChart saved as 'sales_analysis.png'")
💡 Installation: Use
pip install numpy pandas matplotlibto install libraries.
11. Advanced Topics
Explore Python’s powerful advanced features that separate beginners from professional developers.
Once you’re comfortable with basics, explore these powerful features.
Decorators
Decorators modify function behavior without changing their code.
import time
import functools
# Simple decorator
def my_decorator(func):
@functools.wraps(func) # Preserves function metadata
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} finished")
return result
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# Output:
# Calling say_hello
# Hello!
# say_hello finished
# Practical: Timing decorator
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} took {elapsed:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "Done"
slow_function() # slow_function took 1.0012 seconds
Practice Problem: Retry Decorator
Problem: Create a decorator that retries a function up to 3 times if it fails.
Solution:
# Retry Decorator
import time
import random
def retry(max_attempts=3, delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Attempt {attempt} failed: {e}")
if attempt == max_attempts:
raise
time.sleep(delay)
return None
return wrapper
return decorator
# Simulate unreliable API
@retry(max_attempts=3, delay=0.5)
def unreliable_api_call():
if random.random() < 0.7: # 70% failure rate
raise ConnectionError("API timeout")
return "Success!"
# Test
try:
result = unreliable_api_call()
print(f"Final result: {result}")
except Exception as e:
print(f"All retries failed: {e}")
Generators
Generators yield values one at a time, saving memory.
# Generator function
def countdown(n):
while n > 0:
yield n # Pause and return value
n -= 1
# Usage
for num in countdown(5):
print(num) # 5, 4, 3, 2, 1
# Generator expression (like list comprehension but lazy)
squares_gen = (x**2 for x in range(1000000)) # Doesn't create list in memory
# Practical: Reading large files line by line
def read_large_file(file_path):
with open(file_path, "r") as file:
for line in file:
yield line.strip()
# Process without loading entire file
for line in read_large_file("huge_file.txt"):
process_line(line)
Practice Problem: Fibonacci Generator
Problem: Create a generator that produces an infinite sequence of Fibonacci numbers.
Solution:
# Infinite Fibonacci Generator
def fibonacci_generator():
"""Generate infinite Fibonacci sequence."""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Usage: Get first 20 Fibonacci numbers
fib = fibonacci_generator()
first_20 = [next(fib) for _ in range(20)]
print(f"First 20: {first_20}")
# Usage: Find first Fibonacci number > 1000
fib2 = fibonacci_generator()
for num in fib2:
if num > 1000:
print(f"First Fibonacci > 1000: {num}")
break
# Practical: Generator for paginated API results
def api_paginator(url, per_page=10):
"""Simulate paginated API calls."""
page = 1
while True:
# Simulate API call
data = fetch_page(url, page, per_page)
if not data:
break
for item in data:
yield item
page += 1
def fetch_page(url, page, per_page):
# Simulated data
all_data = list(range(1, 26)) # 25 items total
start = (page - 1) * per_page
end = start + per_page
result = all_data[start:end]
print(f"Fetched page {page}: {result}")
return result
# Process all items without loading everything
print("\nProcessing paginated data:")
for item in api_paginator("api.example.com"):
if item > 15: # Stop early condition
break
print(f"Processing item: {item}")
List Comprehensions (Advanced)
# Nested list comprehension
matrix = [[i*j for j in range(1, 4)] for i in range(1, 4)]
# [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
# With conditions
even_squares = [x**2 for x in range(20) if x % 2 == 0]
# Dictionary comprehension
word_lengths = {word: len(word) for word in ["apple", "banana", "cherry"]}
# Set comprehension
unique_lengths = {len(word) for word in ["apple", "banana", "cherry", "pie"]}
Practice Problem: Advanced Comprehensions
Problem: Use comprehensions to process a list of students and create multiple derived data structures.
Solution:
# Advanced Comprehensions Challenge
students = [
{"name": "Alice", "grades": [85, 90, 88], "major": "CS"},
{"name": "Bob", "grades": [78, 82, 80], "major": "Math"},
{"name": "Charlie", "grades": [92, 95, 94], "major": "CS"},
{"name": "Diana", "grades": [88, 85, 90], "major": "Physics"}
]
# 1. List of averages using comprehension
averages = [sum(s["grades"])/len(s["grades"]) for s in students]
print(f"Averages: {averages}")
# 2. Dictionary mapping names to averages
name_average = {s["name"]: sum(s["grades"])/len(s["grades"]) for s in students}
print(f"Name to Average: {name_average}")
# 3. Set of unique majors
majors = {s["major"] for s in students}
print(f"Unique majors: {majors}")
# 4. Filtered list: CS students with average > 90
top_cs = [
s["name"] for s in students
if s["major"] == "CS" and sum(s["grades"])/len(s["grades"]) > 90
]
print(f"Top CS students: {top_cs}")
# 5. Nested comprehension: All grades flattened
all_grades = [grade for s in students for grade in s["grades"]]
print(f"All grades: {all_grades}")
# 6. Complex: Group by major
from collections import defaultdict
by_major = defaultdict(list)
for s in students:
by_major[s["major"]].append(s["name"])
# Or using comprehension with setdefault (less efficient but possible)
by_major2 = {}
for s in students:
by_major2.setdefault(s["major"], []).append(s["name"])
print(f"By major: {dict(by_major)}")
Multithreading & Multiprocessing (Introduction)
import threading
import multiprocessing
import time
# Threading (for I/O-bound tasks)
def download_file(url):
print(f"Downloading {url}")
time.sleep(2) # Simulating download
print(f"Finished {url}")
urls = ["url1", "url2", "url3"]
threads = []
for url in urls:
t = threading.Thread(target=download_file, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join() # Wait for all to complete
# Multiprocessing (for CPU-bound tasks)
def calculate_square(n):
return n * n
if __name__ == "__main__":
numbers = [1, 2, 3, 4, 5]
with multiprocessing.Pool() as pool:
results = pool.map(calculate_square, numbers)
print(results) # [1, 4, 9, 16, 25]
Practice Problem: Concurrent Web Scraper
Problem: Create a program that downloads multiple web pages concurrently using threading.
Solution:
# Concurrent Web Page Downloader
import threading
import time
import requests
from urllib.parse import urlparse
class ConcurrentDownloader:
def __init__(self, max_threads=5):
self.max_threads = max_threads
self.results = {}
self.lock = threading.Lock()
def download(self, url):
"""Download a single URL."""
try:
start = time.time()
response = requests.get(url, timeout=10)
elapsed = time.time() - start
with self.lock:
self.results[url] = {
"status": response.status_code,
"size": len(response.content),
"time": elapsed
}
print(f"✓ {urlparse(url).netloc} ({elapsed:.2f}s)")
except Exception as e:
with self.lock:
self.results[url] = {"error": str(e)}
print(f"✗ {urlparse(url).netloc} - {e}")
def download_all(self, urls):
"""Download all URLs concurrently."""
threads = []
for url in urls:
while threading.active_count() > self.max_threads:
time.sleep(0.1)
t = threading.Thread(target=self.download, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join()
return self.results
# Test
urls = [
"https://www.python.org",
"https://www.github.com",
"https://stackoverflow.com",
"https://www.wikipedia.org",
"https://www.reddit.com"
]
print("Starting concurrent downloads...")
start = time.time()
downloader = ConcurrentDownloader(max_threads=3)
results = downloader.download_all(urls)
total_time = time.time() - start
print(f"\nCompleted in {total_time:.2f} seconds")
print(f"Sequential would take ~{len(urls) * 2}s (estimated)")
# Summary
success = sum(1 for r in results.values() if "error" not in r)
print(f"Successful: {success}/{len(urls)}")
💡 Rule of Thumb: Use threading for waiting operations (downloads, API calls). Use multiprocessing for heavy calculations.
12. Python for Real-World Applications
Discover how Python powers industries from web development to IoT, and start building your specialization.
Web Development
Flask – Lightweight and simple:
from flask import Flask, jsonify, request
app = Flask(__name__)
# In-memory data store
todos = []
@app.route("/")
def home():
return "Welcome to Python API!"
@app.route("/api/todos", methods=["GET"])
def get_todos():
return jsonify(todos)
@app.route("/api/todos", methods=["POST"])
def add_todo():
data = request.json
todos.append(data)
return jsonify({"message": "Todo added!", "todo": data}), 201
@app.route("/api/todos/<int:index>", methods=["DELETE"])
def delete_todo(index):
if 0 <= index < len(todos):
removed = todos.pop(index)
return jsonify({"message": "Deleted", "todo": removed})
return jsonify({"error": "Not found"}), 404
if __name__ == "__main__":
app.run(debug=True)
Django – Full-featured framework for large applications.
Practice Problem: RESTful API with Flask
Problem: Create a complete REST API for a book library with CRUD operations.
Solution:
# Complete Book Library API
from flask import Flask, request, jsonify
from datetime import datetime
app = Flask(__name__)
# Data store with initial data
books = [
{"id": 1, "title": "Python Basics", "author": "John Doe",
"year": 2023, "available": True},
{"id": 2, "title": "Advanced Python", "author": "Jane Smith",
"year": 2024, "available": True}
]
# Helper to find book by ID
def find_book(book_id):
return next((b for b in books if b["id"] == book_id), None)
@app.route("/api/books", methods=["GET"])
def get_books():
"""Get all books with optional filtering."""
author = request.args.get("author")
available = request.args.get("available")
result = books
if author:
result = [b for b in result if author.lower() in b["author"].lower()]
if available is not None:
avail = available.lower() == "true"
result = [b for b in result if b["available"] == avail]
return jsonify({
"count": len(result),
"books": result
})
@app.route("/api/books/<int:book_id>", methods=["GET"])
def get_book(book_id):
"""Get a specific book by ID."""
book = find_book(book_id)
if book:
return jsonify(book)
return jsonify({"error": "Book not found"}), 404
@app.route("/api/books", methods=["POST"])
def create_book():
"""Create a new book."""
data = request.json
# Validation
required = ["title", "author", "year"]
if not all(field in data for field in required):
return jsonify({"error": "Missing required fields"}), 400
new_book = {
"id": max(b["id"] for b in books) + 1 if books else 1,
"title": data["title"],
"author": data["author"],
"year": data["year"],
"available": data.get("available", True),
"created_at": datetime.now().isoformat()
}
books.append(new_book)
return jsonify(new_book), 201
@app.route("/api/books/<int:book_id>", methods=["PUT"])
def update_book(book_id):
"""Update an existing book."""
book = find_book(book_id)
if not book:
return jsonify({"error": "Book not found"}), 404
data = request.json
book.update({
"title": data.get("title", book["title"]),
"author": data.get("author", book["author"]),
"year": data.get("year", book["year"]),
"available": data.get("available", book["available"]),
"updated_at": datetime.now().isoformat()
})
return jsonify(book)
@app.route("/api/books/<int:book_id>", methods=["DELETE"])
def delete_book(book_id):
"""Delete a book."""
book = find_book(book_id)
if not book:
return jsonify({"error": "Book not found"}), 404
books.remove(book)
return jsonify({"message": "Book deleted", "book": book})
if __name__ == "__main__":
app.run(debug=True, port=5000)
Data Science
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# 1. Load data
data = pd.read_csv("sales_data.csv")
# 2. Clean data
data = data.dropna() # Remove missing values
data = data[data["price"] > 0] # Filter outliers
# 3. Analyze
print(data.describe())
print(data.corr()) # Correlation matrix
# 4. Visualize
import matplotlib.pyplot as plt
plt.scatter(data["advertising"], data["sales"])
plt.xlabel("Advertising Budget")
plt.ylabel("Sales")
plt.show()
# 5. Machine Learning
X = data[["advertising", "price"]]
y = data["sales"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = LinearRegression()
model.fit(X_train, y_train)
predictions = model.predict(X_test)
Practice Problem: Complete Data Analysis Pipeline
Problem: Perform end-to-end analysis of a dataset including cleaning, visualization, and prediction.
Solution:
# Complete Data Science Pipeline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, r2_score
# 1. Create synthetic housing data
np.random.seed(42)
n_samples = 1000
data = pd.DataFrame({
'square_feet': np.random.randint(800, 5000, n_samples),
'bedrooms': np.random.randint(1, 6, n_samples),
'bathrooms': np.random.randint(1, 4, n_samples),
'age': np.random.randint(0, 100, n_samples),
'distance_to_city': np.random.uniform(1, 50, n_samples)
})
# Generate price based on features (with noise)
data['price'] = (
data['square_feet'] * 150 +
data['bedrooms'] * 10000 +
data['bathrooms'] * 15000 -
data['age'] * 500 -
data['distance_to_city'] * 1000 +
np.random.normal(0, 50000, n_samples)
)
# 2. Data Exploration
print("=== DATA OVERVIEW ===")
print(data.head())
print(f"\nShape: {data.shape}")
print(f"\nStatistics:\n{data.describe()}")
# 3. Feature Engineering
data['price_per_sqft'] = data['price'] / data['square_feet']
data['total_rooms'] = data['bedrooms'] + data['bathrooms']
# 4. Visualization
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# Price distribution
axes[0,0].hist(data['price'], bins=50, color='skyblue', edgecolor='black')
axes[0,0].set_title('Price Distribution')
axes[0,0].set_xlabel('Price')
# Correlation heatmap
corr = data.corr()
im = axes[0,1].imshow(corr, cmap='coolwarm', aspect='auto')
axes[0,1].set_xticks(range(len(corr.columns)))
axes[0,1].set_yticks(range(len(corr.columns)))
axes[0,1].set_xticklabels(corr.columns, rotation=45, ha='right')
axes[0,1].set_yticklabels(corr.columns)
axes[0,1].set_title('Feature Correlation')
# Price vs Square Feet
axes[1,0].scatter(data['square_feet'], data['price'], alpha=0.5, color='green')
axes[1,0].set_xlabel('Square Feet')
axes[1,0].set_ylabel('Price')
axes[1,0].set_title('Price vs Square Feet')
# Price by bedrooms
data.boxplot(column='price', by='bedrooms', ax=axes[1,1])
axes[1,1].set_title('Price by Number of Bedrooms')
plt.tight_layout()
plt.savefig('housing_analysis.png')
print("\nVisualization saved as 'housing_analysis.png'")
# 5. Machine Learning
features = ['square_feet', 'bedrooms', 'bathrooms', 'age', 'distance_to_city']
X = data[features]
y = data['price']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Scale features
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Train model
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)
# Evaluate
predictions = model.predict(X_test_scaled)
mae = mean_absolute_error(y_test, predictions)
r2 = r2_score(y_test, predictions)
print(f"\n=== MODEL PERFORMANCE ===")
print(f"Mean Absolute Error: ${mae:,.2f}")
print(f"R² Score: {r2:.4f}")
# Feature importance
importance = pd.DataFrame({
'feature': features,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
print(f"\nFeature Importance:\n{importance}")
Automation
import os
import shutil
from datetime import datetime
def organize_downloads():
"""Automatically organize Downloads folder by file type"""
downloads_path = os.path.expanduser("~/Downloads")
# File type categories
categories = {
"Images": [".jpg", ".jpeg", ".png", ".gif"],
"Documents": [".pdf", ".doc", ".docx", ".txt"],
"Videos": [".mp4", ".avi", ".mov"],
"Archives": [".zip", ".rar", ".7z"]
}
# Create folders if they don't exist
for category in categories:
folder = os.path.join(downloads_path, category)
os.makedirs(folder, exist_ok=True)
# Organize files
for filename in os.listdir(downloads_path):
file_path = os.path.join(downloads_path, filename)
if os.path.isfile(file_path):
# Find appropriate category
moved = False
for category, extensions in categories.items():
if any(filename.lower().endswith(ext) for ext in extensions):
dest = os.path.join(downloads_path, category, filename)
shutil.move(file_path, dest)
print(f"Moved {filename} to {category}")
moved = True
break
if not moved:
print(f"Skipped {filename}")
if __name__ == "__main__":
organize_downloads()
Practice Problem: Automated Email Report Generator
Problem: Create a script that generates and sends automated reports via email with attachments.
Solution:
# Automated Report Generator and Email Sender
import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import pandas as pd
from datetime import datetime
import os
class ReportAutomation:
def __init__(self, smtp_server, port, sender_email, password):
self.smtp_server = smtp_server
self.port = port
self.sender_email = sender_email
self.password = password
def generate_sales_report(self, data_file):
"""Generate HTML report from CSV data."""
df = pd.read_csv(data_file)
# Calculate metrics
total_sales = df['sales'].sum()
avg_sales = df['sales'].mean()
top_product = df.loc[df['sales'].idxmax(), 'product']
# Create HTML report
html = f"""
<html>
<head><style>
body {{ font-family: Arial, sans-serif; }}
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #4CAF50; color: white; }}
.metric {{ font-size: 24px; color: #4CAF50; font-weight: bold; }}
</style></head>
<body>
<h2>Sales Report - {datetime.now().strftime('%Y-%m-%d')}</h2>
<h3>Key Metrics</h3>
<p>Total Sales: <span class="metric">${total_sales:,.2f}</span></p>
<p>Average Sales: <span class="metric">${avg_sales:,.2f}</span></p>
<p>Top Product: <span class="metric">{top_product}</span></p>
<h3>Detailed Data</h3>
{df.to_html(index=False)}
</body>
</html>
"""
report_file = f"sales_report_{datetime.now().strftime('%Y%m%d')}.html"
with open(report_file, "w") as f:
f.write(html)
return report_file, total_sales, avg_sales
def send_email(self, recipient, subject, body, attachments=None):
"""Send email with optional attachments."""
msg = MIMEMultipart()
msg['From'] = self.sender_email
msg['To'] = recipient
msg['Subject'] = subject
msg.attach(MIMEText(body, 'html'))
# Attach files
if attachments:
for file_path in attachments:
with open(file_path, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename= {os.path.basename(file_path)}",
)
msg.attach(part)
# Send
context = ssl.create_default_context()
with smtplib.SMTP_SSL(self.smtp_server, self.port, context=context) as server:
server.login(self.sender_email, self.password)
server.sendmail(self.sender_email, recipient, msg.as_string())
print(f"Email sent to {recipient}")
# Usage example
if __name__ == "__main__":
# Create sample data
sample_data = pd.DataFrame({
'product': ['Laptop', 'Mouse', 'Keyboard', 'Monitor'],
'sales': [45000, 8900, 12000, 28000],
'quantity': [45, 890, 120, 56]
})
sample_data.to_csv("sales_data.csv", index=False)
# Initialize and run
automation = ReportAutomation(
smtp_server="smtp.gmail.com",
port=465,
sender_email="your_email@gmail.com",
password="your_app_password"
)
report_file, total, avg = automation.generate_sales_report("sales_data.csv")
automation.send_email(
recipient="manager@company.com",
subject=f"Daily Sales Report - {datetime.now().strftime('%Y-%m-%d')}",
body=f"<p>Please find attached the sales report.</p><p>Total: ${total:,.2f}</p>",
attachments=[report_file]
)
Embedded/IoT Applications
import RPi.GPIO as GPIO
import time
import Adafruit_DHT
# Setup
sensor = Adafruit_DHT.DHT11
pin = 4
def read_temperature():
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
if humidity is not None and temperature is not None:
print(f"Temp: {temperature}°C, Humidity: {humidity}%")
return temperature, humidity
else:
print("Failed to get reading")
return None, None
def control_led(state):
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
GPIO.output(18, state)
# Main loop
try:
while True:
temp, hum = read_temperature()
if temp and temp > 25:
control_led(True) # Turn on fan/LED if hot
else:
control_led(False)
time.sleep(2)
except KeyboardInterrupt:
GPIO.cleanup()
Practice Problem: Smart Home Monitor with Data Logging
Problem: Create an IoT system that monitors temperature, logs data to CSV, and sends alerts when thresholds are exceeded.
Solution:
# Smart Home IoT Monitor
import Adafruit_DHT
import RPi.GPIO as GPIO
import csv
import time
import json
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
class SmartHomeMonitor:
def __init__(self, config_file="config.json"):
# Load configuration
with open(config_file, 'r') as f:
self.config = json.load(f)
# Setup GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Sensor setup
self.sensor = Adafruit_DHT.DHT22
self.sensor_pin = self.config['sensor_pin']
# Actuator pins
self.fan_pin = self.config['fan_pin']
self.led_pin = self.config['led_pin']
GPIO.setup(self.fan_pin, GPIO.OUT)
GPIO.setup(self.led_pin, GPIO.OUT)
# Thresholds
self.temp_high = self.config['temp_high_threshold']
self.temp_low = self.config['temp_low_threshold']
self.humidity_high = self.config['humidity_high_threshold']
# Data logging
self.csv_file = self.config['data_file']
self.ensure_csv_exists()
# Alert tracking
self.last_alert_time = 0
self.alert_cooldown = 300 # 5 minutes between alerts
def ensure_csv_exists(self):
"""Create CSV with headers if it doesn't exist."""
try:
with open(self.csv_file, 'x', newline='') as f:
writer = csv.writer(f)
writer.writerow(['timestamp', 'temperature', 'humidity',
'fan_status', 'alert_triggered'])
except FileExistsError:
pass
def read_sensors(self):
"""Read temperature and humidity from DHT sensor."""
humidity, temperature = Adafruit_DHT.read_retry(self.sensor, self.sensor_pin)
return temperature, humidity
def control_fan(self, state):
"""Turn cooling fan on or off."""
GPIO.output(self.fan_pin, state)
return state
def control_led(self, state):
"""Control status LED."""
GPIO.output(self.led_pin, state)
def log_data(self, temp, humidity, fan_status, alert):
"""Append sensor data to CSV file."""
with open(self.csv_file, 'a', newline='') as f:
writer = csv.writer(f)
writer.writerow([
datetime.now().isoformat(),
temp,
humidity,
fan_status,
alert
])
def send_alert(self, subject, message):
"""Send email alert if cooldown period has passed."""
current_time = time.time()
if current_time - self.last_alert_time < self.alert_cooldown:
return
try:
msg = MIMEText(message)
msg['Subject'] = subject
msg['From'] = self.config['email_sender']
msg['To'] = self.config['email_recipient']
server = smtplib.SMTP(self.config['smtp_server'], 587)
server.starttls()
server.login(self.config['email_sender'],
self.config['email_password'])
server.send_message(msg)
server.quit()
self.last_alert_time = current_time
print(f"Alert sent: {subject}")
except Exception as e:
print(f"Failed to send alert: {e}")
def check_thresholds(self, temp, humidity):
"""Check sensor values against thresholds and take action."""
alert_triggered = False
alert_message = ""
# Temperature too high
if temp > self.temp_high:
self.control_fan(True)
self.control_led(True)
alert_triggered = True
alert_message += f"HIGH TEMPERATURE ALERT: {temp}°C\n"
# Temperature too low
elif temp < self.temp_low:
alert_triggered = True
alert_message += f"LOW TEMPERATURE ALERT: {temp}°C\n"
# Humidity too high
if humidity > self.humidity_high:
alert_triggered = True
alert_message += f"HIGH HUMIDITY ALERT: {humidity}%\n"
# Normal conditions
if self.temp_low <= temp <= self.temp_high:
self.control_fan(False)
self.control_led(False)
# Send alert if needed
if alert_triggered:
self.send_alert(
"Smart Home Alert - Environmental Threshold Exceeded",
alert_message + f"\nTimestamp: {datetime.now()}"
)
return alert_triggered
def get_statistics(self, hours=24):
"""Calculate statistics from recent data."""
try:
with open(self.csv_file, 'r') as f:
reader = csv.DictReader(f)
data = list(reader)
if not data:
return None
# Get recent entries
recent = data[-min(len(data), hours * 30):] # Approx 2 readings per min
temps = [float(row['temperature']) for row in recent]
humidities = [float(row['humidity']) for row in recent]
return {
'avg_temp': sum(temps) / len(temps),
'max_temp': max(temps),
'min_temp': min(temps),
'avg_humidity': sum(humidities) / len(humidities),
'readings_count': len(recent)
}
except Exception as e:
print(f"Error calculating statistics: {e}")
return None
def run(self):
"""Main monitoring loop."""
print("Smart Home Monitor Started")
print(f"Temperature thresholds: {self.temp_low}°C - {self.temp_high}°C")
print(f"Humidity threshold: {self.humidity_high}%")
try:
while True:
temp, humidity = self.read_sensors()
if temp is not None and humidity is not None:
alert = self.check_thresholds(temp, humidity)
fan_status = GPIO.input(self.fan_pin)
self.log_data(temp, humidity, fan_status, alert)
print(f"[{datetime.now().strftime('%H:%M:%S')}] "
f"Temp: {temp:.1f}°C, Humidity: {humidity:.1f}%, "
f"Fan: {'ON' if fan_status else 'OFF'}")
# Print hourly statistics
if datetime.now().minute == 0:
stats = self.get_statistics(1)
if stats:
print(f"\n--- Hourly Stats ---")
print(f"Avg Temp: {stats['avg_temp']:.1f}°C")
print(f"Max Temp: {stats['max_temp']:.1f}°C")
print(f"-------------------\n")
time.sleep(30) # Read every 30 seconds
except KeyboardInterrupt:
print("\nShutting down...")
finally:
GPIO.cleanup()
print("GPIO cleaned up")
# Configuration file (config.json)
"""
{
"sensor_pin": 4,
"fan_pin": 18,
"led_pin": 23,
"temp_high_threshold": 28,
"temp_low_threshold": 18,
"humidity_high_threshold": 70,
"data_file": "sensor_data.csv",
"email_sender": "your_email@gmail.com",
"email_password": "app_password",
"email_recipient": "alert@example.com",
"smtp_server": "smtp.gmail.com"
}
"""
# Run the monitor
if __name__ == "__main__":
monitor = SmartHomeMonitor()
monitor.run()
13. Best Practices
Write professional-quality code that is readable, maintainable, and follows industry standards.
Code Readability
# Bad: Unclear variable names, no spacing
def calc(a,b):
return a+b if a>0 else b
# Good: Descriptive names, proper formatting
def calculate_total_price(base_price, tax_rate):
"""
Calculate final price including tax.
Args:
base_price (float): Price before tax
tax_rate (float): Tax percentage (e.g., 0.08 for 8%)
Returns:
float: Total price with tax
"""
if base_price <= 0:
raise ValueError("Price must be positive")
tax_amount = base_price * tax_rate
total_price = base_price + tax_amount
return round(total_price, 2)
Practice Problem: Refactoring Challenge
Problem: Refactor this poorly written code into clean, readable Python.
Before (Bad Code):
def x(a,b,c):
if c==1:
return a+b
elif c==2:
return a-b
elif c==3:
return a*b
else:
return a/b if b!=0 else 'err'
After (Clean Code):
from enum import Enum
from typing import Union
class Operation(Enum):
ADD = 1
SUBTRACT = 2
MULTIPLY = 3
DIVIDE = 4
def calculate(operand1: float,
operand2: float,
operation: Operation) -> Union[float, str]:
"""
Perform basic arithmetic operations.
Args:
operand1: First number
operand2: Second number
operation: Type of operation to perform
Returns:
Result of calculation or error message
Raises:
ValueError: If operation is invalid
ZeroDivisionError: If dividing by zero
"""
if not isinstance(operation, Operation):
raise ValueError(f"Invalid operation: {operation}")
operations = {
Operation.ADD: lambda x, y: x + y,
Operation.SUBTRACT: lambda x, y: x - y,
Operation.MULTIPLY: lambda x, y: x * y,
Operation.DIVIDE: lambda x, y: x / y if y != 0 else float('inf')
}
result = operations[operation](operand1, operand2)
if result == float('inf'):
raise ZeroDivisionError("Cannot divide by zero")
return result
# Usage
try:
result = calculate(10, 5, Operation.ADD)
print(f"Result: {result}")
except (ValueError, ZeroDivisionError) as e:
print(f"Error: {e}")
Naming Conventions (PEP 8)
| Type | Convention | Example |
|---|---|---|
| Variables | snake_case | user_name, total_count |
| Functions | snake_case | calculate_total() |
| Classes | PascalCase | BankAccount, UserProfile |
| Constants | UPPER_SNAKE_CASE | MAX_SIZE, PI |
| Private | _leading_underscore | _internal_value |
| Strongly Private | __double_underscore | __secret_key |
Writing Clean Code
# 1. Keep functions small and focused
def get_user_data(user_id): # Good: One responsibility
pass
def get_user_data_and_update_log_and_send_email(user_id): # Bad: Too much
# 2. Avoid magic numbers
if status == 1: # Bad: What is 1?
if status == STATUS_ACTIVE: # Good: Named constant
# 3. Use docstrings
def complex_calculation(x, y):
"""
Perform complex mathematical operation.
Formula: (x^2 + y^2) / (x * y)
Args:
x (float): First parameter, must be non-zero
y (float): Second parameter, must be non-zero
Returns:
float: Result of calculation
Raises:
ValueError: If x or y is zero
"""
if x == 0 or y == 0:
raise ValueError("Parameters cannot be zero")
return (x**2 + y**2) / (x * y)
# 4. Handle errors gracefully
try:
result = risky_operation()
except SpecificException as e:
logger.error(f"Operation failed: {e}")
result = default_value
Practice Problem: Complete Best Practices Example
Problem: Create a well-documented, clean module for a customer management system following all best practices.
Solution:
"""
Customer Management System
This module provides classes and functions for managing customer data,
including CRUD operations and data validation.
Author: Your Name
Version: 1.0.0
License: MIT
"""
from dataclasses import dataclass
from datetime import datetime
from typing import List, Optional, Dict
from enum import Enum
import re
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Constants
MAX_NAME_LENGTH = 100
MIN_AGE = 18
EMAIL_REGEX = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
class CustomerStatus(Enum):
"""Enumeration of possible customer statuses."""
ACTIVE = "active"
INACTIVE = "inactive"
SUSPENDED = "suspended"
VIP = "vip"
@dataclass
class Address:
"""Represents a physical address."""
street: str
city: str
postal_code: str
country: str
def __post_init__(self):
"""Validate address data after initialization."""
if not self.street or not self.city:
raise ValueError("Street and city are required")
def format_address(self) -> str:
"""Return formatted address string."""
return f"{self.street}, {self.city}, {self.postal_code}, {self.country}"
class Customer:
"""
Represents a customer in the system.
Attributes:
customer_id: Unique identifier
first_name: Customer's first name
last_name: Customer's last name
email: Validated email address
age: Customer age (must be >= MIN_AGE)
status: Current customer status
address: Optional physical address
created_at: Timestamp of creation
"""
def __init__(
self,
customer_id: int,
first_name: str,
last_name: str,
email: str,
age: int,
status: CustomerStatus = CustomerStatus.ACTIVE,
address: Optional[Address] = None
):
self._customer_id = customer_id
self._created_at = datetime.now()
self.first_name = first_name
self.last_name = last_name
self.email = email
self.age = age
self.status = status
self.address = address
self._validate()
logger.info(f"Created customer: {self.full_name}")
def _validate(self) -> None:
"""Validate all customer data."""
if not self.first_name or len(self.first_name) > MAX_NAME_LENGTH:
raise ValueError(f"First name must be 1-{MAX_NAME_LENGTH} characters")
if not self.last_name or len(self.last_name) > MAX_NAME_LENGTH:
raise ValueError(f"Last name must be 1-{MAX_NAME_LENGTH} characters")
if not EMAIL_REGEX.match(self.email):
raise ValueError("Invalid email format")
if self.age < MIN_AGE:
raise ValueError(f"Customer must be at least {MIN_AGE} years old")
@property
def customer_id(self) -> int:
"""Get customer ID (read-only)."""
return self._customer_id
@property
def full_name(self) -> str:
"""Get customer's full name."""
return f"{self.first_name} {self.last_name}"
@property
def is_active(self) -> bool:
"""Check if customer is active."""
return self.status == CustomerStatus.ACTIVE
def update_status(self, new_status: CustomerStatus) -> None:
"""
Update customer status with logging.
Args:
new_status: New status to set
Raises:
ValueError: If status transition is invalid
"""
if self.status == CustomerStatus.SUSPENDED and new_status == CustomerStatus.VIP:
raise ValueError("Cannot upgrade suspended customer directly to VIP")
old_status = self.status
self.status = new_status
logger.info(f"Status changed for {self.full_name}: {old_status.value} -> {new_status.value}")
def to_dict(self) -> Dict:
"""Convert customer to dictionary representation."""
return {
"customer_id": self.customer_id,
"full_name": self.full_name,
"email": self.email,
"age": self.age,
"status": self.status.value,
"address": self.address.format_address() if self.address else None,
"created_at": self._created_at.isoformat(),
"is_active": self.is_active
}
def __str__(self) -> str:
return f"Customer({self.customer_id}): {self.full_name} [{self.status.value}]"
def __repr__(self) -> str:
return (f"Customer(customer_id={self.customer_id}, "
f"first_name='{self.first_name}', "
f"last_name='{self.last_name}', "
f"email='{self.email}')")
class CustomerRepository:
"""
Repository for customer data access.
Provides abstraction layer for customer storage operations.
"""
def __init__(self):
self._customers: Dict[int, Customer] = {}
self._next_id: int = 1
def create(
self,
first_name: str,
last_name: str,
email: str,
age: int,
**kwargs
) -> Customer:
"""
Create and store new customer.
Args:
first_name: Customer's first name
last_name: Customer's last name
email: Unique email address
age: Customer age
**kwargs: Additional optional parameters
Returns:
Created Customer instance
Raises:
ValueError: If email already exists
"""
# Check for duplicate email
for customer in self._customers.values():
if customer.email == email:
raise ValueError(f"Customer with email {email} already exists")
customer = Customer(
customer_id=self._next_id,
first_name=first_name,
last_name=last_name,
email=email,
age=age,
**kwargs
)
self._customers[self._next_id] = customer
self._next_id += 1
return customer
def get_by_id(self, customer_id: int) -> Optional[Customer]:
"""Retrieve customer by ID."""
return self._customers.get(customer_id)
def get_all_active(self) -> List[Customer]:
"""Get all active customers."""
return [c for c in self._customers.values() if c.is_active]
def search_by_name(self, query: str) -> List[Customer]:
"""
Search customers by name (case-insensitive).
Args:
query: Search string
Returns:
List of matching customers
"""
query = query.lower()
return [
c for c in self._customers.values()
if query in c.full_name.lower()
]
def delete(self, customer_id: int) -> bool:
"""
Soft-delete customer by setting inactive.
Args:
customer_id: ID of customer to delete
Returns:
True if found and deleted, False otherwise
"""
customer = self._customers.get(customer_id)
if customer:
customer.update_status(CustomerStatus.INACTIVE)
logger.info(f"Soft-deleted customer {customer_id}")
return True
return False
# Example usage
if __name__ == "__main__":
# Initialize repository
repo = CustomerRepository()
# Create customers
try:
customer1 = repo.create(
first_name="Alice",
last_name="Johnson",
email="alice@example.com",
age=30,
address=Address("123 Main St", "NYC", "10001", "USA")
)
customer2 = repo.create(
first_name="Bob",
last_name="Smith",
email="bob@example.com",
age=25
)
print(f"Created: {customer1}")
print(f"Created: {customer2}")
# Search
results = repo.search_by_name("ali")
print(f"\nSearch results: {[c.full_name for c in results]}")
# Get active
active = repo.get_all_active()
print(f"\nActive customers: {len(active)}")
# Convert to dict for API response
print(f"\nCustomer data: {customer1.to_dict()}")
except ValueError as e:
logger.error(f"Validation error: {e}")
14. Mini Projects
Apply everything you’ve learned by building real projects that showcase your skills.
Project 1: Calculator (Beginner)
def calculator():
"""Simple command-line calculator"""
print("=== Python Calculator ===")
while True:
print("\nOptions: +, -, *, /, quit")
choice = input("Enter operation: ").strip().lower()
if choice == 'quit':
print("Goodbye!")
break
if choice not in ['+', '-', '*', '/']:
print("Invalid operation!")
continue
try:
num1 = float(input("Enter first number: "))
num2 = float(input("Enter second number: "))
except ValueError:
print("Invalid number!")
continue
if choice == '+':
result = num1 + num2
elif choice == '-':
result = num1 - num2
elif choice == '*':
result = num1 * num2
elif choice == '/':
if num2 == 0:
print("Cannot divide by zero!")
continue
result = num1 / num2
print(f"Result: {result}")
if __name__ == "__main__":
calculator()
Project 2: To-Do List App (Intermediate)
import json
import os
from datetime import datetime
class TodoList:
def __init__(self, filename="tasks.json"):
self.filename = filename
self.tasks = []
self.load_tasks()
def load_tasks(self):
if os.path.exists(self.filename):
with open(self.filename, "r") as f:
self.tasks = json.load(f)
def save_tasks(self):
with open(self.filename, "w") as f:
json.dump(self.tasks, f, indent=2)
def add_task(self, description, priority="medium"):
task = {
"id": len(self.tasks) + 1,
"description": description,
"priority": priority,
"completed": False,
"created_at": datetime.now().isoformat()
}
self.tasks.append(task)
self.save_tasks()
print(f"Task added: {description}")
def list_tasks(self):
if not self.tasks:
print("No tasks found!")
return
print(f"\n{'ID':<5} {'Description':<30} {'Priority':<10} {'Status':<10}")
print("-" * 60)
for task in self.tasks:
status = "✓ Done" if task["completed"] else "○ Pending"
print(f"{task['id']:<5} {task['description']:<30} "
f"{task['priority']:<10} {status:<10}")
def complete_task(self, task_id):
for task in self.tasks:
if task["id"] == task_id:
task["completed"] = True
self.save_tasks()
print(f"Task {task_id} marked as complete!")
return
print(f"Task {task_id} not found!")
def delete_task(self, task_id):
self.tasks = [t for t in self.tasks if t["id"] != task_id]
self.save_tasks()
print(f"Task {task_id} deleted!")
def main():
todo = TodoList()
while True:
print("\n=== TODO LIST ===")
print("1. Add Task")
print("2. List Tasks")
print("3. Complete Task")
print("4. Delete Task")
print("5. Quit")
choice = input("\nSelect option: ").strip()
if choice == "1":
desc = input("Task description: ")
priority = input("Priority (low/medium/high): ") or "medium"
todo.add_task(desc, priority)
elif choice == "2":
todo.list_tasks()
elif choice == "3":
try:
task_id = int(input("Task ID to complete: "))
todo.complete_task(task_id)
except ValueError:
print("Invalid ID!")
elif choice == "4":
try:
task_id = int(input("Task ID to delete: "))
todo.delete_task(task_id)
except ValueError:
print("Invalid ID!")
elif choice == "5":
print("Goodbye!")
break
else:
print("Invalid option!")
if __name__ == "__main__":
main()
Project 3: Web Scraper (Advanced)
import requests
from bs4 import BeautifulSoup
import csv
import time
import logging
from urllib.parse import urljoin, urlparse
from dataclasses import dataclass
from typing import List, Optional
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@dataclass
class ScrapedItem:
"""Data class for scraped items."""
title: str
url: str
price: Optional[str] = None
description: Optional[str] = None
class WebScraper:
"""
Robust web scraper with rate limiting, error handling, and data export.
"""
def __init__(
self,
base_url: str,
delay: float = 1.0,
max_retries: int = 3,
timeout: int = 10
):
self.base_url = base_url
self.delay = delay
self.max_retries = max_retries
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/91.0.4472.124 Safari/537.36'
})
self.scraped_items: List[ScrapedItem] = []
def _make_request(self, url: str) -> Optional[str]:
"""
Make HTTP request with retry logic.
Args:
url: URL to fetch
Returns:
HTML content or None if failed
"""
for attempt in range(self.max_retries):
try:
logger.info(f"Fetching {url} (attempt {attempt + 1})")
time.sleep(self.delay)
response = self.session.get(
url,
timeout=self.timeout,
allow_redirects=True
)
response.raise_for_status()
return response.text
except requests.RequestException as e:
logger.warning(f"Attempt {attempt + 1} failed: {e}")
if attempt == self.max_retries - 1:
logger.error(f"Failed to fetch {url} after {self.max_retries} attempts")
return None
time.sleep(self.delay * (attempt + 1)) # Exponential backoff
return None
def _parse_item(self, element: BeautifulSoup) -> Optional[ScrapedItem]:
"""
Extract data from HTML element.
Override this method for specific sites.
Args:
element: BeautifulSoup element
Returns:
ScrapedItem or None
"""
try:
title = element.get_text(strip=True)
link = element.get('href', '')
# Convert relative URLs to absolute
if link and not link.startswith('http'):
link = urljoin(self.base_url, link)
return ScrapedItem(title=title, url=link)
except Exception as e:
logger.error(f"Error parsing element: {e}")
return None
def scrape_page(self, url: str, selector: str) -> int:
"""
Scrape a single page.
Args:
url: Page URL
selector: CSS selector for target elements
Returns:
Number of items scraped
"""
html = self._make_request(url)
if not html:
return 0
soup = BeautifulSoup(html, 'html.parser')
elements = soup.select(selector)
count = 0
for element in elements:
item = self._parse_item(element)
if item:
self.scraped_items.append(item)
count += 1
logger.info(f"Scraped {count} items from {url}")
return count
def scrape_multiple(
self,
start_url: str,
selector: str,
pages: int = 1,
page_param: str = "page"
) -> None:
"""
Scrape multiple pages.
Args:
start_url: Base URL
selector: CSS selector
pages: Number of pages to scrape
page_param: Query parameter for pagination
"""
for page in range(1, pages + 1):
# Simple pagination - modify based on site structure
separator = '&' if '?' in start_url else '?'
url = f"{start_url}{separator}{page_param}={page}"
count = self.scrape_page(url, selector)
if count == 0:
logger.info(f"No items found on page {page}, stopping.")
break
def export_to_csv(self, filename: str) -> None:
"""
Export scraped data to CSV.
Args:
filename: Output filename
"""
if not self.scraped_items:
logger.warning("No data to export")
return
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(
f,
fieldnames=self.scraped_items[0].__dict__.keys()
)
writer.writeheader()
for item in self.scraped_items:
writer.writerow(item.__dict__)
logger.info(f"Exported {len(self.scraped_items)} items to {filename}")
def get_stats(self) -> dict:
"""Get scraping statistics."""
return {
'total_items': len(self.scraped_items),
'unique_urls': len(set(item.url for item in self.scraped_items)),
'domains': len(set(
urlparse(item.url).netloc
for item in self.scraped_items
if item.url
))
}
# Example: E-commerce product scraper
class ProductScraper(WebScraper):
"""Specialized scraper for e-commerce sites."""
def _parse_item(self, element: BeautifulSoup) -> Optional[ScrapedItem]:
"""Parse product-specific data."""
try:
# Try different common selectors
title_elem = (
element.select_one('.product-title') or
element.select_one('h2') or
element.select_one('h3') or
element
)
price_elem = (
element.select_one('.price') or
element.select_one('.product-price') or
element.select_one('[class*="price"]')
)
link_elem = (
element.select_one('a') or
element
)
title = title_elem.get_text(strip=True) if title_elem else "Unknown"
price = price_elem.get_text(strip=True) if price_elem else None
link = link_elem.get('href', '') if link_elem else ''
if link and not link.startswith('http'):
link = urljoin(self.base_url, link)
return ScrapedItem(
title=title,
url=link,
price=price
)
except Exception as e:
logger.error(f"Error parsing product: {e}")
return None
# Usage example
if __name__ == "__main__":
# Example: Scrape a hypothetical site
scraper = ProductScraper(
base_url="https://example-shop.com",
delay=2.0, # Be respectful with delays
max_retries=3
)
try:
scraper.scrape_multiple(
start_url="https://example-shop.com/products",
selector=".product-item",
pages=3
)
# Print results
print(f"\n{'Title':<50} {'Price':<15}")
print("-" * 65)
for item in scraper.scraped_items[:10]: # Show first 10
print(f"{item.title[:48]:<50} {item.price or 'N/A':<15}")
# Export
scraper.export_to_csv("products.csv")
# Stats
stats = scraper.get_stats()
print(f"\nScraping complete!")
print(f"Total items: {stats['total_items']}")
print(f"Unique URLs: {stats['unique_urls']}")
except KeyboardInterrupt:
logger.info("Scraping interrupted by user")
scraper.export_to_csv("products_partial.csv")
15. Conclusion
Your roadmap to Python mastery and the next steps in your programming journey.
Learning Roadmap
Month 1: Foundations ├── Week 1-2: Basics (variables, data types, operators) ├── Week 3: Control structures (if/else, loops) └── Week 4: Functions and basic file handling Month 2: Intermediate ├── Week 1: Data structures (lists, dicts, sets) ├── Week 2: OOP (classes, inheritance) ├── Week 3: Error handling and modules └── Week 4: Mini projects (calculator, to-do app) Month 3: Advanced & Specialization ├── Week 1: Advanced topics (decorators, generators) ├── Week 2: Choose path (Web/Data/Automation/IoT) ├── Week 3: Frameworks and libraries └── Week 4: Capstone project

Next Steps
- Practice Daily: Code for at least 30 minutes every day
- Build Projects: Apply what you learn immediately
- Read Code: Study open-source projects on GitHub
- Join Communities: r/learnpython, Stack Overflow, Discord servers
- Contribute: Start with documentation, then code
🎯 Remember: Every expert was once a beginner. Python is forgiving, the community is welcoming, and the possibilities are endless. Your journey starts with a single line of code.
Start coding today, and build the future you want to see!
Frequently Asked Questions (FAQ)
Is Python easy to learn?
Yes! Python is consistently ranked as the best programming language for beginners. Its syntax reads like English, and you can write useful programs within hours of starting. The gentle learning curve doesn’t mean it’s limited – Python scales from simple scripts to complex AI systems.
How long does it take to learn Python?
- Basic proficiency: 2-3 months of consistent practice (1-2 hours/day)
- Intermediate level: 6-12 months (can build real projects)
- Advanced/Job-ready: 1-2 years (depending on specialization)
The key is consistent practice and building projects, not just watching tutorials.
What can I build with Python?
Almost anything! Popular projects include:
- Websites and web applications (Instagram, Pinterest use Python)
- Data analysis and visualization dashboards
- Machine learning models and AI chatbots
- Automation scripts to save hours of manual work
- Desktop applications with GUI
- Games (2D games with Pygame)
- IoT devices and robotics projects
Is Python good for embedded systems and IoT?
Yes, increasingly so. While C/C++ dominate resource-constrained microcontrollers, Python shines in:
- Raspberry Pi projects (full Linux environment)
- MicroPython on ESP32 and other microcontrollers
- CircuitPython for education and rapid prototyping
- Gateway devices and edge computing with more resources
For production IoT with strict memory constraints, C/C++ might be better, but Python is excellent for prototyping and higher-level IoT applications.
Which IDE is best for Python?
For beginners: VS Code (free, lightweight, great extensions) For professionals: PyCharm (excellent debugging, refactoring) For data science: Jupyter Notebook (interactive exploration) For web development: VS Code or PyCharm Professional
All are excellent choices – pick one and stick with it rather than constantly switching.
Python vs C/C++ for beginners?
Start with Python if you’re new to programming:
- ✅ Faster to see results (no compilation)
- ✅ Easier syntax (no semicolons, braces)
- ✅ Better error messages
- ✅ Huge library ecosystem
- ✅ More job opportunities for beginners
Learn C/C++ later if you need:
- Maximum performance (game engines, high-frequency trading)
- System-level programming (operating systems, drivers)
- Embedded systems with strict memory limits
- Understanding how computers actually work
Many successful programmers know both – start with Python, then add C/C++ to your toolkit.
Do I need to learn Python 2?
No! Python 2 reached end-of-life in 2020. Always use Python 3.8 or newer. Python 2 code is legacy maintenance only.
Can I get a job knowing only Python?
Yes, but specialize. Pure “Python programmer” jobs are rare. Combine Python with:
- Web: Django/Flask + HTML/CSS/JavaScript
- Data: SQL + Statistics + Machine Learning
- DevOps: Linux + Docker + Cloud platforms
- Automation: Testing frameworks + CI/CD
Is Python slow?
For most tasks, no. While Python is slower than C++ for raw computation, it’s “fast enough” for:
- Web servers (Instagram handles millions of users)
- Data processing (Pandas uses optimized C underneath)
- Prototyping (development speed matters more)
When speed is critical, use:
- NumPy/Pandas for data (C-optimized)
- Cython to compile Python to C
- Multiprocessing for parallel tasks
- Rewrite hotspots in C/C++
How do I stay motivated while learning?
- Build projects you care about (automate a boring task)
- Join communities (find learning buddies)
- Track progress (GitHub commits, project portfolio)
- Teach others (explain concepts to solidify understanding)
- Celebrate small wins (first working program, first bug fixed)
Call to Action
🚀 Ready to continue your Python journey?
- Subscribe to our YouTube channel for weekly Python tutorials and project walkthroughs
- Download our free Python Cheat Sheet (PDF) for quick reference
- Join our Discord community to connect with 10,000+ Python learners
- Explore our advanced tutorials on Django, Machine Learning, and Automation
- Share this guide with a friend who’s learning to code!
Happy Coding! 🐍✨
Tags: #Python #Programming #LearnToCode #PythonTutorial #CodingForBeginners #SoftwareDevelopment #DataScience #WebDevelopment #Automation #IoT
