Python Notes Endsem
Python Notes Endsem
Examples:
# Valid identifiers
name = "Python"
_age = 25
student1 = "John"
my_variable = 100
# Invalid identifiers
# 1name = "Error" # Starts with digit
# if = 5 # Keyword used
# my-var = 10 # Hyphen not allowed
Indentation Rules:
Use 4 spaces (PEP 8 standard)
Types of Comments:
1. Single-line: # This is a comment
Examples:
# Single-line comment
def greet(name):
"""
This is a docstring.
Function to greet a person.
"""
if name: # Check if name exists
print(f"Hello, {name}!")
else:
print("Hello, World!")
# Multi-line comment
"""
This program demonstrates
different types of comments
in Python
"""
Important Definitions:
Unicode: Universal character encoding standard
UTF-8: Variable-length encoding for Unicode
Encoding: Converting text to bytes
Decoding: Converting bytes to text
Examples:
# Unicode strings (default in Python 3)
text = "Hello 🐍
Python"
hindi = "नमस्ते"
emoji = " 😊🚀"
print(f"Original: {message}")
# Text type
text = "Hello" # str
# Boolean type
is_valid = True # bool
# Sequence types
my_list = [1, 2, 3] # list
my_tuple = (1, 2, 3) # tuple
my_range = range(5) # range
# Mapping type
my_dict = {"a": 1} # dict
# Set types
my_set = {1, 2, 3} # set
frozen = frozenset([1,2]) # frozenset
Type Hints:
from typing import List, Dict, Optional
Important Definitions:
Object identity: Unique identifier of an object in memory
Object equality: Comparison of object values
id() function: Returns object's identity (memory address)
Examples:
# Identity vs Equality
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(f"id(a): {id(a)}")
print(f"id(b): {id(b)}")
print(f"id(c): {id(c)}")
# With None
value = None
print(f"value is None: {value is None}") # Correct way
print(f"value == None: {value == None}") # Works but not recommended
Examples:
# Precedence examples
result1 = 2 + 3 * 4 # 14 (not 20)
result2 = (2 + 3) * 4 # 20
result3 = 2 ** 3 ** 2 # 512 (right associative)
result4 = (2 ** 3) ** 2 # 64
# Boolean operators
result5 = True or False and False # True (and has higher precedence)
result6 = (True or False) and False # False
print(f"2 + 3 * 4 = {result1}")
print(f"(2 + 3) * 4 = {result2}")
print(f"2 ** 3 ** 2 = {result3}")
print(f"(2 ** 3) ** 2 = {result4}")
F-strings (Recommended):
name = "Alice"
age = 25
score = 95.67
# Basic f-string
print(f"Name: {name}, Age: {age}")
# With expressions
print(f"Next year, {name} will be {age + 1}")
# Formatting numbers
print(f"Score: {score:.2f}%") # 2 decimal places
print(f"Score: {score:.0f}%") # No decimal places
Format Method:
# Using .format()
template = "Name: {}, Age: {}"
print([Link](name, age))
# With indices
template2 = "Name: {0}, Age: {1}, Name again: {0}"
print([Link](name, age))
# With keywords
Old % Formatting:
# % formatting (legacy)
print("Name: %s, Age: %d" % (name, age))
print("Score: %.2f%%" % score) # %% for literal %
Input:
# Getting user input
user_name = input("Enter your name: ")
user_age = int(input("Enter your age: ")) # Convert to int
8. COMMAND-LINE ARGUMENTS
Theory:
Use [Link] to access command-line arguments passed to Python script.
Examples:
import sys
# [Link]
print(f"Script name: {[Link][0]}")
print(f"Number of arguments: {len([Link])}")
print(f"Arguments: {[Link]}")
if len([Link]) > 1:
print(f"First argument: {[Link][1]}")
Practical Example:
import sys
def calculator():
if len([Link]) != 4:
print("Usage: python [Link] <num1> <operator> <num2>")
return
try:
num1 = float([Link][1])
operator = [Link][2]
num2 = float([Link][3])
if __name__ == "__main__":
calculator()
Syntax:
if condition1:
# code block 1
elif condition2:
# code block 2
else:
# code block 3
Examples:
# Simple if-else
age = 18
if age >= 18:
print("You can vote!")
else:
print("You cannot vote yet.")
# Multiple conditions
score = 85
if score >= 90:
grade = 'A'
elif score >= 80:
grade = 'B'
elif score >= 70:
grade = 'C'
elif score >= 60:
grade = 'D'
else:
grade = 'F'
Basic Syntax:
match value:
case pattern1:
# code
case pattern2:
# code
case _: # default case
# code
Examples:
# Basic match
def handle_status(status):
match status:
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
case _:
return "Unknown Status"
While Loops:
# Basic while loop
count = 0
while count < 5:
print(f"Count: {count}")
count += 1
Examples:
# Break example
for i in range(10):
if i == 5:
break
print(i) # Prints 0, 1, 2, 3, 4
# Continue example
for i in range(10):
if i % 2 == 0:
continue
print(i) # Prints 1, 3, 5, 7, 9
Important Definitions:
Iterator: Object that implements iterator protocol
Iterable: Object that can return an iterator
__iter__() : Returns iterator object
__next__() : Returns next item in sequence
Examples:
# Creating custom iterator
class NumberIterator:
def __init__(self, max_num):
self.max_num = max_num
[Link] = 0
def __iter__(self):
return self
def __next__(self):
if [Link] < self.max_num:
[Link] += 1
return [Link]
# Manual iteration
numbers2 = NumberIterator(3)
iterator = iter(numbers2)
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# print(next(iterator)) # StopIteration error
14. COMPREHENSIONS
List Comprehensions:
# Basic list comprehension
squares = [x**2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# With condition
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
# With if-else
numbers = [x if x % 2 == 0 else -x for x in range(10)]
print(numbers) # [0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
Set Comprehensions:
# Set comprehension
unique_squares = {x**2 for x in range(-5, 6)}
print(unique_squares) # {0, 1, 4, 9, 16, 25}
Dictionary Comprehensions:
# Dictionary comprehension
square_dict = {x: x**2 for x in range(5)}
print(square_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# With condition
even_square_dict = {x: x**2 for x in range(10) if x % 2 == 0}
print(even_square_dict) # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
Nested Comprehensions:
# Nested list comprehension
matrix = [[i*j for j in range(3)] for i in range(3)]
print(matrix) # [[0, 0, 0], [0, 1, 2], [0, 2, 4]]
# List unpacking
numbers = [1, 2, 3, 4, 5]
first, *middle, last = numbers
print(f"First: {first}, Middle: {middle}, Last: {last}")
# Dictionary unpacking
def create_profile(name, age, city):
return f"{name}, {age} years old, from {city}"
Zipping:
# Basic zip
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["Mumbai", "Delhi", "Bangalore"]
# Unzip
unzipped_names, unzipped_ages, unzipped_cities = zip(*combined)
print(unzipped_names) # ('Alice', 'Bob', 'Charlie')
Range:
# Basic range
print(list(range(5))) # [0, 1, 2, 3, 4]
print(list(range(2, 8))) # [2, 3, 4, 5, 6, 7]
print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8]
print(list(range(10, 0, -1))) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
🎯 PROGRAMMING QUESTIONS
EASY LEVEL (2-3 marks)
Q1: Write a program to check if a number is even or odd using if-else.
def check_even_odd(num):
"""Check if a number is even or odd."""
if num % 2 == 0:
return f"{num} is even"
else:
return f"{num} is odd"
# Test
number = int(input("Enter a number: "))
result = check_even_odd(number)
print(result)
# Output example:
# Enter a number: 7
# 7 is odd
# Test
number = int(input("Enter number: "))
multiplication_table(number)
# Output example:
# Multiplication table for 5:
# 5 × 1 = 5
# 5 × 2 = 10
# ...
def count_vowels(text):
"""Count number of vowels in given text."""
vowels = "aeiouAEIOU"
count = 0
vowel_list = []
# Test
text = input("Enter a string: ")
count, vowels_found = count_vowels(text)
print(f"Number of vowels: {count}")
print(f"Vowels found: {vowels_found}")
# Output example:
# Enter a string: Hello World
# Number of vowels: 3
# Vowels found: ['e', 'o', 'o']
def is_prime(num):
"""Check if a number is prime."""
if num < 2:
return False
for i in range(2, int(num**0.5) + 1):
if num % i == 0:
return False
return True
def find_primes(n):
# Test
limit = int(input("Enter limit: "))
primes = find_primes(limit)
print(f"Prime numbers up to {limit}: {primes}")
print(f"Total primes: {len(primes)}")
# Output example:
# Enter limit: 20
# Prime numbers up to 20: [2, 3, 5, 7, 11, 13, 17, 19]
# Total primes: 8
def calculator():
"""Simple calculator using match statement (Python 3.10+)."""
print("Simple Calculator")
print("Operations: +, -, *, /, %, **")
try:
num1 = float(input("Enter first number: "))
operator = input("Enter operator: ")
num2 = float(input("Enter second number: "))
match operator:
case '+':
result = num1 + num2
case '-':
result = num1 - num2
case '*':
result = num1 * num2
case '/':
if num2 == 0:
return "Error: Division by zero!"
result = num1 / num2
case '%':
if num2 == 0:
return "Error: Division by zero!"
result = num1 % num2
case '**':
result = num1 ** num2
case _:
return "Error: Invalid operator!"
except ValueError:
return "Error: Invalid input!"
# Test
# Output example:
# Simple Calculator
# Operations: +, -, *, /, %, **
# Enter first number: 10
# Enter operator: *
# Enter second number: 5
# 10.0 * 5.0 = 50.0
class FibonacciIterator:
"""Custom iterator for Fibonacci sequence."""
def __iter__(self):
return self
def __next__(self):
if [Link] < self.max_count:
if [Link] == 0:
[Link] += 1
return [Link]
elif [Link] == 1:
[Link] += 1
return self.next_val
else:
fib_val = [Link] + self.next_val
[Link] = self.next_val
self.next_val = fib_val
[Link] += 1
return fib_val
else:
raise StopIteration
# Test
print("First 10 Fibonacci numbers:")
fib_iter = FibonacciIterator(10)
for num in fib_iter:
print(num, end=" ")
print()
# Manual iteration
print("\\nManual iteration:")
fib_iter2 = FibonacciIterator(5)
iterator = iter(fib_iter2)
for _ in range(5):
print(next(iterator))
# Output:
# First 10 Fibonacci numbers:
# 0 1 1 2 3 5 8 13 21 34
def comprehensive_demo():
"""Demonstrate all types of comprehensions with complex examples."""
# Sample data
students = [
{"name": "Alice", "subjects": {"Math": 85, "Science": 92, "English": 78}},
{"name": "Bob", "subjects": {"Math": 76, "Science": 88, "English": 82}},
{"name": "Charlie", "subjects": {"Math": 94, "Science": 79, "English": 91}},
{"name": "Diana", "subjects": {"Math": 67, "Science": 85, "English": 89}}
]
subject_averages = {
subject: sum(student["subjects"][subject] for student in students) / len(students)
for subject in all_subjects
}
Function Syntax:
def function_name(parameters):
"""Docstring (optional)"""
# Function body
return value # Optional
Examples:
# Simple function
def greet():
"""Function to greet user."""
print("Hello, World!")
# Calling functions
greet() # Hello, World!
2. FUNCTION ARGUMENTS
Theory:
Python supports different types of arguments: positional, keyword, default, and variable-length arguments.
Types of Arguments:
Positional Arguments:
def calculate_area(length, width):
"""Calculate rectangle area."""
return length * width
# Order matters
area = calculate_area(5, 3) # length=5, width=3
print(area) # 15
Keyword Arguments:
def create_profile(name, age, city):
"""Create user profile."""
return f"{name}, {age} years old, from {city}"
Default Arguments:
def greet_user(name, greeting="Hello", punctuation="!"):
"""Greet user with customizable greeting."""
return f"{greeting}, {name}{punctuation}"
# Using defaults
print(greet_user("Alice")) # Hello, Alice!
print(greet_user("Bob", "Hi")) # Hi, Bob!
print(greet_user("Charlie", "Hey", ".")) # Hey, Charlie.
3. RECURSION
Theory:
Recursion is when a function calls itself. Every recursive function needs a base case (stopping condition) and recursive
case.
Important Definitions:
Recursion: Function calling itself
Base case: Condition that stops recursion
Recursive case: Function calling itself with modified parameters
Stack overflow: Error when recursion goes too deep
Examples:
Factorial:
def factorial(n):
"""Calculate factorial using recursion."""
# Base case
if n == 0 or n == 1:
return 1
# Recursive case
else:
return n * factorial(n - 1)
# Test
print(factorial(5)) # 120
print(factorial(0)) # 1
Fibonacci:
def fibonacci(n):
"""Calculate nth Fibonacci number."""
# Base cases
if n <= 0:
return 0
elif n == 1:
return 1
# Recursive case
else:
return fibonacci(n-1) + fibonacci(n-2)
# Test
for i in range(10):
print(f"F({i}) = {fibonacci(i)}")
if n <= 0:
return 0
elif n == 1:
return 1
else:
memo[n] = fibonacci_memo(n-1, memo) + fibonacci_memo(n-2, memo)
return memo[n]
Binary Search:
def binary_search(arr, target, left=0, right=None):
"""Binary search using recursion."""
if right is None:
right = len(arr) - 1
# Test
numbers = [1, 3, 5, 7, 9, 11, 13, 15]
print(binary_search(numbers, 7)) # 3
print(binary_search(numbers, 4)) # -1
4. LAMBDA FUNCTIONS
Theory:
Lambda functions are anonymous functions defined using lambda keyword. They're used for short, simple functions.
Syntax:
lambda arguments: expression
Examples:
Basic Lambda:
# Regular function
def square(x):
return x ** 2
# Lambda equivalent
square_lambda = lambda x: x ** 2
print(square(5)) # 25
print(square_lambda(5)) # 25
# Multiple arguments
add = lambda x, y: x + y
print(add(3, 4)) # 7
# With filter
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # [2, 4, 6, 8, 10]
# With map
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# With sorted
students = [("Alice", 85), ("Bob", 90), ("Charlie", 78)]
MAP Function:
Applies function to every item in iterable.
# Basic map
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
FILTER Function:
Filters items based on condition.
# Basic filter
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # [2, 4, 6, 8, 10]
# Filter strings
words = ["apple", "banana", "cherry", "date"]
long_words = list(filter(lambda word: len(word) > 5, words))
print(long_words) # ['banana', 'cherry']
REDUCE Function:
Applies function cumulatively to items in sequence.
# Find maximum
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum) # 5
# String concatenation
words = ["Hello", " ", "World", "!"]
sentence = reduce(lambda x, y: x + y, words)
print(sentence) # "Hello World!"
print(factorial_reduce(5)) # 120
empty_list = []
print(any(empty_list)) # False
numbers_with_zero = [1, 2, 0, 4]
# Reverse sorting
reverse_sorted = sorted(numbers, reverse=True)
print(reverse_sorted) # [9, 6, 5, 4, 3, 2, 1, 1]
# Sorting tuples
students = [("Alice", 85), ("Bob", 90), ("Charlie", 78), ("Diana", 92)]
by_grade = sorted(students, key=lambda student: student[1])
print(by_grade) # [('Charlie', 78), ('Alice', 85), ('Bob', 90), ('Diana', 92)]
# Sorting dictionaries
people = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 20}
]
by_age = sorted(people, key=lambda person: person["age"])
print(by_age)
# With strings
words = ["python", "java", "c", "javascript"]
shortest = min(words, key=len)
longest = max(words, key=len)
print(f"Shortest: {shortest}") # c
print(f"Longest: {longest}") # javascript
Examples:
Functions as Variables:
def greet(name):
return f"Hello, {name}!"
def farewell(name):
return f"Goodbye, {name}!"
print(operations["add"](5, 3)) # 8
print(operations["multiply"](4, 7)) # 28
Functions as Arguments:
def apply_operation(func, x, y):
"""Apply given function to two numbers."""
return func(x, y)
print(double(5)) # 10
print(triple(4)) # 12
# Create validators
basic_validator = create_validator(6, 12)
strict_validator = create_validator(8, 16)
8. CLOSURES
Theory:
A closure is created when a nested function references variables from its enclosing scope. The nested function "closes
over" these variables.
Important Definitions:
Closure: Nested function that captures variables from enclosing scope
Enclosing scope: The scope of the outer function
Free variables: Variables from enclosing scope used in nested function
Examples:
Basic Closure:
def outer_function(x):
"""Outer function with local variable."""
return inner_function
# Create closure
add_10 = outer_function(10)
print(add_10(5)) # 15
def counter():
nonlocal count # Modify variable in enclosing scope
count += 1
return count
return counter
print(counter1()) # 1
print(counter1()) # 2
print(counter2()) # 101
print(counter1()) # 3
print(counter2()) # 102
def deposit(amount):
nonlocal balance
if amount > 0:
balance += amount
return f"Deposited ₹{amount}. New balance: ₹{balance}"
return "Invalid deposit amount"
def withdraw(amount):
nonlocal balance
if 0 < amount <= balance:
balance -= amount
return f"Withdrew ₹{amount}. New balance: ₹{balance}"
return "Invalid withdrawal amount or insufficient funds"
# Create account
account = create_account(1000)
print(account["balance"]()) # Current balance: ₹1000
print(account["deposit"](500)) # Deposited ₹500. New balance: ₹1500
print(account["withdraw"](200)) # Withdrew ₹200. New balance: ₹1300
9. DECORATORS
Theory:
Decorators are functions that modify or extend the behavior of other functions without changing their code. They use the
@ syntax.
Important Definitions:
Decorator: Function that takes another function and extends its behavior
Wrapper function: Inner function in decorator that wraps the original function
@syntax: Syntactic sugar for applying decorators
Basic Decorator:
def my_decorator(func):
"""Basic decorator that adds functionality."""
def wrapper():
print("Something before the function")
func()
print("Something after the function")
return wrapper
say_hello()
# Output:
# Something before the function
# Hello!
# Something after the function
@timing_decorator
def slow_function():
"""Function that takes some time."""
import time
[Link](1)
return "Done!"
result = slow_function()
print(result)
# Output:
# slow_function took 1.0041 seconds
# Done!
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# Output:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
Practical Decorators:
def login_required(func):
"""Decorator to check if user is logged in."""
def wrapper(*args, **kwargs):
# Simulate checking login status
is_logged_in = True # This would be actual check
if is_logged_in:
return func(*args, **kwargs)
else:
return "Please log in first!"
def cache_result(func):
"""Decorator to cache function results."""
cache = {}
if key in cache:
print(f"Cache hit for {func.__name__}")
return cache[key]
print(f"Computing {func.__name__}")
result = func(*args, **kwargs)
cache[key] = result
return result
return wrapper
@cache_result
def expensive_calculation(n):
"""Simulate expensive calculation."""
import time
[Link](1) # Simulate work
return n * n
# Test caching
print(expensive_calculation(5)) # Computing expensive_calculation
print(expensive_calculation(5)) # Cache hit for expensive_calculation
10. GENERATORS
Theory:
Generators are functions that return an iterator object. They use yield instead of return and can pause and resume
execution.
Important Definitions:
Generator: Function that yields values one at a time
yield: Keyword that pauses function and returns value
Generator object: Iterator returned by generator function
send(): Method to send values to generator
Basic Generator:
def simple_generator():
"""Simple generator that yields three values."""
print("Starting generator")
yield 1
print("Between yields")
yield 2
print("Before last yield")
yield 3
print("Generator finished")
# Convert to list
numbers = list(number_generator(3))
print(numbers) # [0, 1, 2]
Fibonacci Generator:
def fibonacci_generator(limit):
"""Generate Fibonacci numbers up to limit."""
a, b = 0, 1
while a < limit:
yield a
a, b = b, a + b
# Use generator
fib_gen = fibonacci_generator(100)
fib_numbers = list(fib_gen)
print(fib_numbers) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Comparison:
Aspect Iterator Generator
Implementation Class with __iter__ and __next__ Function with yield
Iterator Example:
class CountDown:
"""Iterator class for countdown."""
def __iter__(self):
return self
def __next__(self):
if [Link] <= 0:
raise StopIteration
[Link] -= 1
return [Link] + 1
# Use iterator
countdown = CountDown(5)
for num in countdown:
print(num) # 5, 4, 3, 2, 1
Generator Equivalent:
def countdown_generator(start):
"""Generator function for countdown."""
# Use generator
for num in countdown_generator(5):
print(num) # 5, 4, 3, 2, 1
Types of Imports:
# 1. Import entire module
import math
print([Link]) # 3.141592653589793
print([Link](16)) # 4.0
"""
Custom math module demonstrating module creation.
"""
PI = 3.14159
def factorial(n):
"""Calculate factorial."""
if n <= 1:
return 1
return n * factorial(n - 1)
class Calculator:
"""Simple calculator class."""
Examples:
Simple Class:
class Student:
"""A simple Student class."""
def display_info(self):
"""Method to display student information."""
print(f"Student from {self.school_name}")
# Calling methods
student1.display_info() # Student from ABC University
student2.display_info() # Student from ABC University
def introduce(self):
"""Method to introduce the person."""
return f"Hi, I'm {[Link]} and I'm {[Link]} years old."
def have_birthday(self):
"""Method to increment age."""
[Link] += 1
return f"Happy birthday! {[Link]} is now {[Link]} years old."
Examples:
class BankAccount:
"""Bank account class demonstrating class and instance attributes."""
# Class attributes
bank_name = "Python Bank"
interest_rate = 0.05
total_accounts = 0
def get_account_info(self):
"""Get account information."""
return f"Account {self.account_number}: {self.account_holder}, Balance: ₹{[Link]
e}"
@classmethod
def get_bank_info(cls):
"""Class method to get bank information."""
return f"Bank: {cls.bank_name}, Total Accounts: {cls.total_accounts}"
# Create accounts
account1 = BankAccount("Alice", 1000)
account2 = BankAccount("Bob", 2000)
def __init__(self):
self.instance_var = "Instance Variable"
demo = Demo()
Examples:
class Rectangle:
"""Rectangle class demonstrating methods and self."""
def area(self):
"""Calculate area of rectangle."""
return [Link] * [Link]
def perimeter(self):
"""Calculate perimeter of rectangle."""
return 2 * ([Link] + [Link])
def is_square(self):
"""Check if rectangle is a square."""
def __str__(self):
"""String representation of rectangle."""
return f"Rectangle({[Link]} x {[Link]})"
# Create rectangle
rect = Rectangle(5, 3)
print(rect) # Rectangle(5 x 3)
print(f"Area: {[Link]()}") # Area: 15
print(f"Perimeter: {[Link]()}") # Perimeter: 16
print(f"Is square: {rect.is_square()}") # Is square: False
# Scale rectangle
[Link](2)
print(rect) # Rectangle(10 x 6)
# Create square
square = Rectangle(4, 4)
print(f"Is square: {square.is_square()}") # Is square: True
Method Types:
class MyClass:
"""Demonstrating different types of methods."""
def instance_method(self):
"""Instance method - has access to self."""
return f"Instance method called. Value: {self.instance_variable}"
@classmethod
def class_method(cls):
"""Class method - has access to cls (class itself)."""
return f"Class method called. Class variable: {cls.class_variable}"
@staticmethod
def static_method():
"""Static method - no access to self or cls."""
return "Static method called. No access to instance or class data."
# Usage
obj = MyClass("Hello")
Important Definitions:
Constructor: Special method that initializes new objects
__init__ : Python's constructor method
Initialization: Setting initial values for object attributes
Examples:
Basic Constructor:
class Book:
"""Book class with constructor."""
def mark_as_read(self):
"""Mark book as read."""
self.is_read = True
def __str__(self):
"""String representation."""
status = "Read" if self.is_read else "Unread"
return f"'{[Link]}' by {[Link]} ({[Link]} pages) - {status}"
# Create books
book1 = Book("1984", "George Orwell", 328)
book2 = Book("To Kill a Mockingbird", "Harper Lee", 376)
def _generate_id(self):
"""Private method to generate employee ID."""
def __str__(self):
return f"{[Link]} ({self.employee_id}) - {[Link]}, ₹{[Link]}"
self._celsius = celsius
@property
def celsius(self):
"""Get temperature in Celsius."""
return self._celsius
@[Link]
def celsius(self, value):
"""Set temperature in Celsius with validation."""
if not isinstance(value, (int, float)):
raise TypeError("Temperature must be a number")
@property
def fahrenheit(self):
"""Get temperature in Fahrenheit."""
return (self._celsius * 9/5) + 32
@property
def kelvin(self):
"""Get temperature in Kelvin."""
return self._celsius + 273.15
def __str__(self):
return f"{self._celsius}°C ({[Link]:.1f}°F, {[Link]:.1f}K)"
# Valid temperature
temp1 = Temperature(25)
print(temp1) # 25°C (77.0°F, 298.1K)
# Invalid temperatures
try:
temp2 = Temperature(-300) # Below absolute zero
except ValueError as e:
print(f"Error: {e}")
try:
temp3 = Temperature("hot") # Not a number
except TypeError as e:
print(f"Error: {e}")
Examples:
class BankAccount:
"""Demonstrating attribute access control."""
self._balance -= amount
# Using private method
transaction_id = self.__encrypt_data(f"withdraw_{amount}")
return f"Withdrew ₹{amount}. Transaction: {transaction_id}"
# Create account
account = BankAccount("Alice", 1000)
# Using methods
print([Link](500)) # Deposited ₹500
print(account.get_balance(1234)) # Balance: ₹1500
print([Link](200, 1234)) # Withdrew ₹200. Transaction: encrypted_withdraw_200
print([Link](200, 1111)) # Invalid PIN
@property
def radius(self):
@[Link]
def radius(self, value):
"""Setter for radius with validation."""
if value <= 0:
raise ValueError("Radius must be positive")
self._radius = value
@property
def area(self):
"""Read-only property for area."""
return 3.14159 * self._radius ** 2
@property
def circumference(self):
"""Read-only property for circumference."""
return 2 * 3.14159 * self._radius
# Usage
circle = Circle(5)
print(f"Radius: {[Link]}") # 5
print(f"Area: {[Link]:.2f}") # 78.54
print(f"Circumference: {[Link]:.2f}") # 31.42
# Modify radius
[Link] = 10
print(f"New area: {[Link]:.2f}") # 314.16
Examples:
class Student:
"""Student class demonstrating class variables and methods."""
# Class variables
school_name = "Python University"
def get_average(self):
"""Get student's average grade."""
if [Link]:
return sum([Link]) / len([Link])
return 0
def get_letter_grade(self):
"""Get letter grade based on average."""
avg = self.get_average()
for letter, min_score in Student.grade_scale.items():
if avg >= min_score:
return letter
return "F"
@classmethod
def get_school_info(cls):
"""Class method to get school information."""
return f"School: {cls.school_name}, Total Students: {cls.total_students}"
@classmethod
def change_school_name(cls, new_name):
"""Class method to change school name."""
old_name = cls.school_name
cls.school_name = new_name
return f"School name changed from '{old_name}' to '{new_name}'"
@staticmethod
def is_passing_grade(score):
"""Static method to check if score is passing."""
return score >= 60
@staticmethod
def calculate_gpa(grades):
"""Static method to calculate GPA."""
if not grades:
return 0.0
grade_points = {"A": 4.0, "B": 3.0, "C": 2.0, "D": 1.0, "F": 0.0}
total_points = sum(grade_points.get(grade, 0.0) for grade in grades)
return total_points / len(grades)
def __str__(self):
# Create students
student1 = Student("Alice", "S001")
student2 = Student("Bob", "S002")
student3 = Student("Charlie", "S003")
# Add grades
student1.add_grade(85)
student1.add_grade(92)
student1.add_grade(78)
student2.add_grade(76)
student2.add_grade(82)
student2.add_grade(88)
# Calculate GPA
letter_grades = ["A", "B", "B", "A", "C"]
gpa = Student.calculate_gpa(letter_grades)
print(f"GPA for {letter_grades}: {gpa:.2f}") # GPA for ['A', 'B', 'B', 'A', 'C']: 3.00
7. INHERITANCE
Theory:
Inheritance allows a class to inherit attributes and methods from another class. The inheriting class is called child/derived
class, and the inherited class is called parent/base class.
Important Definitions:
Inheritance: Mechanism to create new class based on existing class
Parent/Base class: Class being inherited from
Child/Derived class: Class that inherits from parent class
IS-A relationship: Relationship expressed by inheritance
Basic Inheritance:
class Animal:
"""Base class for all animals."""
def sleep(self):
return f"{[Link]} is sleeping."
def make_sound(self):
return f"{[Link]} makes a sound."
def __str__(self):
return f"{[Link]} the {[Link]}"
class Dog(Animal):
"""Dog class inheriting from Animal."""
def __str__(self):
return f"{[Link]} the {[Link]} dog"
class Cat(Animal):
"""Cat class inheriting from Animal."""
def __str__(self):
return f"{[Link]} the {[Link]} cat"
# Create objects
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers", "Orange")
# Inherited methods
print([Link]()) # Buddy is eating.
print([Link]()) # Whiskers is sleeping.
# Overridden methods
print(dog.make_sound()) # Buddy barks: Woof! Woof!
# Child-specific methods
print([Link]()) # Buddy fetches the ball.
print([Link]()) # Whiskers climbs the tree.
# Check inheritance
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True
print(isinstance(cat, Dog)) # False
Multi-level Inheritance:
class Vehicle:
"""Base vehicle class."""
def start(self):
return f"{[Link]} {[Link]} {[Link]} is starting."
def stop(self):
return f"{[Link]} {[Link]} {[Link]} has stopped."
class Car(Vehicle):
"""Car class inheriting from Vehicle."""
def honk(self):
return f"{[Link]} {[Link]} honks: Beep! Beep!"
class ElectricCar(Car):
"""Electric car inheriting from Car."""
def charge(self):
self.charge_level = 100
return f"{[Link]} {[Link]} is fully charged."
8. METHOD OVERRIDING
Theory:
Method overriding allows a child class to provide a specific implementation of a method that is already defined in its parent
class.
Examples:
class Shape:
"""Base shape class."""
def area(self):
"""Base implementation - to be overridden."""
return 0
def perimeter(self):
"""Base implementation - to be overridden."""
return 0
def describe(self):
"""Method that uses overridden methods."""
return f"{[Link]}: Area = {[Link]()}, Perimeter = {[Link]()}"
class Rectangle(Shape):
"""Rectangle class with method overriding."""
class Circle(Shape):
"""Circle class with method overriding."""
class Triangle(Shape):
"""Triangle class with method overriding."""
# Create shapes
rectangle = Rectangle(5, 3)
circle = Circle(4)
triangle = Triangle(3, 4, 5)
# Output:
# Rectangle: Area = 15, Perimeter = 16
# Circle: Area = 50.26544, Perimeter = 25.13272
# Triangle: Area = 6.0, Perimeter = 12
def get_details(self):
return f"Employee: {[Link]}, Salary: ₹{[Link]}"
def calculate_bonus(self):
return [Link] * 0.1 # 10% bonus
class Manager(Employee):
"""Manager class extending Employee."""
class Developer(Employee):
"""Developer class extending Employee."""
# Create employees
manager = Manager("Alice", 80000, 5)
developer = Developer("Bob", 70000, ["Python", "JavaScript", "Java"])
print(manager.get_details())
print(f"Manager bonus: ₹{manager.calculate_bonus()}")
print(developer.get_details())
print(f"Developer bonus: ₹{developer.calculate_bonus()}")
# Output:
# Employee: Alice, Salary: ₹80000, Team Size: 5
# Manager bonus: ₹13000
# Employee: Bob, Salary: ₹70000, Languages: Python, JavaScript, Java
# Developer bonus: ₹13000
Examples:
Student Management System:
from collections import defaultdict, Counter
from datetime import datetime
class StudentManagementSystem:
"""Complete student management system using various data structures."""
def __init__(self):
# Dictionary for O(1) student lookup by ID
[Link] = {}
[Link][student_id].append({
'subject': subject,
'grade': grade,
'date': [Link]().strftime("%Y-%m-%d")
})
student = [Link][student_id]
grades = [Link](student_id, [])
if grades:
info += "Grades:\\n"
for grade_record in grades:
info += f" {grade_record['subject']}: {grade_record['grade']}\\n"
# Calculate average
avg = sum(g['grade'] for g in grades) / len(grades)
info += f"Average: {avg:.2f}\\n"
return info
student_ids = [Link][department]
total_students = len(student_ids)
if not all_grades:
return f"Department: {department}\\nStudents: {total_students}\\nNo grades record
return stats
top_students = student_averages[:n]
return result
# Example usage
sms = StudentManagementSystem()
# Add students
print(sms.add_student("CS001", "Alice Johnson", "Computer Science", "alice@[Link]"))
print(sms.add_student("CS002", "Bob Smith", "Computer Science", "bob@[Link]"))
print(sms.add_student("EE001", "Charlie Brown", "Electrical Engineering", "charlie@[Link]
m"))
# Add grades
sms.add_grade("CS001", "Python Programming", 95)
sms.add_grade("CS001", "Data Structures", 88)
sms.add_grade("CS001", "Algorithms", 92)
# Get information
print("\\n" + "="*50)
print(sms.get_student_info("CS001"))
print("="*50)
class InventorySystem:
"""E-commerce inventory management using various data structures."""
def __init__(self):
# Dictionary for product information
[Link] = {}
product = {
'id': product_id,
'name': name,
'category': category,
'price': price,
'stock': stock,
'supplier': supplier,
'min_stock': min_stock,
'added_date': [Link]().strftime("%Y-%m-%d %H:%M:%S")
}
product = [Link][product_id]
old_stock = product['stock']
new_stock = old_stock + quantity_change
if new_stock < 0:
return f"Insufficient stock! Current: {old_stock}, Requested: {abs(quantity_chang
e)}"
product['stock'] = new_stock
# Record transaction
self.recent_transactions.append({
'type': transaction_type,
'product_id': product_id,
'quantity_change': quantity_change,
'old_stock': old_stock,
'new_stock': new_stock,
'timestamp': [Link]().strftime("%Y-%m-%d %H:%M:%S")
})
# Restore heap
for alert in temp_heap:
[Link](self.low_stock_alerts, alert)
if not alerts:
return "No low stock alerts!"
return result
product_ids = [Link][category]
total_products = len(product_ids)
total_value = 0
total_stock = 0
products_info = []
total_stock += stock
total_value += value
products_info.append({
'name': product['name'],
'stock': stock,
'price': price,
'value': value
})
transactions = list(self.recent_transactions)[-limit:]
if 'quantity_change' in transaction:
change = transaction['quantity_change']
result += f" ({'+' if change > 0 else ''}{change} units)"
result += f" [{transaction['old_stock']} → {transaction['new_stock']}]"
result += "\\n"
return result
if not results:
return f"No products found for '{query}'"
return result
# Example usage
inventory = InventorySystem()
# Add products
print(inventory.add_product("LAPTOP001", "Gaming Laptop", "Electronics", 75000, 5, "TechCor
p", 3))
print(inventory.add_product("MOUSE001", "Wireless Mouse", "Electronics", 1500, 25, "TechCor
p", 10))
print(inventory.add_product("BOOK001", "Python Programming", "Books", 800, 50, "BookPublishe
r", 5))
print(inventory.add_product("CHAIR001", "Office Chair", "Furniture", 8000, 2, "FurnitureCor
p", 5))
print("\\n" + "="*60)
print("\\n" + "="*60)
# Get reports
print(inventory.get_low_stock_alerts())
print("="*60)
print(inventory.get_category_report("Electronics"))
print("="*60)
print(inventory.get_recent_transactions(5))
print("="*60)
print(inventory.search_products("laptop"))
Important Definitions:
Stack: Linear data structure with LIFO access
Queue: Linear data structure with FIFO access
Push: Add element to stack
Pop: Remove element from stack
Enqueue: Add element to queue
Dequeue: Remove element from queue
Stack Implementation:
class Stack:
"""Stack implementation using list."""
def __init__(self):
[Link] = []
def pop(self):
"""Remove and return top item."""
if self.is_empty():
raise IndexError("Stack is empty")
return [Link]()
def peek(self):
"""Return top item without removing."""
if self.is_empty():
raise IndexError("Stack is empty")
return [Link][-1]
def size(self):
"""Return number of items in stack."""
return len([Link])
def __str__(self):
return f"Stack({[Link]})"
# Stack applications
def balanced_parentheses(expression):
"""Check if parentheses are balanced using stack."""
stack = Stack()
opening = "({["
closing = ")}]"
pairs = {"(": ")", "{": "}", "[": "]"}
top = [Link]()
if pairs[top] != char:
return False
return stack.is_empty()
def evaluate_postfix(expression):
"""Evaluate postfix expression using stack."""
stack = Stack()
operators = {'+', '-', '*', '/', '//', '%', '**'}
tokens = [Link]()
b = [Link]()
a = [Link]()
if token == '+':
result = a + b
elif token == '-':
result = a - b
elif token == '*':
result = a * b
elif token == '/':
result = a / b
elif token == '//':
result = a // b
elif token == '%':
[Link](result)
else:
try:
number = float(token)
[Link](number)
except ValueError:
raise ValueError(f"Invalid token: {token}")
if [Link]() != 1:
raise ValueError("Invalid postfix expression")
return [Link]()
def infix_to_postfix(expression):
"""Convert infix to postfix using stack."""
stack = Stack()
output = []
# Operator precedence
precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '//': 2, '%': 2, '**': 3}
right_associative = {'**'}
Queue Implementation:
from collections import deque
class Queue:
"""Queue implementation using deque for efficiency."""
def __init__(self):
[Link] = deque()
def dequeue(self):
"""Remove and return front item."""
if self.is_empty():
raise IndexError("Queue is empty")
return [Link]()
def front(self):
"""Return front item without removing."""
if self.is_empty():
raise IndexError("Queue is empty")
return [Link][0]
def rear(self):
"""Return rear item without removing."""
if self.is_empty():
raise IndexError("Queue is empty")
return [Link][-1]
def is_empty(self):
"""Check if queue is empty."""
return len([Link]) == 0
def size(self):
"""Return number of items in queue."""
return len([Link])
def __str__(self):
return f"Queue({list([Link])})"
class PriorityQueue:
"""Priority queue implementation using heap."""
def __init__(self):
[Link] = []
[Link] = 0
def dequeue(self):
"""Remove and return highest priority item."""
if self.is_empty():
raise IndexError("Priority queue is empty")
import heapq
priority, index, item = [Link]([Link])
return item
def is_empty(self):
"""Check if priority queue is empty."""
return len([Link]) == 0
def size(self):
"""Return number of items."""
return len([Link])
# Queue applications
def hot_potato_game(names, num):
"""Hot potato game using queue."""
queue = Queue()
return [Link]()
[Link](start)
[Link](start)
return result
print("\\nBFS Traversal:")
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
bfs_result = breadth_first_search(graph, 'A')
print(f"BFS from A: {bfs_result}")
print("\\nTask Scheduling:")
task_scheduler()
Important Definitions:
Tree: Hierarchical data structure with nodes and edges
Root: Top node of the tree
Leaf: Node with no children
Parent: Node with children
Child: Node connected to parent
Subtree: Tree formed by a node and its descendants
Height: Maximum depth of the tree
Depth: Distance from root to a node
def __str__(self):
return str([Link])
class BinaryTree:
"""Binary tree implementation using nodes."""
if node is None:
return -1
if node is None:
return 0
if node is None:
return 0
if node is None:
return None
if [Link] == data:
return node
def level_order_traversal(self):
"""Level order traversal using queue."""
if [Link] is None:
return []
result = []
queue = Queue()
[Link]([Link])
if [Link]:
[Link]([Link])
if [Link]:
[Link]([Link])
if [Link]:
self.print_tree([Link], level + 1, "R--- ")
else:
print(" " * ((level + 1) * 4) + "R--- None")
# Left subtree: (3 + 4)
plus_node = tree.insert_left([Link], '+')
tree.insert_left(plus_node, 3)
tree.insert_right(plus_node, 4)
# Right subtree: 2
tree.insert_right([Link], 2)
return tree
def evaluate_expression_tree(node):
"""Evaluate expression tree."""
if node is None:
return 0
# Leaf node