0% found this document useful (0 votes)
24 views22 pages

Here

Uploaded by

Klaus Mikleson
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views22 pages

Here

Uploaded by

Klaus Mikleson
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

Here’s a well-structured version of your content:

Comprehensive Python Interview Preparation


Notes
Chapter 1: Python Fundamentals

1.1 Introduction to Python


What is Python?
Python is a high-level, interpreted, and general-purpose programming language known
for its simple syntax and readability. It supports multiple programming paradigms,
including procedural, object-oriented, and functional programming.
Key Features:
1. Simple and Easy to Learn: Python’s syntax is straightforward and resembles
natural language.
2. Interpreted Language: Code is executed line-by-line, making debugging easier.
3. Cross-Platform Compatibility: Runs on multiple platforms, including Windows,
Linux, and macOS.
4. Extensive Standard Library: Provides built-in modules for a wide range of tasks.
5. Dynamic Typing: No need to declare variable types explicitly; the interpreter
assigns types dynamically.
6. Community Support: A large and active community supports Python, providing
a vast number of libraries and frameworks.
Python 2 vs Python 3:
Python 2 is no longer supported, while Python 3 is the standard for current and future
development.
Key DiIerences:
• Print statement is changed to a function: print("Hello, World!")
• Integer division behavior: 5 / 2 results in 2.5 (floating-point division).
• String handling is more consistent with Unicode support.

1.2 Basic Syntax and Structure


Identifiers and Keywords:
• Identifiers: Names for variables, functions, classes, etc., that must begin with a
letter or an underscore and can contain alphanumeric characters and
underscores. They cannot be keywords like if, else, while, etc.
• Keywords: Reserved words that have special meaning in Python
(e.g., True, False, None).
Indentation:
Python uses indentation (spaces or tabs) to define blocks of code. Consistency is
crucial because inconsistent indentation will cause errors.
Comments:
• Single-line comment: #
• Multi-line comment: ''' comment ''' or """ comment """
Input and Output Functions:
• input(): Takes user input as a string.
• print(): Outputs data to the console.
Example:
python
Copy code
name = input("Enter your name: ") # Takes user input
print(f"Hello, {name}!") # Outputs a greeting message

1.3 Data Types and Variables


Data Types:
• Primitive Data Types: int, float, str, bool, NoneType
• Composite Data Types: list, tuple, set, dict
Type Conversion:
• Implicit Conversion: Automatically converts one data type to another.
• Explicit Conversion: Uses functions like int(), float(), str(), list().
Example:
python
Copy code
# Implicit Type Conversion
x = 10
y = 2.5
z = x + y # z will be of float type (12.5)

# Explicit Type Conversion


a = int(5.6) # a will be 5
b = str(10) # b will be "10"

1.4 Operators in Python


Operator Types:
• Arithmetic Operators: +, -, *, /, %, ** (Exponentiation), // (Floor Division)
• Comparison Operators: ==, !=, >, <, >=, <=
• Logical Operators: and, or, not
• Bitwise Operators: &, |, ^, ~, <<, >>
• Assignment Operators: =, +=, -=, *=, /=, %=, **=, //=
• Identity Operators: is, is not
• Membership Operators: in, not in
Operator Precedence:
Determines the order in which operators are evaluated in expressions.
Precedence Order:
1. () : Parentheses
2. ** : Exponentiation
3. ~ + - : Unary operators
4. * / % // : Multiplication, Division, Modulus, Floor division
5. + - : Addition, Subtraction
6. >> << : Right and Left bitwise shift
7. & : Bitwise AND
8. ^ | : Bitwise XOR, Bitwise OR
9. <= < > >= : Comparison operators
10. == != : Equality operators
11. = %= /= //= -= += *= **= : Assignment operators
12. is, is not : Identity operators
13. in, not in : Membership operators
14. not, and, or : Logical operators
Example:
python
Copy code
result = 10 + 2 * 3 # 10 + (2*3) = 16
print(result) # Output: 16

1.5 Control Flow Statements


Conditional Statements:
if, elif, else are used to control the flow of the program based on conditions.
Example:
python
Copy code
age = 18
if age >= 18:
print("You are eligible to vote.")
else:
print("You are not eligible to vote.")
Loops:
• for loop: Iterates over a sequence (like a list, tuple, or string).
• while loop: Repeats as long as a condition is true.
Example:
python
Copy code
for i in range(1, 6):
print(i)

count = 1
while count <= 5:
print(count)
count += 1
Loop Control Statements:
• break: Exits the loop immediately.
• continue: Skips the remaining code inside the loop for the current iteration.
• pass: Placeholder that does nothing and is used syntactically.
Example:
python
Copy code
for letter in "Python":
if letter == "h":
break # Loop will terminate when letter is 'h'
print(letter)
1.6 Functions
Defining a Function:
A function is defined using the def keyword, followed by the function name and
parentheses.
Example:
python
Copy code
def greet(name):
print(f"Hello, {name}!")

greet("Alice") # Output: Hello, Alice!


Types of Function Arguments:
1. Positional Arguments: Passed in the order defined in the function.
2. Keyword Arguments: Passed with the argument name and value.
3. Default Arguments: Have default values if not provided.
4. Variable-length Arguments: *args for tuple of arguments and **kwargs for
dictionary of keyword arguments.
Example:
python
Copy code
def add_numbers(*args):
return sum(args)

print(add_numbers(1, 2, 3)) # Output: 6


Lambda Functions:
Anonymous functions created using the lambda keyword. Typically used for short,
simple operations.
Example:
python
Copy code
square = lambda x: x * x
print(square(5)) # Output: 25
Recursion:
A function that calls itself to solve smaller subproblems of the original problem.
Example:
python
Copy code
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

print(factorial(5)) # Output: 120


Chapter 2: Object-Oriented Programming and
Advanced Python
This chapter covers the key concepts of Object-Oriented Programming (OOP) in Python,
along with advanced Python topics like error handling, file operations, and modules.
2.1 Object-Oriented Programming Concepts

What is Object-Oriented Programming (OOP)?


OOP is a programming paradigm based on the concept of "objects" that contain both
data (attributes) and functions (methods) to operate on the data. It allows developers to
create classes, reuse code through inheritance, and structure programs using objects
that represent real-world entities.
Key OOP Concepts:
1. Class: A blueprint for creating objects. It defines a set of attributes and methods
that the objects created from the class will have.
2. Object: An instance of a class. It has its own data and can use the methods
defined in the class.
3. Encapsulation: Restricting access to certain components of an object to
prevent unintended interference and misuse.
4. Inheritance: Allows a class to inherit attributes and methods from another
class, promoting code reusability.
5. Polymorphism: Allows methods to have the same name but behave diverently
based on the object calling them.
6. Abstraction: Hides internal details and exposes only essential functionalities.

2.1.1 Class and Object


A class is created using the class keyword, and an object is instantiated using the class
name.
Example:
python
Copy code
class Person:
def __init__(self, name, age): # Constructor to initialize attributes
self.name = name
self.age = age

def greet(self): # Method to display a greeting


print(f"Hello, my name is {self.name} and I am {self.age} years old.")

# Create an object of the Person class


person1 = Person("Alice", 30)
person1.greet() # Output: Hello, my name is Alice and I am 30 years old.

2.1.2 Encapsulation
Encapsulation hides the internal state of an object and restricts access to it using
methods (getters and setters). Private attributes are prefixed with double underscores
(__).
Example:
python
Copy code
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute

def get_balance(self): # Getter method


return self.__balance

def set_balance(self, amount): # Setter method


if amount >= 0:
self.__balance = amount

account = BankAccount(1000)
print(account.get_balance()) # Output: 1000

2.1.3 Inheritance
Inheritance allows a new class (child class) to inherit attributes and methods from an
existing class (parent class).
Types of Inheritance:
1. Single Inheritance
2. Multiple Inheritance
3. Multilevel Inheritance
4. Hierarchical Inheritance
5. Hybrid Inheritance
Example:
python
Copy code
# Parent class
class Animal:
def speak(self):
print("Animal sound")

# Child class inheriting from Animal


class Dog(Animal):
def bark(self):
print("Dog barks")

dog = Dog()
dog.speak() # Output: Animal sound
dog.bark() # Output: Dog barks

2.1.4 Polymorphism
Polymorphism allows methods to have the same name but behave diverently
depending on the object calling them.
Example:
python
Copy code
class Animal:
def sound(self):
pass

class Dog(Animal):
def sound(self):
print("Bark")

class Cat(Animal):
def sound(self):
print("Meow")

# Function that uses polymorphism


def animal_sound(animal):
animal.sound()

dog = Dog()
cat = Cat()
animal_sound(dog) # Output: Bark
animal_sound(cat) # Output: Meow

2.2 Error Handling and Exceptions


What is Exception Handling?
Exception handling manages runtime errors and ensures that the program continues to
run smoothly without crashing unexpectedly.
Key Concepts:
• try block: Contains code that may raise an exception.
• except block: Handles the exception if it is raised in the try block.
• else block: Executes if no exceptions are raised.
• finally block: Always executes, regardless of whether an exception is raised.
Syntax:
python
Copy code
try:
result = 10 / 0
except ZeroDivisionError:
print("You cannot divide by zero!")
else:
print("No exceptions occurred!")
finally:
print("This block always executes.")
Output:
scss
Copy code
You cannot divide by zero!
This block always executes.

Example:
python
Copy code
try:
x = int(input("Enter a number: "))
y = 10 / x
except ValueError:
print("Invalid input. Please enter a number.")
except ZeroDivisionError:
print("Division by zero is not allowed.")
else:
print(f"Result: {y}")
finally:
print("Execution completed.")

2.2.1 Custom Exceptions


You can define your own custom exceptions using class definitions.
Example:
python
Copy code
class NegativeValueError(Exception):
"""Custom exception for negative values"""
pass

def check_positive(number):
if number < 0:
raise NegativeValueError("Negative value not allowed!")

try:
check_positive(-5)
except NegativeValueError as e:
print(e) # Output: Negative value not allowed!

2.3 Modules and Packages


Modules: A module is a file containing Python definitions and statements that can be
included in another Python program using the import statement.
Example:
python
Copy code
import math
print(math.sqrt(16)) # Output: 4.0
Creating a Custom Module:
Save Python code in a .py file and import it using import module_name.
Example:
python
Copy code
# File: mymodule.py
def greet(name):
print(f"Hello, {name}!")

# File: main.py
import mymodule
mymodule.greet("Alice") # Output: Hello, Alice!

2.4 File Handling


Python provides built-in functions like open(), read(), write(), and close() for file
operations. It supports diverent file modes: r (read), w (write), a (append), rb (read
binary), wb (write binary).
Example:
python
Copy code
# Writing to a file
with open("sample.txt", "w") as file:
file.write("Hello, World!")

# Reading from a file


with open("sample.txt", "r") as file:
content = file.read()
print(content) # Output: Hello, World!
Exception Handling in File Operations:
python
Copy code
try:
with open("non_existent_file.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("File not found.")

Chapter 2 Program Example: Combining OOP, Exception Handling, and File


Handling
python
Copy code
class Student:
def __init__(self, name, age, grades):
self.name = name
self.age = age
self.grades = grades
def get_average(self):
try:
average = sum(self.grades) / len(self.grades)
return average
except ZeroDivisionError:
return "No grades available."
except Exception as e:
return str(e)

Chapter 3: Data Structures, Algorithms, and


Libraries
This chapter introduces Python’s key data structures, sorting and searching algorithms,
and important libraries for data handling, which are essential for solving algorithmic
problems during technical interviews.

3.1 Python Data Structures


Overview of Data Structures:
• Eviciently organize and store data, facilitating quick access and modification.
Common Python Data Structures:
1. List (Ordered, mutable, supports duplicates):
o Operations: indexing, slicing, appending, etc.
o Example:
python
Copy code
my_list = [1, 2, 3]
my_list.append(4) # Adds 4 to the end
print(my_list) # Output: [1, 2, 3, 4]
2. Tuple (Ordered, immutable, supports duplicates):
o Example:
python
Copy code
my_tuple = (10, 20, 30)
print(my_tuple[1]) # Output: 20
3. Set (Unordered, no duplicates):
o Example:
python
Copy code
my_set = {1, 2, 3, 3}
print(my_set) # Output: {1, 2, 3}
4. Dictionary (Unordered, key-value pairs, keys must be unique):
o Example:
python
Copy code
my_dict = {'name': 'Alice', 'age': 25}
print(my_dict['name']) # Output: Alice
3.2 Sorting Algorithms
1. Bubble Sort: Repeatedly swaps adjacent elements if they are in the wrong order.
o Time Complexity: O(n²)
o Example:
python
Copy code
def bubble_sort(arr):
for i in range(len(arr)):
for j in range(0, len(arr) - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
2. Selection Sort: Selects the minimum element and swaps it with the first
unsorted element.
o Time Complexity: O(n²)
o Example:
python
Copy code
def selection_sort(arr):
for i in range(len(arr)):
min_idx = i
for j in range(i + 1, len(arr)):
if arr[j] < arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr
3. Insertion Sort: Inserts each element into its correct position in a sorted array.
o Time Complexity: O(n²)
o Example:
python
Copy code
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j=i-1
while j >= 0 and key < arr[j]:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
return arr
4. Merge Sort: Recursively splits the array, sorts, and merges them.
o Time Complexity: O(n log n)
o Example:
python
Copy code
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]
merge_sort(left_half)
merge_sort(right_half)
i=j=k=0
while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1
while i < len(left_half):
arr[k] = left_half[i]
i += 1
k += 1
while j < len(right_half):
arr[k] = right_half[j]
j += 1
k += 1
return arr
5. Quick Sort: Selects a pivot element and partitions the array around the pivot.
o Time Complexity: O(n log n) on average
o Example:
python
Copy code
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
3.3 Searching Algorithms
1. Linear Search: Sequentially checks each element.
o Time Complexity: O(n)
o Example:
python
Copy code
def linear_search(arr, target):
for i, value in enumerate(arr):
if value == target:
return i
return -1
2. Binary Search: Eviciently searches in a sorted array by dividing the array in half.
o Time Complexity: O(log n)
o Example:
python
Copy code
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
3.4 Important Python Libraries for Data Handling
1. numpy: Used for numerical and scientific computing.
o Example:
python
Copy code
import numpy as np
array = np.array([1, 2, 3])
print(np.mean(array))
2. pandas: Provides DataFrames for data manipulation.
o Example:
python
Copy code
import pandas as pd
data = {'Name': ['Alice', 'Bob'], 'Age': [25, 30]}
df = pd.DataFrame(data)
print(df)
3. matplotlib: For data visualizations.
o Example:
python
Copy code
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [2, 4, 6])
plt.show()
4. scipy: Builds on numpy for scientific computations.
o Example:
python
Copy code
from scipy import stats
data = [1, 2, 2, 3]
mode = stats.mode(data)
print(mode)
5. collections: Specialized containers like Counter.
o Example:
python
Copy code
from collections import Counter
count = Counter([1, 2, 2, 3])
print(count)
6. itertools: For creating complex iterables.
o Example:
python
Copy code
from itertools import permutations
print(list(permutations([1, 2, 3])))
3.5 Complexity Analysis
1. Time Complexity: Measures time taken as input size grows.
o Common complexities: O(1), O(n), O(n²), O(log n)
o Example:
python
Copy code
def constant_time(arr):
return arr[0] # O(1)
2. Space Complexity: Measures memory usage.
o Common complexities: O(1), O(n)
o Example:
python
Copy code
def linear_space(arr):
output = [] # O(n)
for element in arr:
output.append(element * 2)
return output
Chapter 4: Advanced Concepts and Practical
Applications
This chapter covers advanced Python concepts such as decorators, generators, and
concurrency, along with practical applications and error handling scenarios that are
frequently discussed in interviews.

4.1 Advanced Functions

4.1.1 Decorators
What are Decorators?
A decorator is a higher-order function that takes another function as an argument and
extends or modifies its behavior without changing its actual code.
Decorators are commonly used for logging, access control, measuring execution time,
and other cross-cutting concerns.
Syntax and Usage:
Decorators are defined using the @decorator_name syntax above the function
definition.
Example:
python
Copy code
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print(f"Wrapper executed before {original_function.__name__}")
return original_function(*args, **kwargs)
return wrapper_function

@decorator_function
def display():
print("Display function executed")

display()
Output:
css
Copy code
Wrapper executed before display
Display function executed
4.1.2 Generators
What are Generators?
Generators are a special type of iterator in Python that allow you to iterate over a
sequence of values without creating a large in-memory list. They are defined using a
function with the yield keyword instead of return.
Advantages:
• Memory-evicient as they generate values on the fly without storing them all at
once.
• Suitable for working with large datasets or infinite sequences.
Example:
python
Copy code
def countdown(num):
while num > 0:
yield num
num -= 1

for number in countdown(5):


print(number)
Output:
Copy code
5
4
3
2
1
4.1.3 staticmethod and classmethod
Static Methods:
• Defined using the @staticmethod decorator.
• Do not require access to class or instance variables and are used for utility
functions related to the class.
Example:
python
Copy code
class MathOperations:
@staticmethod
def add(a, b):
return a + b

print(MathOperations.add(5, 10)) # Output: 15


Class Methods:
• Defined using the @classmethod decorator.
• Take cls as the first parameter and can access or modify class-level attributes.
Example:
python
Copy code
class Employee:
num_of_employees = 0

def __init__(self, name):


self.name = name
Employee.num_of_employees += 1

@classmethod
def get_num_of_employees(cls):
return cls.num_of_employees
emp1 = Employee("Alice")
emp2 = Employee("Bob")
print(Employee.get_num_of_employees()) # Output: 2

4.2 Concurrency and Parallelism


Python provides multiple ways to handle concurrency and parallelism, such as
threading, multiprocessing, and asynchronous programming. Understanding these
concepts is crucial for building evicient and responsive applications.
4.2.1 Threading
What is Threading?
Threading allows a program to run multiple operations concurrently within a single
process. The threading module in Python provides tools for creating and managing
threads.
Usage:
Suitable for I/O-bound operations like file I/O or network requests.
Example:
python
Copy code
import threading

def print_numbers():
for i in range(1, 6):
print(f"Number: {i}")

def print_letters():
for letter in "ABCDE":
print(f"Letter: {letter}")

# Creating threads
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)

# Starting threads
thread1.start()
thread2.start()

# Wait for threads to complete


thread1.join()
thread2.join()

print("Threads completed.")
Output:
makefile
Copy code
Number: 1
Letter: A
Number: 2
Letter: B
Number: 3
Letter: C
Number: 4
Letter: D
Number: 5
Letter: E
Threads completed.
4.2.2 Multiprocessing
What is Multiprocessing?
Multiprocessing creates separate processes, each with its own memory space, allowing
true parallel execution. The multiprocessing module enables parallel processing for
CPU-bound tasks.
Usage:
Suitable for CPU-bound operations like mathematical calculations or data processing.
Example:
python
Copy code
import multiprocessing

def square_number(number):
return number * number

if __name__ == '__main__':
numbers = [1, 2, 3, 4, 5]
with multiprocessing.Pool() as pool:
results = pool.map(square_number, numbers)
print(results) # Output: [1, 4, 9, 16, 25]
4.2.3 Asynchronous Programming
What is Asynchronous Programming?
Asynchronous programming allows a program to perform non-blocking operations,
making it suitable for I/O-bound operations like web scraping, file I/O, and network
requests. The asyncio module enables asynchronous programming using
the async and await keywords.
Usage:
Used for building responsive applications that handle multiple I/O-bound tasks
simultaneously.
Example:
python
Copy code
import asyncio

async def fetch_data():


print("Fetching data...")
await asyncio.sleep(2) # Simulates a network request delay
print("Data fetched!")
async def main():
await asyncio.gather(fetch_data(), fetch_data()) # Run multiple fetch operations
concurrently

asyncio.run(main())
Output:
kotlin
Copy code
Fetching data...
Fetching data...
Data fetched!
Data fetched!

4.3 Exception Handling in Depth


Exception handling is crucial in Python to prevent a program from crashing due to
unexpected errors. It allows developers to gracefully handle errors and continue
program execution.
Syntax and Structure:
• try: Contains the code that might raise an exception.
• except: Handles the exception.
• else: Runs if no exception is raised.
• finally: Runs whether or not an exception was raised, often used for cleanup
tasks.
Example:
python
Copy code
try:
x = int(input("Enter a number: "))
y = 10 / x
except ValueError:
print("Invalid input. Please enter a number.")
except ZeroDivisionError:
print("Division by zero is not allowed.")
else:
print(f"Result: {y}")
finally:
print("Execution completed.")
Output:
csharp
Copy code
Enter a number: 0
Division by zero is not allowed.
Execution completed.

4.4 Practical Use Cases for Advanced Concepts


Using Decorators for Logging
Decorators are often used to add logging functionality to existing functions.
Example:
python
Copy code
def logger(func):
def wrapper(*args, **kwargs):
print(f"Executing {func.__name__} with arguments {args} and {kwargs}")
result = func(*args, **kwargs)
print(f"Executed {func.__name__} with result {result}")
return result
return wrapper

@logger
def add(a, b):
return a + b

print(add(5, 10)) # Output includes log statements


Using Generators to Handle Large Data Sets
Generators are useful when working with large datasets that cannot fit in memory.
Example:
python
Copy code
def read_large_file(file_path):
with open(file_path, "r") as file:
for line in file:
yield line # Yield one line at a time to save memory

for line in read_large_file("large_file.txt"):


print(line)
Implementing Concurrency with asyncio
Building a web scraper that performs multiple HTTP requests concurrently
using asyncio and the aiohttp library.
Example:
python
Copy code
import asyncio
import aiohttp

async def fetch(url):


async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()

async def main():


urls = ["https://example.com", "https://httpbin.org",
"https://jsonplaceholder.typicode.com/posts"]
tasks = [fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)

asyncio.run(main())

4.5 Important Concepts for Technical Interviews


DiIerences Between @staticmethod and @classmethod
• @staticmethod: Does not access or modify the class or instance state. Used for
utility functions that do not need class-specific data.
• @classmethod: Takes cls as its first argument and can modify class-level
attributes.
Common Use Cases for Generators
• Handling large files or datasets that cannot fit into memory.
• Implementing pipelines where each step yields a result to the next step.
• Infinite sequences, such as generating Fibonacci numbers.
Common Use Cases for Decorators
• Logging, input validation, access control, caching/memoization, timing, and
performance measurement.

Chapter 4 Program Example: Combining Advanced Concepts


python
Copy code
import time
import threading

# 1. Decorator for measuring execution time


def time_it(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Executed {func.__name__} in {end_time - start_time:.4f} seconds")
return result
return wrapper

@time_it
def compute_square(numbers):
return [x * x for x in numbers]

# 2. Generator for large data processing


def large_data_generator(n):
for i in range(n):
yield i * i

# 3. Threading example for concurrency


def print_squares(numbers):
for num in numbers:
print(f"Square: {num * num}")

numbers = [1, 2, 3, 4, 5]

# Create a thread for printing squares


square_thread = threading.Thread(target=print_squares, args=(numbers,))
square_thread.start()

# Measure execution time of a function


squares = compute_square(numbers)

# Use generator to process large data


for square in large_data_generator(5):
print(f"Generated Square: {square}")

square_thread.join()

This completes Chapter 4 with detailed notes on advanced concepts such as


decorators, generators, concurrency, exception handling, and practical use cases.

For Dear Little Wolf

You might also like