Complete Python Programming Course
Complete Python Programming Course
Programming Course
From Basic to Industrial-Level Advanced
Level 5: Enterprise
Table of Contents
1. Introduction to Python
5. Operators
12. Tuples
13. Dictionaries
14. Sets
16. Comprehensions
27. Iterators
28. Generators
33. Decorators
35. Metaclasses
37. Threading
38. Multiprocessing
LEVEL 1: FOUNDATIONAL
PYTHON (BASIC)
1. Introduction to Python
What is Python?
4. **High Demand**: One of the most sought-after skills in the job market
Python Applications
Installing Python
Windows
1. Visit [[Link]]([Link]
macOS
# Using Homebrew (recommended)
brew install python3
Linux (Ubuntu/Debian)
Verifying Installation
python --version
# or
python3 --version
print("Hello, World!")
Run it:
python [Link]
Indentation
Python uses indentation to define code blocks. This is **mandatory**, not just for
readability.
# Correct
if True:
print("This is indented")
print("This is also indented")
Comments
# This is a single-line comment
"""
This is a multi-line comment
or a docstring when used to document functions/classes
"""
'''
You can also use single quotes
for multi-line comments
'''
Line Continuation
# Using backslash
total = 1 + 2 + 3 + \
4 + 5 + 6
# Empty class
class MyClass:
pass
Variables
Variables are containers for storing data values. Python has no command for
declaring a variable; it's created when you assign a value.
# Variable assignment
x = 5
name = "Alice"
is_active = True
# Multiple assignment
a, b, c = 1, 2, 3
# Invalid names
# 2var = 1 # starts with number
# my-var = 2 # contains hyphen
# my var = 3 # contains space
# class = 4 # Python keyword
# Constants: UPPER_CASE
MAX_SIZE = 100
PI = 3.14159
# Classes: PascalCase
class UserProfile:
pass
1. Numeric Types
# Integer
age = 25
population = 7_800_000_000 # Underscores for readability (Python 3.6+
# Float
price = 19.99
temperature = -5.5
scientific = 3.14e2 # 314.0
# Complex
z = 3 + 4j
2. String
# String concatenation
full_name = "John" + " " + "Doe"
# String repetition
laugh = "Ha" * 3 # "HaHaHa"
3. Boolean
is_student = True
is_employed = False
4. NoneType
# Type conversion
x = 5
y = float(x) # 5.0
z = str(x) # "5"
# String to number
s = "123"
num = int(s) # 123
Dynamic Typing
5. Operators
Arithmetic Operators
a = 10
b = 3
# Addition
result = a + b # 13
# Subtraction
result = a - b # 7
# Multiplication
result = a * b # 30
# Modulus (remainder)
result = a % b # 1
# Exponentiation
result = a ** b # 1000 (10^3)
Assignment Operators
x = 5
# Compound assignment
x += 3 # x = x + 3 (now 8)
x -= 2 # x = x - 2 (now 6)
x *= 4 # x = x * 4 (now 24)
x /= 3 # x = x / 3 (now 8.0)
x //= 2 # x = x // 2 (now 4.0)
x %= 3 # x = x % 3 (now 1.0)
x **= 2 # x = x ** 2 (now 1.0)
Comparison Operators
a = 5
b = 3
# Equal to
result = (a == b) # False
# Not equal to
result = (a != b) # True
# Greater than
result = (a > b) # True
# Less than
result = (a < b) # False
# Combining operators
x = 10
result = (x > 5 and x < 15) # True (equivalent to 5 < x < 15)
Identity Operators
a = [1, 2, 3]
b = [1, 2, 3]
c = a
# is not
print(a is not b) # True
# not in
print("orange" not in fruits) # True
Bitwise Operators
a = 10 # 1010 in binary
b = 4 # 0100 in binary
# AND
result = a & b # 0 (0000)
# OR
result = a | b # 14 (1110)
# XOR
result = a ^ b # 14 (1110)
# NOT
result = ~a # -11
# Left shift
result = a << 2 # 40 (101000)
# Right shift
result = a >> 2 # 2 (0010)
1. `**` (Exponentiation)
4. `+`, `-`
5. `<<`, `>>`
6. `&`
7. `^`
8. `|`
9. `==`, `!=`, `>`, `<`, `>=`, `<=`, `is`, `is not`, `in`, `not in`
10. `not`
11. `and`
12. `or`
# Multiple arguments
print("Hello", "World") # Hello World (space added automatically)
# Custom separator
print("Hello", "World", sep=", ") # Hello, World
# No newline at end
print("Loading", end="...")
# Printing variables
name = "Alice"
age = 25
print("Name:", name, "Age:", age)
String Formatting
name = "Alice"
age = 25
print("My name is %s and I'm %d years old" % (name, age))
Method 2: [Link]()
name = "Alice"
age = 25
print("My name is {} and I'm {} years old".format(name, age))
# Formatting numbers
pi = 3.14159
print("Pi is approximately {:.2f}".format(pi)) # Pi is approximately
name = "Alice"
age = 25
print(f"My name is {name} and I'm {age} years old")
# Formatting
pi = 3.14159
print(f"Pi is approximately {pi:.2f}") # Pi is approximately 3.14
# Or more concisely
a, b, c = map(int, input("Enter three numbers: ").split())
while True:
try:
age = int(input("Enter your age: "))
if age < 0:
print("Age cannot be negative. Try again.")
continue
break
except ValueError:
print("Invalid input. Please enter a number.")
age = 18
age = 16
age = 25
has_license = True
age = 20
status = "Adult" if age >= 18 else "Minor"
print(status) # Adult
# Or in returns
def get_status(age):
return "Adult" if age >= 18 else "Minor"
print(http_status(200)) # OK
# Empty string
if "":
print("This won't print")
# Non-empty string
if "hello":
print("This will print")
# Empty list
my_list = []
if not my_list:
print("List is empty")
# While-else
count = 0
while count < 5:
print(count)
count += 1
else:
print("Loop completed normally") # Executes if loop wasn't broken
# Using range()
for i in range(5): # 0, 1, 2, 3, 4
print(i)
# Reverse iteration
for i in range(10, 0, -1): # 10, 9, 8, ..., 1
print(i)
# For-else
for i in range(5):
print(i)
else:
print("Loop completed") # Executes if loop wasn't broken
`break` Statement
# Exit loop prematurely
for i in range(10):
if i == 5:
break # Exit loop when i is 5
print(i)
# Output: 0, 1, 2, 3, 4
`continue` Statement
Nested Loops
# Multiplication table
for i in range(1, 6):
for j in range(1, 6):
print(f"{i} x {j} = {i*j}")
print() # Blank line after each row
# Pattern printing
for i in range(1, 6):
for j in range(i):
print("*", end="")
print()
# Output:
# *
# **
# ***
# ****
# *****
`zip()` Function
PEP 8 is Python's style guide. Following it makes your code more readable and
maintainable.
Indentation
# Hanging indent
result = some_function(
argument1, argument2,
argument3, argument4
)
Line Length
# Maximum line length: 79 characters (or 99 for less strict)
# Break long lines using implied continuation
# Good
long_string = (
"This is a very long string that "
"spans multiple lines for readability"
)
# Function calls
result = some_function(
parameter1="value1",
parameter2="value2",
parameter3="value3"
)
Blank Lines
# Two blank lines before top-level functions and classes
def function1():
pass
def function2():
pass
class MyClass:
pass
class AnotherClass:
pass
def method2(self):
pass
Imports
# Imports at the top of file
# Standard library first, then third-party, then local
import os
import sys
import numpy as np
import pandas as pd
Whitespace
# Avoid extraneous whitespace
# Good
spam(ham[1], {eggs: 2})
foo = (0,)
x = y
# Bad
spam( ham[ 1 ], { eggs: 2 } )
foo = (0, )
x=y
# Around operators
# Good
i = i + 1
x = x * 2 - 1
c = (a + b) * (a - b)
# In keyword arguments
def function(arg=5):
pass
function(arg=10)
Naming Conventions
# Variables and functions: lowercase with underscores
user_name = "Alice"
def calculate_total():
pass
# Classes: PascalCase
class UserProfile:
pass
class HTTPResponse:
pass
# Block comments
# This is a block comment explaining
# a complex piece of logic that follows
# in the code below
def function(param):
"""
One-line docstring for simple functions.
"""
pass
Args:
arg1 (int): Description of arg1
arg2 (str): Description of arg2
Returns:
bool: Description of return value
Raises:
ValueError: When arg1 is negative
"""
pass
String Quotes
# Use double quotes for strings
message = "Hello, World!"
**Difficulty**: Easy
**Example**:
Input: a = 5, b = 10
Output: a = 10, b = 5
Question 2: Temperature Converter
Create a program that converts temperature from Celsius to Fahrenheit and vice
versa.
**Formula**: F = (C × 9/5) + 32
**Difficulty**: Easy
Write a program that takes a number as input and determines whether it's even or
odd.
**Difficulty**: Easy
Create a calculator that can perform basic operations (+, -, *, /) based on user input.
**Difficulty**: Easy
**Rules**:
- Divisible by 4
Create a program that takes a score (0-100) and outputs the corresponding grade:
- 90-100: A
- 80-89: B
- 70-79: C
- 60-69: D
- Below 60: F
**Difficulty**: Easy
Write a program that prints the multiplication table for a given number (1-10).
**Difficulty**: Easy
**Difficulty**: Medium
Question 9: Factorial Calculator
**Difficulty**: Medium
*
**
***
****
*****
**Difficulty**: Easy
a, b = b, a
**Explanation**:
- Python's tuple unpacking (method 1) is the most elegant and Pythonic way
- The arithmetic method works but can cause overflow with large numbers
def fahrenheit_to_celsius(fahrenheit):
"""Convert Fahrenheit to Celsius."""
return (fahrenheit - 32) * 5/9
# Main program
print("Temperature Converter")
print("1. Celsius to Fahrenheit")
print("2. Fahrenheit to Celsius")
if choice == '1':
celsius = float(input("Enter temperature in Celsius: "))
fahrenheit = celsius_to_fahrenheit(celsius)
print(f"{celsius}°C = {fahrenheit:.2f}°F")
elif choice == '2':
fahrenheit = float(input("Enter temperature in Fahrenheit: "))
celsius = fahrenheit_to_celsius(fahrenheit)
print(f"{fahrenheit}°F = {celsius:.2f}°C")
else:
print("Invalid choice!")
**Key Concepts**:
- Input validation
if number % 2 == 0:
print(f"{number} is even")
else:
print(f"{number} is odd")
**Explanation**:
# Main program
print("Simple Calculator")
print("Operations: +, -, *, /")
if operator == '+':
result = add(num1, num2)
elif operator == '-':
result = subtract(num1, num2)
elif operator == '*':
result = multiply(num1, num2)
elif operator == '/':
result = divide(num1, num2)
else:
result = "Invalid operator!"
print(f"Result: {result}")
**Best Practices**:
def is_leap_year(year):
"""
Check if a year is a leap year.
Rules:
- Divisible by 4: leap year
- Unless divisible by 100: not a leap year
- Unless divisible by 400: leap year
"""
if year % 400 == 0:
return True
if year % 100 == 0:
return False
if year % 4 == 0:
return True
return False
# Alternative one-liner
def is_leap_year_oneliner(year):
return year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)
# Main program
year = int(input("Enter a year: "))
if is_leap_year(year):
print(f"{year} is a leap year")
else:
print(f"{year} is not a leap year")
# Test cases
test_years = [2000, 1900, 2004, 2023, 2024]
for y in test_years:
status = "is" if is_leap_year(y) else "is not"
print(f"{y} {status} a leap year")
**Logic Explanation**:
- Years divisible by 100 (but not 400) are not leap years (e.g., 1900)
# Main program
try:
score = float(input("Enter your score (0-100): "))
grade = calculate_grade(score)
# Additional feedback
if grade in ['A', 'B']:
print("Excellent work!")
elif grade == 'C':
print("Good job!")
elif grade == 'D':
print("You passed, but consider studying more.")
else:
print("You need to improve. Don't give up!")
except ValueError:
print("Please enter a valid number")
**Features**:
- Input validation
- Error handling with try-except
# Main program
try:
number = int(input("Enter a number: "))
print_multiplication_table(number)
except ValueError:
print("Please enter a valid integer")
**Formatting Tips**:
if n == 2:
return True
if n % 2 == 0:
return False
return True
def is_prime_optimized(n):
"""Optimized prime checker using sqrt."""
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
# Main program
try:
number = int(input("Enter a number: "))
if is_prime(number):
print(f"{number} is a prime number")
else:
print(f"{number} is not a prime number")
except ValueError:
print("Please enter a valid integer")
**Optimization**:
- Only check up to √n (any factor larger than √n must have a corresponding factor
smaller than √n)
result = 1
for i in range(2, n + 1):
result *= i
return result
def factorial_recursive(n):
"""Calculate factorial using recursion."""
if n < 0:
return "Factorial not defined for negative numbers"
if n == 0 or n == 1:
return 1
return n * factorial_recursive(n - 1)
# Main program
try:
number = int(input("Enter a number: "))
if number < 0:
print("Factorial is not defined for negative numbers")
else:
# Using iterative method
result_iter = factorial_iterative(number)
print(f"{number}! = {result_iter}")
except ValueError:
print("Please enter a valid integer")
except RecursionError:
print("Number too large for recursive calculation")
**Concepts**:
def print_pattern_numbered(n):
"""Numbered pattern."""
for i in range(1, n + 1):
for j in range(1, i + 1):
print(j, end=" ")
print()
def print_pattern_pyramid(n):
"""Pyramid pattern."""
for i in range(1, n + 1):
# Print spaces
print(' ' * (n - i), end="")
# Print stars
print('*' * (2 * i - 1))
def print_pattern_diamond(n):
"""Diamond pattern."""
# Upper half
for i in range(1, n + 1):
print(' ' * (n - i) + '*' * (2 * i - 1))
# Lower half
for i in range(n - 1, 0, -1):
print(' ' * (n - i) + '*' * (2 * i - 1))
# Main program
try:
n = int(input("Enter number of rows: "))
print("\n3. Pyramid")
print_pattern_pyramid(n)
print("\n4. Diamond")
print_pattern_diamond(n)
except ValueError:
print("Please enter a valid integer")
**Pattern Variations**:
Lists are ordered, mutable collections that can hold items of different types.
# Creating lists
empty_list = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, [1, 2, 3]]
Accessing Elements
# Indexing (zero-based)
first = fruits[0] # "apple"
second = fruits[1] # "banana"
last = fruits[-1] # "elderberry"
second_last = fruits[-2] # "date"
# Slicing [start:end:step]
print(fruits[1:3]) # ["banana", "cherry"]
print(fruits[:3]) # ["apple", "banana", "cherry"]
print(fruits[2:]) # ["cherry", "date", "elderberry"]
print(fruits[::2]) # ["apple", "cherry", "elderberry"]
print(fruits[::-1]) # Reverse list
# Nested lists
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix[0][1]) # 2 (first row, second column)
Modifying Lists
numbers = [1, 2, 3, 4, 5]
# Changing elements
numbers[0] = 10 # [10, 2, 3, 4, 5]
numbers[-1] = 50 # [10, 2, 3, 4, 50]
# Changing slices
numbers[1:3] = [20, 30] # [10, 20, 30, 4, 50]
# Deleting elements
del numbers[0] # [20, 30, 4, 50]
del numbers[1:3] # [20, 50]
List Methods
fruits = ["apple", "banana"]
# Adding elements
[Link]("cherry") # Add to end
[Link](1, "avocado") # Insert at index
[Link](["date", "fig"]) # Add multiple items
# Removing elements
[Link]("banana") # Remove first occurrence
popped = [Link]() # Remove and return last item
popped = [Link](0) # Remove and return item at index
[Link]() # Remove all items
# Searching
fruits = ["apple", "banana", "cherry", "banana"]
index = [Link]("banana") # 1 (first occurrence)
count = [Link]("banana") # 2
# Checking membership
if "apple" in fruits:
print("Found apple!")
# Sorting
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
[Link]() # Sort in place (ascending)
[Link](reverse=True) # Sort descending
sorted_nums = sorted(numbers) # Return new sorted list (original un
# Reversing
[Link]() # Reverse in place
reversed_nums = numbers[::-1] # Return new reversed list
# Copying
original = [1, 2, 3]
shallow_copy = [Link]()
another_copy = original[:]
import copy
deep_copy = [Link](original)
List Operations
# Concatenation
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2 # [1, 2, 3, 4, 5, 6]
# Repetition
repeated = [1, 2] * 3 # [1, 2, 1, 2, 1, 2]
# Length
length = len(combined) # 6
# Transform strings
words = ["hello", "world"]
upper_words = [[Link]() for word in words]
Common Patterns
numbers = list(range(10))
for chunk in chunks(numbers, 3):
print(chunk) # [0, 1, 2], [3, 4, 5], [6, 7, 8], [9]
12. Tuples
# Creating tuples
empty_tuple = ()
single_item = (1,) # Note the comma!
numbers = (1, 2, 3, 4, 5)
mixed = (1, "hello", 3.14, True)
# Indexing
x = point[0] # 10
y = point[1] # 20
z = point[-1] # 30
# Slicing
print(point[1:]) # (20, 30)
# Unpacking
x, y, z = point
print(f"x={x}, y={y}, z={z}")
Tuple Methods
numbers = (1, 2, 3, 2, 4, 2, 5)
# Length
length = len(numbers) # 7
# Membership
if 3 in numbers:
print("Found 3!")
Tuple Operations
# Concatenation
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined = tuple1 + tuple2 # (1, 2, 3, 4, 5, 6)
# Repetition
repeated = (1, 2) * 3 # (1, 2, 1, 2, 1, 2)
Named Tuples
# Create instances
p1 = Point(10, 20, 30)
p2 = Point(x=5, y=15, z=25)
# Still immutable
# p1.x = 15 # AttributeError
# Convert to dict
point_dict = p1._asdict()
print(point_dict) # {'x': 10, 'y': 20, 'z': 30}
13. Dictionaries
# Creating dictionaries
empty_dict = {}
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
Modifying Dictionaries
person = {"name": "Alice", "age": 25}
# Adding/updating entries
person["city"] = "New York" # Add new key
person["age"] = 26 # Update existing key
# Remove entries
removed_value = [Link]("salary") # Remove and return value
[Link]() # Remove and return last item (Python 3.7+)
del person["city"] # Remove specific key
[Link]() # Remove all items
Dictionary Methods
Iterating Over Dictionaries
Dictionary Comprehensions
# Create dict from list
numbers = [1, 2, 3, 4, 5]
squares = {x: x**2 for x in numbers}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
Nested Dictionaries
# Dictionary of dictionaries
users = {
"alice": {"age": 25, "city": "New York"},
"bob": {"age": 30, "city": "London"},
"charlie": {"age": 35, "city": "Tokyo"}
}
# With lists
groups = defaultdict(list)
groups["fruits"].append("apple")
groups["vegetables"].append("carrot")
info = defaultdict(default_value)
print(info["missing_key"]) # "Unknown"
# Count elements
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counter = Counter(words)
print(counter) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
# Counter operations
c1 = Counter(["a", "b", "c", "a"])
c2 = Counter(["a", "b", "b", "d"])
# Addition
print(c1 + c2) # Counter({'a': 3, 'b': 3, 'c': 1, 'd': 1})
# Subtraction
print(c1 - c2) # Counter({'a': 1, 'c': 1})
14. Sets
Sets are unordered collections of **unique** elements. They're mutable and don't
allow duplicates.
# Creating sets
empty_set = set() # Note: {} creates an empty dict!
numbers = {1, 2, 3, 4, 5}
mixed = {1, "hello", 3.14, True}
# From string
char_set = set("hello") # {'h', 'e', 'l', 'o'}
Set Operations
# Adding elements
fruits = {"apple", "banana"}
[Link]("cherry")
[Link](["date", "elderberry"]) # Add multiple
# Removing elements
[Link]("banana") # Raises KeyError if not found
[Link]("banana") # No error if not found
popped = [Link]() # Remove and return random element
[Link]() # Remove all elements
# Subset
small_set = {1, 2}
is_subset = small_set <= a
is_subset = small_set.issubset(a) # True
# Superset
is_superset = a >= small_set
is_superset = [Link](small_set) # True
Set Comprehensions
# Create set of squares
squares = {x**2 for x in range(10)}
# Can't modify
# [Link](6) # AttributeError
String Basics
# Creating strings
single = 'Hello'
double = "World"
triple = """Multiple
lines"""
# Indexing
first = text[0] # 'P'
last = text[-1] # 'g'
# Slicing
print(text[0:6]) # 'Python'
print(text[7:]) # 'Programming'
print(text[:6]) # 'Python'
print(text[::2]) # 'Pto rgamn' (every 2nd char)
print(text[::-1]) # 'gnimmargorP nohtyP' (reverse)
# Case conversion
print([Link]()) # 'HELLO WORLD'
print([Link]()) # 'hello world'
print([Link]()) # 'Hello world'
print([Link]()) # 'Hello World'
print([Link]()) # 'hELLO wORLD'
# Checking case
print("HELLO".isupper()) # True
print("hello".islower()) # True
print("Hello World".istitle()) # True
# Finding substrings
index = [Link]("Python") # 0 (first occurrence)
index = [Link]("Python", 10) # 19 (search from index 10)
index = [Link]("Java") # -1 (not found)
# Count occurrences
count = [Link]("Python") # 2
# Checking start/end
print([Link]("Python")) # True
print([Link]("ful.")) # True
String Methods - Modification
# Trimming whitespace
print([Link]()) # "Hello, World!"
print([Link]()) # "Hello, World! "
print([Link]()) # " Hello, World!"
# Replacing
new_text = [Link]("World", "Python") #" Hello, Python! "
new_text = [Link]("l", "L", 2) # Replace first 2 occurrences
# Splitting
words = "apple,banana,cherry".split(",") # ['apple', 'banana', 'cherr
lines = "Line1\nLine2\nLine3".splitlines()
# Joining
words = ["Python", "is", "awesome"]
sentence = " ".join(words) # "Python is awesome"
csv = ",".join(words) # "Python,is,awesome"
String Formatting
# Old style (%)
name = "Alice"
age = 25
print("Name: %s, Age: %d" % (name, age))
# [Link]()
print("Name: {}, Age: {}".format(name, age))
print("Name: {n}, Age: {a}".format(n=name, a=age))
print("Name: {0}, Age: {1}".format(name, age))
# Alignment
print(f"{'left':<10}|") # 'left |'
print(f"{'right':>10}|") # ' right|'
print(f"{'center':^10}|") # ' center |'
# Number formatting
num = 1234567.89
print(f"{num:,.2f}") # '1,234,567.89'
print(f"{num:e}") # '1.234568e+06'
print(f"{42:05d}") # '00042'
String Validation
# Checking string type
print("123".isdigit()) # True
print("abc123".isalnum()) # True (alphanumeric)
print("abc".isalpha()) # True (alphabetic)
print(" ".isspace()) # True (whitespace)
print("Hello World".isascii()) # True (Python 3.7+)
# Identifier check
print("variable_name".isidentifier()) # True
print("2variable".isidentifier()) # False
String Encoding/Decoding
# Encode to bytes
text = "Hello, 世界"
encoded = [Link]("utf-8")
print(encoded) # b'Hello, \xe4\xb8\x96\xe7\x95\x8c'
# Find pattern
match = [Link](r"\$\d+\.\d+", text)
if match:
print([Link]()) # $19.99
# Replace pattern
new_text = [Link](r"\d+", "XX", text) # "The price is $[Link]"
# Split by pattern
parts = [Link](r"\s+", "Hello World") # ['Hello', 'World']
16. Comprehensions
List Comprehensions
# Basic syntax: [expression for item in iterable]
# Traditional way
squares = []
for x in range(10):
[Link](x**2)
# With condition
evens = [x for x in range(20) if x % 2 == 0]
# With if-else
parity = ["even" if x % 2 == 0 else "odd" for x in range(10)]
# Nested loops
pairs = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
# String operations
words = ["hello", "world", "python"]
upper = [[Link]() for word in words]
Dictionary Comprehensions
# Basic syntax: {key_expr: value_expr for item in iterable}
# Square numbers
squares = {x: x**2 for x in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Conditional value
status = {x: "even" if x % 2 == 0 else "odd" for x in range(5)}
Set Comprehensions
# Basic syntax: {expression for item in iterable}
Generator Expressions
# Using generator
for square in squares_gen:
print(square)
if square > 100:
break
# 2D matrix
matrix = [[j for j in range(5)] for i in range(3)]
# [[0, 1, 2, 3, 4],
# [0, 1, 2, 3, 4],
# [0, 1, 2, 3, 4]]
# Transpose matrix
original = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in original] for i in range(len(original
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Real-World Examples
# Parse CSV data
csv_data = "name,age,city\nAlice,25,NY\nBob,30,LA"
lines = csv_data.split("\n")
headers = lines[0].split(",")
rows = [dict(zip(headers, [Link](","))) for line in lines[1:]]
# [{'name': 'Alice', 'age': '25', 'city': 'NY'},
# {'name': 'Bob', 'age': '30', 'city': 'LA'}]
# File processing
# Read and filter lines from file
# filtered = [[Link]() for line in open('[Link]') if not [Link]
# Word frequency
text = "the quick brown fox jumps over the lazy dog the quick brown fo
words = [Link]()
word_count = {word: [Link](word) for word in set(words)}
# Cartesian product
colors = ["red", "green", "blue"]
sizes = ["S", "M", "L"]
products = [f"{color}-{size}" for color in colors for size in sizes]
# ['red-S', 'red-M', 'red-L', 'green-S', ...]
*[Due to length constraints, I'll continue with the remaining sections in the next part.
The course will include all 5 levels with complete examples, explanations, and
workshop questions.]*
LEVEL 3: OBJECT-ORIENTED &
ADVANCED PYTHON
What is OOP?
Object-Oriented Programming is a programming paradigm that organizes code
around "objects" - data structures that contain both data (attributes) and code
(methods).
Encapsulation:
Bundling data and methods together
Abstraction:
Hiding complex implementation details
Inheritance:
Creating new classes from existing ones
Polymorphism:
Using a single interface for different types
22. Classes and Objects
Creating a Class
class Dog:
"""A simple Dog class."""
def bark(self):
"""Make the dog bark."""
return f"{[Link]} says Woof!"
def get_info(self):
"""Return dog information."""
return f"{[Link]} is {[Link]} years old"
class Dog:
# Class variable (shared by all instances)
species = "Canis familiaris"
total_dogs = 0
@classmethod
def get_total_dogs(cls):
"""Class method to access class variable."""
return cls.total_dogs
@staticmethod
def is_adult(age):
"""Static method - doesn't access instance or class."""
return age >= 2
# Usage
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 1)
Single Inheritance
Example: Inheritance
class Animal:
"""Base class."""
def speak(self):
pass # To be overridden
class Dog(Animal):
"""Derived class."""
def speak(self):
return f"{[Link]} says Woof!"
class Cat(Animal):
"""Another derived class."""
def speak(self):
return f"{[Link]} says Meow!"
# Polymorphism in action
animals = [Dog("Buddy"), Cat("Whiskers"), Dog("Max")]
Using super()
Example: super() Function
class Vehicle:
def __init__(self, brand, model):
[Link] = brand
[Link] = model
def info(self):
return f"{[Link]} {[Link]}"
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model) # Call parent __init__
[Link] = doors
def info(self):
return f"{super().info()} with {[Link]} doors"
24. Magic Methods (Dunder Methods)
class Book:
def __init__(self, title, author, pages):
[Link] = title
[Link] = author
[Link] = pages
def __str__(self):
"""String representation for users."""
return f"{[Link]} by {[Link]}"
def __repr__(self):
"""String representation for developers."""
return f"Book('{[Link]}', '{[Link]}', {[Link]
def __len__(self):
"""Return length (pages)."""
return [Link]
25. Generators
# Usage
squares_list = get_squares_list(5)
print(list(squares_list)) # [0, 1, 4, 9, 16]
squares_gen = get_squares_generator(5)
for square in squares_gen:
print(square) # Prints one at a time
# Generator expression
squares_gen_expr = (x ** 2 for x in range(5))
print(list(squares_gen_expr)) # [0, 1, 4, 9, 16]
Infinite sequences
Pipeline processing
Try-Except Blocks
# Basic try-except
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
# Try-except-else-finally
try:
file = open("[Link]", "r")
data = [Link]()
except FileNotFoundError:
print("File not found!")
else:
print("File read successfully!")
print(data)
finally:
print("Execution completed.")
# [Link]() if file exists
Raising Exceptions
Example: Raising Exceptions
try:
result = divide(10, 0)
except ValueError as e:
print(f"Error: {e}")
# Custom exceptions
class InsufficientFundsError(Exception):
"""Custom exception for bank account."""
pass
class BankAccount:
def __init__(self, balance):
[Link] = balance
account = BankAccount(100)
try:
[Link](150)
except InsufficientFundsError as e:
print(e) # Insufficient funds. Balance: 100
27. Level 3 Workshop Questions
Medium
Requirements:
Library class: add books, borrow books, return books, list available books
Solution:
class Book:
def __init__(self, title, author, isbn):
[Link] = title
[Link] = author
[Link] = isbn
self.is_available = True
def __str__(self):
status = "Available" if self.is_available else "Borrowed"
return f"{[Link]} by {[Link]} ({status})"
class Library:
def __init__(self, name):
[Link] = name
[Link] = []
def list_available_books(self):
available = [book for book in [Link] if book.is_availa
if available:
print(f"\nAvailable books in {[Link]}:")
for book in available:
print(f" - {book}")
else:
print("No books available")
# Usage
library = Library("City Library")
library.add_book(Book("1984", "George Orwell", "001"))
library.add_book(Book("To Kill a Mockingbird", "Harper Lee", "002"
library.list_available_books()
library.borrow_book("001")
library.list_available_books()
library.return_book("001")
Hard
Solution:
class BankAccount:
"""Base class for bank accounts."""
def get_balance(self):
return [Link]
def get_statement(self):
print(f"\nAccount Statement for {self.holder_name}")
print(f"Account Number: {self.account_number}")
print(f"Current Balance: ${[Link]}")
print("\nTransactions:")
for transaction in [Link]:
print(f" - {transaction}")
class SavingsAccount(BankAccount):
"""Savings account with interest."""
def add_interest(self):
interest = [Link] * self.interest_rate
[Link] += interest
[Link](f"Interest added: ${interest:.2f}
return interest
class CheckingAccount(BankAccount):
"""Checking account with overdraft protection."""
# Usage
savings = SavingsAccount("SAV001", "Alice", 1000)
checking = CheckingAccount("CHK001", "Bob", 500, 200)
[Link](500)
savings.add_interest()
savings.get_statement()
2. Explore Specializations:
The best way to improve is to code every day. Start small projects,
experiment with new concepts, and don't be afraid to make
mistakes - they're the best teachers!
Happy Coding! 🐍✨
Topics Covered
SOLID Principles
Performance Optimization
Memory Management
Warning:
Topics Covered
Career Tip:
Congratulations! 🎉
Remember:
Programming is a practical skill. The more you code, the better you
become. Keep building, keep learning, and never stop exploring!
Happy Coding! 🐍
Tuples are a preferred choice over lists in scenarios where data immutability is crucial. Since tuples are immutable, they provide data safety by preventing accidental modification after creation. This makes them suitable for fixed collections of items, such as configuration constants or returning multiple values from functions. Tuples can also be used as dictionary keys due to their immutability, which lists cannot do. Additionally, tuples have a slight performance advantage over lists in terms of processing speed .
The key differences between iterative and recursive approaches in implementing factorial calculations involve their process and efficiency. The iterative approach uses a loop to accumulate the product of numbers up to 'n'. It is generally more memory efficient and straightforward as it avoids the overhead of multiple function calls. The recursive approach defines the problem in terms of itself, calling the function repeatedly until the base case is reached, which can be more intuitive but uses a stack for each call, potentially leading to stack overflow for large values. Edge case handling ensures both approaches return 1 for 0! as a base condition .
Python's exception handling using try-except blocks enhances program robustness by providing mechanisms to catch and gracefully handle runtime errors, preventing program crashes. This approach allows developers to specify error-specific actions through multiple except blocks, thereby enabling precise control and management of different error types. The try-except-else-finally structure further improves robustness by allowing for executing code irrespective of exceptions (finally) and only when no exceptions occur (else), ensuring resource deallocation, and cleanup tasks are performed reliably .
Class and instance variables affect the design of a Python class by determining the scope and storage of data across instances. Class variables are shared among all instances, useful for data that remains constant across instances or for keeping shared state, such as tracking the total count of instances. Instance variables are unique to each instance, holding data specific to that particular instance. Designing with class variables requires considering concurrency and data consistency across instances, while instance variables provide flexibility and encapsulation of data for individual objects .
List operations such as slicing and list comprehension support data transformation and analysis by allowing targeted extraction and transformation of list contents. Slicing enables specific sections of a list to be extracted using start, stop, and step parameters, facilitating manipulation and subset creation. List comprehensions offer a concise method to generate new lists by applying expressions and conditions to each element, supporting transformations such as filtering, mapping, and aggregating data. These operations simplify working with iterable data for analysis tasks and streamline data processing workflows .
The super() function in Python's object-oriented programming is used to call methods from a parent or superclass. It allows a derived class to access and invoke methods that are overridden in its base class, facilitating code reusability and extending functionalities. super() is useful for cooperative multiple inheritance scenarios, where it ensures that all necessary initializations are called properly. By avoiding hard-coded references to parent classes, super() enhances maintainability and facilitates changes in the class hierarchy .
List comprehension in Python provides a more concise and readable way to create lists compared to traditional looping. It allows the generation of lists using a single line of code, potentially reducing verbosity. In terms of memory efficiency, list comprehensions are generally similar to loops because they create the entire list in memory. However, using generator expressions, which are similar to list comprehensions but with round brackets, offers memory efficiency by generating items one at a time instead of storing them all at once .
Common patterns in Python for flattening nested data structures involve using nested loops within list comprehensions or employing recursive functions. One approach is using a comprehension like [item for sublist in nested for item in sublist], effectively flattening a 2D list by iterating over sublists and then over each item within them. Alternatively, recursion can be used for structures with arbitrary nesting, applying a recursive function to traverse and append elements to a flat list. These methods enhance data structure manipulation by transforming nested lists into single-level lists .
Decorators in Python offer the benefit of enhancing or modifying the behavior of functions or methods without permanently altering their actual code. They are useful for cross-cutting concerns such as logging, monitoring, authentication, and access control. Decorators can also be used for memoization to cache results of expensive function calls or add functionality like validation or input transformation. They promote clean and modular code by separating repetitive logic from core business logic, supporting the DRY (Don't Repeat Yourself) principle .
Generators offer several advantages over regular functions due to their ability to yield items one at a time, which is memory efficient. They are particularly beneficial when working with large datasets or streams of data that don't fit entirely into memory, as they allow processing of data in chunks or sequences on-the-fly. Additionally, generators simplify code readability and management for pipeline processing and infinite sequences, enhancing performance by not storing all values simultaneously .