This page shows the source code for Session-3-2-Examples.py in browser-friendly HTML format. It was generated automatically from the original Python file.
"""
Advanced Python - Session 2: Advanced Functions & Iterators
Code Examples and Projects
"""
from functools import wraps, reduce
from contextlib import contextmanager
import time
from datetime import datetime
import csv
# ============================================
# PART 1: Lambda Functions & Functional Programming
# ============================================
print("=" * 60)
print("PART 1: Lambda Functions & Functional Programming")
print("=" * 60)
# Example 1: Basic Lambda
print("\n--- Example 1: Lambda Functions ---")
square = lambda x: x ** 2
add = lambda x, y: x + y
print(f"Square of 5: {square(5)}")
print(f"Add 3 + 4: {add(3, 4)}")
# Example 2: Lambda with sorting
print("\n--- Example 2: Lambda for Sorting ---")
students = [
("Alice", 85),
("Bob", 92),
("Charlie", 78),
("Diana", 95)
]
# Sort by name
sorted_by_name = sorted(students, key=lambda x: x[0])
print(f"Sorted by name: {sorted_by_name}")
# Sort by grade (descending)
sorted_by_grade = sorted(students, key=lambda x: x[1], reverse=True)
print(f"Sorted by grade: {sorted_by_grade}")
# Example 3: Map function
print("\n--- Example 3: Map Function ---")
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(f"Numbers: {numbers}")
print(f"Squared: {squared}")
# Convert strings to uppercase
names = ["alice", "bob", "charlie"]
upper_names = list(map(str.upper, names))
print(f"Names: {names}")
print(f"Uppercase: {upper_names}")
# Example 4: Filter function
print("\n--- Example 4: Filter Function ---")
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
odds = list(filter(lambda x: x % 2 != 0, numbers))
print(f"Numbers: {numbers}")
print(f"Even numbers: {evens}")
print(f"Odd numbers: {odds}")
# Example 5: Reduce function
print("\n--- Example 5: Reduce Function ---")
numbers = [1, 2, 3, 4, 5]
sum_all = reduce(lambda x, y: x + y, numbers)
product = reduce(lambda x, y: x * y, numbers)
print(f"Numbers: {numbers}")
print(f"Sum: {sum_all}")
print(f"Product: {product}")
# Example 6: Combining map, filter, reduce
print("\n--- Example 6: Combining Functional Tools ---")
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Sum of squares of even numbers
result = reduce(
lambda x, y: x + y,
map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers))
)
print(f"Sum of squares of even numbers: {result}")
# ============================================
# PART 2: Closures
# ============================================
print("\n" + "=" * 60)
print("PART 2: Closures")
print("=" * 60)
# Example 1: Basic closure
print("\n--- Example 1: Basic Closure ---")
def make_multiplier(n):
"""Creates a function that multiplies by n"""
def multiplier(x):
return x * n # 'n' is captured from outer scope
return multiplier
times_3 = make_multiplier(3)
times_5 = make_multiplier(5)
print(f"times_3(10) = {times_3(10)}")
print(f"times_5(10) = {times_5(10)}")
# Example 2: Logger with closure
print("\n--- Example 2: Logger Closure ---")
def make_logger(prefix):
"""Creates a logger with a specific prefix"""
def log(message):
timestamp = datetime.now().strftime("%H:%M:%S")
print(f"[{timestamp}] [{prefix}] {message}")
return log
error_log = make_logger("ERROR")
info_log = make_logger("INFO")
debug_log = make_logger("DEBUG")
error_log("File not found")
info_log("Process started")
debug_log("Variable x = 42")
# Example 3: Counter with closure
print("\n--- Example 3: Counter Closure ---")
def make_counter():
"""Creates a counter function"""
count = 0
def counter():
nonlocal count # Modify outer variable
count += 1
return count
return counter
counter1 = make_counter()
counter2 = make_counter()
print(f"Counter1: {counter1()}, {counter1()}, {counter1()}")
print(f"Counter2: {counter2()}, {counter2()}")
# Example 4: HTML tag generator
print("\n--- Example 4: HTML Tag Generator ---")
def make_tag(tag):
"""Creates function to wrap content in HTML tag"""
def wrap_content(content):
return f"<{tag}>{content}</{tag}>"
return wrap_content
h1 = make_tag("h1")
p = make_tag("p")
div = make_tag("div")
print(h1("Welcome to Python"))
print(p("This is a paragraph"))
print(div(p("Nested content")))
# ============================================
# PART 3: Decorators
# ============================================
print("\n" + "=" * 60)
print("PART 3: Decorators")
print("=" * 60)
# Example 1: Basic decorator
print("\n--- Example 1: Basic Decorator ---")
def simple_decorator(func):
@wraps(func)
def wrapper():
print("Before function")
func()
print("After function")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
say_hello()
# Example 2: Decorator with arguments
print("\n--- Example 2: Decorator with Arguments ---")
def repeat(times):
"""Decorator that repeats function execution"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello {name}!")
greet("Alice")
# Example 3: Timer decorator
print("\n--- Example 3: Timer Decorator ---")
def timer(func):
"""Measures function execution time"""
@wraps(func)
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_function():
"""Simulate slow operation"""
time.sleep(0.5)
return "Done"
result = slow_function()
# Example 4: Logging decorator
print("\n--- Example 4: Logging Decorator ---")
def log_calls(func):
"""Logs function calls and returns"""
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_calls
def add(a, b):
return a + b
result = add(5, 3)
# Example 5: Validation decorator
print("\n--- Example 5: Validation Decorator ---")
def validate_positive(func):
"""Ensures all arguments are positive"""
@wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if isinstance(arg, (int, float)) and arg < 0:
raise ValueError("All arguments must be positive")
return func(*args, **kwargs)
return wrapper
@validate_positive
def calculate_area(width, height):
return width * height
try:
print(f"Area: {calculate_area(5, 10)}")
print(f"Area: {calculate_area(-5, 10)}") # Will raise error
except ValueError as e:
print(f"Error: {e}")
# Example 6: Chaining decorators
print("\n--- Example 6: Chaining Decorators ---")
@timer
@log_calls
def multiply(a, b):
time.sleep(0.2) # Simulate work
return a * b
result = multiply(4, 5)
# Example 7: Class decorator
print("\n--- Example 7: Class Decorator ---")
def singleton(cls):
"""Ensures only one instance of class exists"""
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("Creating Database instance")
db1 = Database()
db2 = Database() # Returns same instance
print(f"db1 is db2: {db1 is db2}")
# ============================================
# PART 4: Generators
# ============================================
print("\n" + "=" * 60)
print("PART 4: Generators")
print("=" * 60)
# Example 1: Basic generator
print("\n--- Example 1: Basic Generator ---")
def countdown(n):
"""Generator that counts down from n"""
while n > 0:
yield n
n -= 1
for num in countdown(5):
print(num, end=" ")
print()
# Example 2: Infinite generator
print("\n--- Example 2: Infinite Generator ---")
def infinite_sequence():
"""Generates infinite sequence of numbers"""
num = 0
while True:
yield num
num += 1
# Take first 10 numbers
gen = infinite_sequence()
first_10 = [next(gen) for _ in range(10)]
print(f"First 10 numbers: {first_10}")
# Example 3: Fibonacci generator
print("\n--- Example 3: Fibonacci Generator ---")
def fibonacci(n):
"""Generates first n Fibonacci numbers"""
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
fibs = list(fibonacci(10))
print(f"First 10 Fibonacci numbers: {fibs}")
# Example 4: Generator expression
print("\n--- Example 4: Generator Expression ---")
# List comprehension (creates entire list)
squares_list = [x**2 for x in range(10)]
print(f"List: {squares_list}")
# Generator expression (lazy evaluation)
squares_gen = (x**2 for x in range(10))
print(f"Generator: {squares_gen}")
print(f"Values: {list(squares_gen)}")
# Example 5: File reading generator
print("\n--- Example 5: File Reading Generator ---")
def read_file_lines(filename):
"""Generator to read file line by line"""
try:
with open(filename, 'r') as file:
for line in file:
yield line.strip()
except FileNotFoundError:
print(f"File {filename} not found")
return
# Create sample file
with open('sample.txt', 'w') as f:
f.write("Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n")
print("Reading file with generator:")
for line in read_file_lines('sample.txt'):
print(f" {line}")
# Example 6: Generator pipeline
print("\n--- Example 6: Generator Pipeline ---")
def generate_numbers(n):
"""Generate numbers"""
for i in range(n):
yield i
def square_numbers(numbers):
"""Square each number"""
for num in numbers:
yield num ** 2
def filter_even(numbers):
"""Keep only even numbers"""
for num in numbers:
if num % 2 == 0:
yield num
# Create pipeline
numbers = generate_numbers(10)
squared = square_numbers(numbers)
evens = filter_even(squared)
result = list(evens)
print(f"Pipeline result: {result}")
# ============================================
# PART 5: Iterators
# ============================================
print("\n" + "=" * 60)
print("PART 5: Iterators")
print("=" * 60)
# Example 1: Basic iterator
print("\n--- Example 1: Basic Iterator ---")
class CountDown:
"""Iterator that counts down"""
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
counter = CountDown(5)
for num in counter:
print(num, end=" ")
print()
# Example 2: Range-like iterator
print("\n--- Example 2: Custom Range Iterator ---")
class MyRange:
"""Custom implementation of range()"""
def __init__(self, start, end, step=1):
self.current = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
if (self.step > 0 and self.current >= self.end) or \
(self.step < 0 and self.current <= self.end):
raise StopIteration
value = self.current
self.current += self.step
return value
print("Custom range(0, 10, 2):")
for num in MyRange(0, 10, 2):
print(num, end=" ")
print()
# Example 3: Data batcher iterator
print("\n--- Example 3: Data Batcher ---")
class DataBatcher:
"""Iterator that yields data in batches"""
def __init__(self, data, batch_size):
self.data = data
self.batch_size = batch_size
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
batch = self.data[self.index:self.index + self.batch_size]
self.index += self.batch_size
return batch
data = list(range(10))
batcher = DataBatcher(data, 3)
print("Batches:")
for batch in batcher:
print(f" {batch}")
# Example 4: CSV reader iterator
print("\n--- Example 4: CSV Reader Iterator ---")
class CSVReader:
"""Iterator for reading CSV files"""
def __init__(self, filename):
self.filename = filename
self.file = None
self.reader = None
def __iter__(self):
self.file = open(self.filename, 'r')
self.reader = csv.DictReader(self.file)
return self
def __next__(self):
try:
return next(self.reader)
except StopIteration:
self.file.close()
raise
# Create sample CSV
with open('data.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['name', 'age', 'city'])
writer.writerow(['Alice', '25', 'NYC'])
writer.writerow(['Bob', '30', 'LA'])
writer.writerow(['Charlie', '35', 'Chicago'])
print("CSV data:")
for row in CSVReader('data.csv'):
print(f" {row}")
# ============================================
# PART 6: Context Managers
# ============================================
print("\n" + "=" * 60)
print("PART 6: Context Managers")
print("=" * 60)
# Example 1: Basic context manager
print("\n--- Example 1: File Context Manager ---")
class FileManager:
"""Custom file context manager"""
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
print(f"Opening {self.filename}")
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"Closing {self.filename}")
if self.file:
self.file.close()
return False # Don't suppress exceptions
with FileManager('test.txt', 'w') as f:
f.write("Hello from context manager!")
# Example 2: Timer context manager
print("\n--- Example 2: Timer Context Manager ---")
class Timer:
"""Context manager for timing code blocks"""
def __init__(self, name="Block"):
self.name = name
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.end = time.time()
self.elapsed = self.end - self.start
print(f"{self.name} took {self.elapsed:.4f}s")
return False
with Timer("Loop execution"):
total = sum(range(1000000))
# Example 3: Using @contextmanager
print("\n--- Example 3: @contextmanager Decorator ---")
@contextmanager
def temporary_change(obj, attr, value):
"""Temporarily change an object's attribute"""
original = getattr(obj, attr)
setattr(obj, attr, value)
try:
yield
finally:
setattr(obj, attr, original)
class Config:
debug = False
config = Config()
print(f"Debug before: {config.debug}")
with temporary_change(config, 'debug', True):
print(f"Debug during: {config.debug}")
print(f"Debug after: {config.debug}")
# Example 4: Database connection manager
print("\n--- Example 4: Database Connection Manager ---")
class DatabaseConnection:
"""Simulates database connection context manager"""
def __init__(self, db_name):
self.db_name = db_name
self.connection = None
def __enter__(self):
print(f"Connecting to {self.db_name}...")
self.connection = f"Connection to {self.db_name}"
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"Closing connection to {self.db_name}")
self.connection = None
if exc_type is not None:
print(f"Exception occurred: {exc_val}")
return False
def execute(self, query):
print(f"Executing: {query}")
return f"Result of {query}"
with DatabaseConnection("mydb") as db:
result = db.execute("SELECT * FROM users")
print(result)
# ============================================
# PROJECT 1: Advanced Logging System
# ============================================
print("\n" + "=" * 60)
print("PROJECT 1: Advanced Logging System")
print("=" * 60)
class LogLevel:
DEBUG = 0
INFO = 1
WARNING = 2
ERROR = 3
CRITICAL = 4
def create_logger(name, level=LogLevel.INFO, filename=None):
"""Factory function to create custom logger"""
def log_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Before execution
if level <= LogLevel.INFO:
message = f"[{timestamp}] [INFO] Calling {func.__name__}"
print(message)
if filename:
with open(filename, 'a') as f:
f.write(message + '\n')
try:
result = func(*args, **kwargs)
# After successful execution
if level <= LogLevel.DEBUG:
message = f"[{timestamp}] [DEBUG] {func.__name__} returned {result}"
print(message)
if filename:
with open(filename, 'a') as f:
f.write(message + '\n')
return result
except Exception as e:
# On error
message = f"[{timestamp}] [ERROR] {func.__name__} raised {type(e).__name__}: {e}"
print(message)
if filename:
with open(filename, 'a') as f:
f.write(message + '\n')
raise
return wrapper
return log_decorator
# Using the logger
@create_logger("MyApp", level=LogLevel.DEBUG, filename="app.log")
def process_data(data):
"""Process some data"""
return f"Processed {len(data)} items"
@create_logger("MyApp", level=LogLevel.INFO)
def divide(a, b):
"""Divide two numbers"""
return a / b
result = process_data([1, 2, 3, 4, 5])
result = divide(10, 2)
try:
result = divide(10, 0)
except ZeroDivisionError:
pass
# ============================================
# PROJECT 2: Data Processing Pipeline
# ============================================
print("\n" + "=" * 60)
print("PROJECT 2: Data Processing Pipeline")
print("=" * 60)
def read_csv_generator(filename):
"""Generator to read CSV file"""
with open(filename, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
yield row
def filter_by_value(rows, field, threshold):
"""Generator to filter rows by field value"""
for row in rows:
try:
if float(row[field]) > threshold:
yield row
except (ValueError, KeyError):
continue
def transform_data(rows, transformations):
"""Generator to transform data"""
for row in rows:
new_row = row.copy()
for field, func in transformations.items():
if field in new_row:
new_row[field] = func(new_row[field])
yield new_row
def batch_processor(rows, batch_size):
"""Generator to process data in batches"""
batch = []
for row in rows:
batch.append(row)
if len(batch) >= batch_size:
yield batch
batch = []
if batch: # Yield remaining items
yield batch
# Create sample data
with open('sales_pipeline.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['product', 'price', 'quantity', 'region'])
writer.writerow(['Laptop', '999.99', '5', 'East'])
writer.writerow(['Mouse', '29.99', '50', 'West'])
writer.writerow(['Keyboard', '79.99', '30', 'East'])
writer.writerow(['Monitor', '349.99', '15', 'North'])
writer.writerow(['Desk', '449.99', '8', 'South'])
# Build pipeline
print("\nProcessing pipeline:")
rows = read_csv_generator('sales_pipeline.csv')
filtered = filter_by_value(rows, 'price', 100)
transformed = transform_data(filtered, {
'price': lambda x: f"${float(x):.2f}",
'product': str.upper
})
for row in transformed:
print(f" {row}")
# ============================================
# PROJECT 3: Advanced Context Manager
# ============================================
print("\n" + "=" * 60)
print("PROJECT 3: Advanced Context Manager")
print("=" * 60)
@contextmanager
def managed_resource(resource_name, setup_time=0.1, cleanup_time=0.1):
"""Generic resource manager with setup and cleanup"""
print(f"Setting up {resource_name}...")
time.sleep(setup_time)
resource = {"name": resource_name, "active": True}
try:
yield resource
except Exception as e:
print(f"Error occurred: {e}")
raise
finally:
print(f"Cleaning up {resource_name}...")
time.sleep(cleanup_time)
resource["active"] = False
# Using the resource manager
with managed_resource("Database Connection") as db:
print(f"Using {db['name']}: Active = {db['active']}")
# Simulate work
time.sleep(0.2)
print("\n" + "=" * 60)
print("Advanced Session 2 Completed!")
print("Master these patterns for professional Python code!")
print("=" * 60)