0% found this document useful (0 votes)
62 views46 pages

Key Python Concepts Explained

The document outlines key Python concepts, including differences between Python 2 and 3, decorators, lists vs. tuples, lambda functions, generators, memory management, and the use of modules and packages. It also explains the purpose of the 'with' statement, the differences between dictionaries and lists, string reversal methods, and the distinction between 'is' and '==' operators. Additionally, it covers shallow vs. deep copying and methods to remove duplicates from a list.

Uploaded by

GOWTHAM P
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)
62 views46 pages

Key Python Concepts Explained

The document outlines key Python concepts, including differences between Python 2 and 3, decorators, lists vs. tuples, lambda functions, generators, memory management, and the use of modules and packages. It also explains the purpose of the 'with' statement, the differences between dictionaries and lists, string reversal methods, and the distinction between 'is' and '==' operators. Additionally, it covers shallow vs. deep copying and methods to remove duplicates from a list.

Uploaded by

GOWTHAM P
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

a

u ry
r.s
de
co
Basic Python Concepts:
[Link] 2 and Python 3
:@

Python2 and Python3 are twomajorversionsofPython,butPython2isno longer


maintained. Some key differences include:
am

Feature Python 2 Python 3

Print Statement print "Hello" (without parentheses) print("Hello") (with parentheses)

5/2 returns 2 (integer division by


gr

Integer Division 5/2 returns 2.5 (true division)


default)

Unicode Strings are Unicode by default (str


a

Strings are ASCII by default


Support type)
st

Used xrange() for memory-efficient


xrange() range() now behaves like xrange()
looping
In

1
Feature Python 2 Python 3

raw_input() for strings, input() for


Input Function input() takes all input as strings

a
integers

ry
All new development is for Python
Libraries Some libraries support Python 2 only
3

u
Example:

r.s
# Python 2 (Not supported anymore)

print "Hello, World" # No parentheses

de
# Python 3

co
print("Hello, World") # Requires parentheses
:@
2. What Are Python Decorators and How Do They Work?
A decorator is a function that modifies the behavior of another function without changing
its structure. It is often used for logging, authentication, and timing functions.
m

Example: Logging with a Decorator

defdecorator_function(original_function):
ra

def wrapper_function():
ag

print(f"Executing {original_function.__name__}")

return original_function()
st

return wrapper_function
In

@decorator_function # This is how decorators are applied

def say_hello():

print("Hello!")

2
say_hello()

Output:

Executing say_hello

Hello!

Here, decorator_function wraps say_hello() and adds extra functionality.

a
3. Difference Between a List and a Tuple

ry
Feature List (list) Tuple (tuple)

u
Mutability Mutable (can be changed) Immutable (cannot be changed)

r.s
Performance Slower due to dynamic resizing Faster due to fixed size

Syntax Defined using [] Defined using ()

Use Case
de
When data needs modification When data should remain constant

Example:
co
# List (Mutable)

my_list = [1, 2, 3]
:@

my_list[0] = 10 # Allowed

print(my_list) # [10, 2, 3]
am

# Tuple (Immutable)

my_tuple = (1, 2, 3)
gr

# my_tuple[0] = 10 # Error: TypeError: 'tuple' object does not support item assignment

Lists are preferred when frequent modifications are needed, whereas tuples are used for
a

fixed data.
st

4. What is a Lambda Function?


In

3
A lambda function is an anonymous function in Python that can have multiple arguments
but only one expression.
Syntax:

lambda arguments: expression

a
Example:

ry
# Regular Function
def square(x):

u
return x * x

r.s
de
print(square(5)) # Output: 25

# Lambda Equivalent
co
square_lambda = lambda x: x * x

print(square_lambda(5)) # Output: 25
:@

Lambda functions are often used in map(), filter(), and sorted() operations.

Example with sorted():


am

data = [(1, "Alice"), (3, "Bob"), (2, "Charlie")]

sorted_data = sorted(data, key=lambda x: x[0])

print(sorted_data) # [(1, "Alice"), (2, "Charlie"), (3, "Bob")]


gr

5. What is a Python Generator and How is it Different from a


a

Normal Function?
st

A generator is a special type of iterator that produces values lazily, meaning it generates
In

values on the fly instead of storing them in memory. It is defined using the yield keyword.
Key Differences Between Generators and Normal Functions

4
Feature Normal Function Generator Function

Generates values one by one


Memory Usage Stores all values in memory
(efficient)

Executes completely when


Execution Saves state and resumes from yield
called

a
Return
Uses return Uses yield

ry
Statement

Example: Normal Function vs. Generator

u
# Normal function (stores all values)

r.s
defsquare_list(n):

result = []

for i in range(n):

[Link](i * i) de
co
return result
:@

print(square_list(5)) # [0, 1, 4, 9, 16]

# Generator function (yields one value at a time)


am

defsquare_generator(n):

foriin range(n):

yield i * i
a gr

gen=square_generator(5)
st

print(next(gen)) # Output: 0

print(next(gen)) # Output: 1
In

print(next(gen)) # Output: 4

5
Using yield allows generators to be memory efficient when dealing with large datasets.

6. How Does Python Handle Memory Management?

a
Python uses automatic memory management and relies on a garbage collector to free
up unused memory. The key components of Python’s memory management system are:

ry
Key Aspects of Python Memory Management

u
1. Reference Counting:

r.s
o Every object in Python has a reference count (i.e., the number of variables
pointing to it).

o When the reference count drops to zero, Python automatically deletes the

de
object.

Example:
co
import sys
:@

a = [1, 2, 3]

print([Link](a)) # Output: 2 (one reference from 'a' and another from function call)

b = a # Another reference to the same object


am

print([Link](a)) # Output: 3
gr

del a # Deletes one reference

print([Link](b)) # Output: 2
a

2. Garbage Collection:
st

o Python uses a garbage collector to clean up cyclic references (e.g., objects


referring to each other).
In

The gc module can be used to manually trigger garbage collection.


o

Example:

import gc

6
Using yield allows generators to be memory efficient when dealing with large datasets.

6. How Does Python Handle Memory Management?

a
Python uses automatic memory management and relies on a garbage collector to free
up unused memory. The key components of Python’s memory management system are:

ry
Key Aspects of Python Memory Management

u
1. Reference Counting:

r.s
o Every object in Python has a reference count (i.e., the number of variables
pointing to it).

o When the reference count drops to zero, Python automatically deletes the

de
object.

Example:
co
import sys
:@

a = [1, 2, 3]

print([Link](a)) # Output: 2 (one reference from 'a' and another from function call)

b = a # Another reference to the same object


am

print([Link](a)) # Output: 3
gr

del a # Deletes one reference

print([Link](b)) # Output: 2
a

2. Garbage Collection:
st

o Python uses a garbage collector to clean up cyclic references (e.g., objects


referring to each other).
In

The gc module can be used to manually trigger garbage collection.


o

Example:

import gc

7
[Link]() # Force garbage collection

3. Memory Pools (Private Heap):

o Python does not release memory back to the OS immediately; it maintains a


private heap for efficient allocation.

o The PyObject_Malloc() function handles memory allocation internally.

Optimizing Memory Usage:

a
Use generators instead of lists to handle large datasets efficiently.

ry

• Use slots in classes to reduce memory usage.

u
r.s
7. What Is the Difference Between deepcopy() and copy() in
Python?

Feature [Link]() (Shallow Copy)


de
The copy module in Python provides two ways to duplicate objects:

[Link]() (Deep Copy)


co
Creates a new object but Creates a completely independent
Definition
references nested objects copy
:@

Nested Changes in nested objects affect Changes in nested objects do not


Objects both copies affect the original
Performance Slower (due to recursion)
Faster
am

Example:

import copy
gr

# Original list with nested list


a

original_list = [[1, 2, 3], [4, 5, 6]]


st

# Shallow Copy
In

shallow_copy = [Link](original_list)

8
# Deep Copy

deep_copy = [Link](original_list)

a
ry
# Modify the nested list

original_list[0][0] = 99

u
r.s
print(shallow_copy) # [[99, 2, 3], [4, 5, 6]] (affected)

print(deep_copy) # [[1, 2, 3], [4, 5, 6]] (unchanged)

de
When to Use?

• Use shallow copy when the object has immutable elements or when deep copy
isn't necessary.
co

Use deep copy when working with nested data structures that require full
duplication.
:@

8. What Is a Python Module and Package?


am

Module:

• A module is a single Python file (.py) that contains functions, classes, and variables.

• It allows code reuse by importing it into other scripts.


gr

Example of a Module (math_utils.py):

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


a
st

Importing the Module:


In

python

CopyEdit

import math_utils

9
print(math_utils.add(3, 5)) # Output: 8

Package:

• A package is a collection of modules organized in directories containing an


__init__.py file.

a

It helps in structuring large codebases.

ry
Example of a Package:

u
markdown

CopyEdit

r.s
my_package/

│── __init__.py

de
│── [Link]

│── [Link]
co
Using a Package:

from my_package import module1


:@

Key Differences Between Modules and Packages

Feature Module Package


am

Structure A single .py file A directory with multiple modules

Purpose Organizes related functions/classes Groups related modules


gr

Importing import module_name from package_name import module_name


a

9. What Is the Purpose of the with Statement in Python?


st

The with statement is used for resource management (e.g., file handling, database
connections) and ensures that resources are automatically closed after execution.
In

Why Use the with Statement?

• Ensures Proper Cleanup: Resources are automatically released (e.g., closing a


file).

10
• Prevents Memory Leaks: Avoids forgetting to release resources.

• Reduces Code Complexity: No need for try-except-finally blocks.

Example: File Handling Without with

a
file = open("[Link]", "w")

ry
try:

[Link]("Hello, World!")

u
finally:

r.s
[Link]() # Must be manually closed

Example: File Handling With with

de
with open("[Link]", "w") as file:

[Link]("Hello, World!") # File closes automatically


co
Here, the file is automatically closed once the with block exits.
:@

Data Structures and Algorithms:


[Link],andHow Is It Different from a
List?
am

A dictionary in Python is an unordered collection of key-value pairs, where keys are


unique and immutable (e.g., strings, numbers, tuples). It allows for fast lookups and
modifications.
gr

Dictionary vs. List


a

Feature Dictionary (dict) List (list)


st

Ordered sequence of elements


Structure Key-value pairs ({key: value})
([item1, item2])
In

Slower (O(n) for searching an


Fast (O(1) for key lookup using a
Access Time element)
hash table)

11
Feature Dictionary (dict) List (list)

Order (Python
Maintains insertion order Maintains order
3.7+)

a
Keys must be unique and
Key/Index Type Indices are integers starting from 0

ry
immutable

Use Case Fast lookups, structured data Ordered collections, sequences

u
Example of a Dictionary

r.s
employee = {

"name": "Alice",

de
"age": 30,

"role": "Data Analyst" co


}
:@

print(employee["name"]) # Output: Alice

Example of a List

fruits = ["apple", "banana", "cherry"]


am

print(fruits[1]) # Output: banana

When to Use?
gr

• Use a dictionary when you need quick lookups based on unique identifiers (keys).
• Use a list when order matters and indexed access is required.
a
st

11. How Would You Reverse a String in Python?


There are multiple ways to reverse a string in Python:
In

1. Using String Slicing (Fastest and Easiest)

s = "Goldman"

reversed_s = s[::-1]

12
print(reversed_s) # Output: "namdloG"

2. Using reversed() and join()

s = "Sachs"

a
reversed_s = "".join(reversed(s))

ry
print(reversed_s) # Output: "shcaS"

3. Using a Loop

u
s = "Python"

r.s
reversed_s = ""

for char in s:

de
reversed_s = char + reversed_s # Adding characters in reverse order

print(reversed_s) # Output: "nohtyP" co


4. Using Recursion

defreverse_string(s):
:@

iflen(s) == 0:

return s

return s[-1] + reverse_string(s[:-1])


am

print(reverse_string("Finance")) # Output: "ecnaniF"

Best Approach?
gr

• String slicing ([::-1]) is the most efficient method because it is optimized at the C
level.
a


reversed() with join() is also efficient but slightly less readable.
st

• Loops and recursion are less efficient and mainly used for demonstration
purposes.
In

12. Explain the Difference Between is and == in Python.

13
Python provides two comparison operators:

Operator Purpose Example

==
Compares values x == y checks if values of x and y are the same

a
(Equality)

ry
Compares memory x is y checks if x and y refer to the same object in
is (Identity)
locations memory

u
Example 1: Using == (Value Comparison)

r.s
a = [1, 2, 3]
b = [1, 2, 3]

de
print(a == b) # True (because values are the same) print(a is b) #
co
False (because they are different objects in memory) Example 2:

Using is (Identity Comparison) x= "hello" y= "hello"


:@
am

print(x == y) # True (values are the same)

print(x is y) # True (Python optimizes immutable objects like strings and caches them)

Example 3: Identity in Mutability


gr

list1 = [10, 20, 30]


a

list2 = list1 # Both point to the same object


st

print(list1 is list2) # True


In

[Link](40)

print(list2) # Output: [10, 20, 30, 40] (because both refer to the same object)

Key Takeaways

14
• Use == when comparing values.

• Use is when checking whether two variables reference the same object.

a
13. How Would You Remove Duplicates from a List in Python?

ry
There are multiple ways to remove duplicates:

1. Using set() (Fastest but Unordered)

u
nums = [4, 2, 7, 4, 2, 8]

r.s
unique_nums = list(set(nums))

print(unique_nums) # Output: [2, 4, 7, 8] (order may change)

de
• ⚠ Limitation: Order is not preserved.
2. Using [Link]() (Preserves Order)
co
nums = [4, 2, 7, 4, 2, 8]
unique_nums = list([Link](nums))
:@

print(unique_nums) # Output: [4, 2, 7, 8]

• Advantage: Maintains order (Python 3.7+).

3. Using a Loop (Manual Method)


am

nums = [4, 2, 7, 4, 2, 8]

unique_nums = []
gr

for num in nums:

if num not in unique_nums:


a

unique_nums.append(num)
st

print(unique_nums) # Output: [4, 2, 7, 8]

• ⚠ Limitation: Slower (O(n²) complexity).


In

4. Using List Comprehension + set()

nums = [4, 2, 7, 4, 2, 8]

15
seen = set()

unique_nums = [num for num in nums if num not in seen and not [Link](num)]

print(unique_nums) # Output: [4, 2, 7, 8]

a
• Advantage: Preserves order and is efficient.

ry
Best Approach?

• Use set() for speed when order doesn't matter.

u
• Use [Link]() or list comprehension for order preservation.

r.s
14. How Do You Implement a Stack or Queue in Python?

de
A stack follows the Last-In-First-Out (LIFO) principle, while a queue follows the First-In-
First-Out (FIFO) principle. Both can be implemented in Python using lists or the
[Link] module.
co
StackImplementation (LIFO)

Usinga Python list:


:@

classStack:

def__init__(self):
am

[Link] = []

defpush(self, item):
gr

[Link](item) # Add to the top


a

defpop(self):
st

ifnot self.is_empty():
In

return [Link]() # Remove from the top

return "Stack is empty"

16
defpeek(self):

ifnot self.is_empty():

return [Link][-1] # Get the top element without removing it

a
return "Stack is empty"

ry
defis_empty(self):

u
return len([Link]) == 0

r.s
#Usage s=Stack()

de
[Link](10) [Link](20)

print([Link]()) # Output: 20 co
print([Link]()) # Output: 10
:@

• Push: O(1)
• Pop: O(1)
am

Queue Implementation (FIFO)

Using [Link] (more efficient than a list for pop operations):

from collections import deque


a gr

class Queue:
st

def __init__(self):

[Link] = deque()
In

def enqueue(self, item):

[Link](item) # Add to the rear

17
defdequeue(self):

ifnot self.is_empty():

a
return [Link]() # Remove from the front

ry
return "Queue is empty"

u
defis_empty(self):

r.s
return len([Link]) == 0

de
#Usage q=Queue()

[Link](10) [Link](20) co
print([Link]()) # Output: 10
:@

• Enqueue: O(1)
• Dequeue: O(1)
am

Key Takeaway:

• Stack: Use a list or deque for LIFO operations.


• Queue: Use deque or [Link] for FIFO operations (more efficient than list
gr

pop(0)).
a

15. How Would You Find the Most Common Element in a List?
st

There are multiple ways to find the most common element.


In

1. Using [Link] (Recommended)

from collections import Counter

18
nums = [1, 3, 3, 3, 2, 2, 4]

counter = Counter(nums)

most_common = counter.most_common(1)[0][0] # Returns most frequent element

a
print(most_common) # Output: 3

ry
• Counter(nums).most_common(1) returns a list of tuples [(3, 3)], so we extract the
first element.

u
2. Using max() with count() (Inefficient for Large Lists)

nums = [1, 3, 3, 3, 2, 2, 4]

r.s
most_common = max(set(nums), key=[Link])

print(most_common) # Output: 3

de
• Time Complexity: O(n²) because count() iterates over the list multiple times.

3. Using a Dictionary (Manual Counting)


co
nums = [1, 3, 3, 3, 2, 2, 4]

freq = {}
:@

for num in nums:


am

freq[num] = [Link](num, 0) + 1 # Increase count for each element

most_common = max(freq, key=[Link]) # Get the key with max value


gr

print(most_common) # Output: 3

• Time Complexity: O(n)


a

Key Takeaway:
st

• Use [Link] (O(n)) for efficiency.


In

• Avoid max(set(nums), key=[Link]) (O(n²)) for large lists.

19
16. What Is the Time Complexity of the Built-in sorted() Function
in Python?
Thebuilt-in sorted() function in PythonusesTimsort,ahybridsortingalgorithmcombining
Merge Sort and Insertion Sort.

a
Time Complexity of sorted()

ry
Case Complexity

u
Best Case (Nearly Sorted) O(n)

r.s
Average Case O(n log n)

Worst Case O(n log n)

de
Example Usage

arr = [4, 2, 7, 1, 9]
co
sorted_arr = sorted(arr) # Returns a new sorted list

print(sorted_arr) # Output: [1, 2, 4, 7, 9]


:@

Why O(n log n) Complexity?

• Merge Sort contributes O(n log n).



am

Insertion Sort improves performance for nearly sorted data (O(n) in the best case).

Custom Sorting

Sorting by absolute values:


gr

arr = [-3, -1, 2, -7]

sorted_arr = sorted(arr, key=abs)


a

print(sorted_arr) # Output: [-1, 2, -3, -7]


st

Key Takeaway:
In

• sorted() is highly optimized and stable (preserves order of equal elements).



Always runs in O(n log n) in the worst case.

20
17. How Would You Implement a Binary Search Algorithm in
Python?
BinarySearchisanefficientsearch [Link]
divide-and-conquerapproach.

a
BinarySearchAlgorithm(Iterative)

ry
defbinary_search(arr,target):

u
left,right = 0, len(arr) -1

r.s
whileleft <= right:

de
mid=(left+right)//2# Find the middle index

if arr[mid] == target:
co
returnmid#Targetfound

elif arr[mid] < target:


:@

left=mid+1#Search in the right half

else:
am

right=mid-1#Search in the left half

return-1#Targetnotfound
a gr

# Example
st

arr = [1, 3, 5, 7, 9, 11]

target = 7
In

print(binary_search(arr,target)) # Output: 3 (Index of 7)

BinarySearchAlgorithm(Recursive)

def binary_search_recursive(arr, left, right, target):

21
if left> right:

return-1#Base case: target not found

a
mid=(left+right) // 2

ry
ifarr[mid]==target:

u
return mid

r.s
elifarr[mid]<target:

returnbinary_search_recursive(arr, mid + 1, right, target)

de
else:

returnbinary_search_recursive(arr, left, mid - 1, target)


co
# Example
:@

arr=[1,3,5,7,9, 11]

target = 5

print(binary_search_recursive(arr, 0, len(arr) - 1, target)) # Output: 2


am

Time Complexity

Case Complexity
gr

Best Case (Middle Element) O(1)

Average Case O(log n)


a

Worst Case O(log n)


st

Key Takeaway:
In

• Binary Search works only on sorted lists.


• O(log n) makes it much faster than linear search (O(n)).

22
18. What Is the Purpose of the collections Module in Python?
The collectionsmodule provides specializedcontainerdatatypesbeyondPython’sbuilt-in
types (list, tuple, dict, set). These are optimized for performance and memory efficiency.
Key Data Structures in collections

a
ry
1. Counter – Counts elements in an iterable.

2. defaultdict – A dictionary with default values.

u
3. OrderedDict – A dictionary that maintains the order of keys.

r.s
4. deque – A double-ended queue for fast append/pop.

5. namedtuple – A tuple with named fields.

de
Example 1: Counter – Counting Elements co
from collections import Counter
:@

nums = [1, 2, 2, 3, 3, 3, 4]

counter = Counter(nums)

print(counter) # Output: Counter({3: 3, 2: 2, 1: 1, 4: 1})


am

print(counter.most_common(1)) # Output: [(3, 3)] (Most frequent element)

• Use Case: Count word occurrences in a text file.


gr

Example 2: defaultdict – Avoiding Key Errors


a

from collections import defaultdict


st

word_count = defaultdict(int) # Default value is 0


In

words = ["apple", "banana", "apple"]

for word in words:

23
word_count[word] += 1 # No need to check if key exists

print(word_count) # Output: {'apple': 2, 'banana': 1}

a
• Use Case: Grouping data without handling missing keys.

ry
Example 3: OrderedDict – Preserving Insertion Order

u
from collections import OrderedDict

r.s
ordered_dict = OrderedDict()

de
ordered_dict["a"] = 1

ordered_dict["b"] = 2 co
ordered_dict["c"] = 3
:@

print(ordered_dict) # Output: OrderedDict([('a', 1), ('b', 2), ('c', 3)])

• Use Case: Cache implementations where order matters.


am

Example 4: deque – Efficient Append and Pop

from collections import deque


gr

dq = deque([1, 2, 3])
a

[Link](4) # Append to the right


st

[Link](0) # Append to the left


In

print(dq) # Output: deque([0, 1, 2, 3, 4])

print([Link]()) # Output: 4 (Removes from the right)

print([Link]()) # Output: 0 (Removes from the left)

24
• Use Case: Implementing queues/stacks efficiently.

Example 5: namedtuple – Creating Readable Tuples

a
from collections import namedtuple

ry
Person = namedtuple("Person", ["name", "age"])

u
p = Person("Alice", 30)

r.s
print([Link]) # Output: Alice

de
print([Link]) # Output: 30

• Use Case: Storing structured data like database rows.


co
Key Takeaway:
The collections module provides optimized data structures for counting, ordering,
queuing, and structuring data.
:@

19. What Are List Comprehensions in Python? Can You Give an


Example?
am

A list comprehension is a concise way to create lists using a single-line expression.

Basic Syntax:
gr

[expression for item in iterable if condition]

• expression → The value to be stored in the new list.


a

• iterable → The sequence to iterate over.


st


condition (optional) → A filter for elements.
In

Example 1: Creating a List of Squares

squares = [x**2 for x in range(5)]

25
print(squares) # Output: [0, 1, 4, 9, 16]

Equivalent to:

squares = []

a
for x in range(5):

ry
[Link](x**2)

u
Example 2: Filtering Even Numbers

r.s
nums = [1, 2, 3, 4, 5, 6]

evens = [x for x in nums if x % 2 == 0]

de
print(evens) # Output: [2, 4, 6]

• Condition (if x % 2 == 0) filters elements.


co
Example 3: Nested List Comprehension
:@

matrix = [[j for j in range(3)] for i in range(3)]

print(matrix)

# Output: [[0, 1, 2], [0, 1, 2], [0, 1, 2]]


am

• Creates a 3×3 matrix.

Example 4: Using if-else in List Comprehension


gr

nums = [1, 2, 3, 4, 5]
a

result = ["Even" if x % 2 == 0 else "Odd" for x in nums]


st

print(result) # Output: ['Odd', 'Even', 'Odd', 'Even', 'Odd']

• Inline if-else condition.


In

Example 5: Flattening a Nested List

nested_list = [[1, 2], [3, 4], [5, 6]]

26
flat_list = [num for sublist in nested_list for num in sublist]

print(flat_list) # Output: [1, 2, 3, 4, 5, 6]

• Extracts elements from sublists into a single list.

a
Key Takeaway:
List comprehensions are faster and more readable than traditional loops.

u ry
Conclusion

r.s
• collections module provides efficient data structures for counting, ordering,
queuing, and structuring data.

• List comprehensions simplify list creation and filtering, making code more

de
Pythonic.

Object-Oriented Programming (OOP):


co
[Link] the Conceptof InheritanceinPython.

Inheritance is an OOP (Object-Oriented Programming) concept where a class inherits


:@

properties and behaviors (methods) from another class. This promotes code reusability
and modularity.
Types of Inheritance in Python
am

1. Single Inheritance – One class inherits from another.

2. Multiple Inheritance – A class inherits from multiple classes.


gr

3. Multilevel Inheritance – A class inherits from a derived class.

4. Hierarchical Inheritance – Multiple classes inherit from one base class.


a

5. Hybrid Inheritance – A mix of the above.


st

Example 1: Single Inheritance


In

class Animal:

def speak(self):

return "I make sounds"

27
class Dog(Animal): # Dog inherits from Animal

def speak(self):

a
return "Bark"

ry
dog= Dog()

u
print([Link]()) # Output: Bark

r.s
• The Dog class overrides the speak() method from Animal.

de
Example 2: Multiple Inheritance

class Engine: co
def start(self):

return "Engine started"


:@

class Wheels:

def roll(self):
am

return "Wheels rolling"

class Car(Engine, Wheels):


gr

pass
a
st

my_car = Car()

print(my_car.start()) # Output: Engine started


In

print(my_car.roll()) # Output: Wheels rolling

• Car class inherits methods from both Engine and Wheels.

28
Example 3: Multilevel Inheritance

class Animal:

def breathe(self):

a
return "Breathing"

ry
class Mammal(Animal):

u
def has_fur(self):

r.s
return "I have fur"

de
class Dog(Mammal):

def bark(self): co
return "Bark"
:@

dog= Dog()

print([Link]()) # Output: Breathing

print(dog.has_fur()) # Output: I have fur


am

print([Link]()) # Output: Bark

• Dog inherits from Mammal, which inherits from Animal.

Key Takeaway:
gr

Inheritance eliminates code duplication and follows the DRY principle (Don't Repeat
Yourself).
a
st

21. What Is the Purpose of the __init__ Method in Python?


In

The __init__ method is a constructor in Python that initializes an object when a class is
instantiated. It allows passing values at the time of object creation.

29
Example: Using __init__ for Initialization

classPerson:

def__init__(self, name, age):

a
[Link] = name # Instance variable

ry
[Link] = age

u
defgreet(self):

r.s
return f"Hi, I'm {[Link]} and I'm {[Link]} years old."

de
p1=Person("Alice", 25)

print([Link]()) # Output: Hi, I'm Alice and I'm 25 years old.


co
• __init__ assigns values to name and age at object creation.

• Without __init__, attributes must be assigned manually.


:@

Example: Default Arguments in __init__

classCar:
am

def__init__(self, brand="Toyota"):

[Link] = brand
gr

car1=Car("BMW")
a

car2=Car() # Uses default value


st

print([Link]) # Output: BMW


In

print([Link]) # Output: Toyota

• If no value is provided, the default brand="Toyota" is used.

30
Key Takeaway:
__init__ automates object initialization, improving efficiency and code clarity.

22. What Are Class Variables and Instance Variables in Python?

a
ry
Python classes have two types of variables:

1. Instance Variables – Defined inside __init__, unique to each object.

u
2. Class Variables – Defined inside the class but shared among all objects.

r.s
Example: Difference Between Instance and Class Variables

de
class Employee:

company = "Google" # Class variable co


def __init__(self, name, salary):
:@

[Link] = name # Instance variable

[Link] = salary
am

emp1 = Employee("Alice", 70000)

emp2 = Employee("Bob", 80000)


gr

print([Link]) # Output: Google


a

print([Link]) # Output: Google


st

[Link] = "Microsoft" # Changing class variable


In

print([Link]) # Output: Microsoft

print([Link]) # Output: Microsoft

31
[Link] = 75000 # Changing instance variable (only for emp1)

print([Link]) # Output: 75000 print([Link]) # Output:

80000

a
• company is a class variable → shared across all instances.

ry
• name and salary are instance variables → unique for each object.

u
Key Differences

r.s
Feature Class Variable Instance Variable

Definition Inside the class Inside __init__

de
Shared? Yes (same across all instances) No (unique per instance)

Accessed via [Link] [Link]


co
Modification Changes reflect for all objects Changes affect only that object
:@

Key Takeaway:

• Class variables store global properties of a class.



Instance variables store unique attributes per object.
am

Conclusion

• Inheritance allows a class to derive properties from another class.


gr

• __init__ initializes objects automatically at creation.



a

Class variables are shared across instances, while instance variables are unique
per object.
st

23. Explain the Concept of Polymorphism in Python


In

Polymorphism means "many forms" and refers to the ability of different objects to be
treated as instances of the same class through a shared interface. In Python,
polymorphism allows methods in different classes to share the same name but behave
differently based on the object calling them.

32
Example 1: Polymorphism in Methods

class Dog:

a
def speak(self):

ry
return "Bark"

u
class Cat:

r.s
def speak(self):

return "Meow"

de
#Function that demonstrates polymorphism co
defmake_sound(animal):

print([Link]())
:@

dog = Dog()

cat = Cat()
am

make_sound(dog) # Output: Bark

make_sound(cat) # Output: Meow


gr

Here, speak() behaves differently depending on whether the object is a Dog or a


Cat.
a
st

Example 2: Polymorphism in Built-in Functions


In

print(len("Hello")) # Output: 5 (String length)

print(len([1, 2, 3, 4])) # Output: 4 (List length)

print(len({"a": 1, "b": 2})) # Output: 2 (Dictionary length)

33
Python’s len() function behaves differently depending on the data type.

Example 3: Polymorphism in Class Inheritance (Method Overriding)

a
class Animal:

ry
def make_sound(self):

return "Some sound"

u
r.s
class Dog(Animal):

def make_sound(self): # Overriding method

de
return "Bark"
co
dog= Dog()

print(dog.make_sound()) # Output: Bark


:@

The make_sound() method in Dog overrides the one in Animal.

Key Takeaway

Polymorphism allows code flexibility by allowing different classes to be treated as a


am

single entity, reducing redundancy and improving maintainability.

24. What is the Difference Between @staticmethod and


gr

@classmethod?
a

Both @staticmethod and @classmethod modify the behavior of methods, but they serve
different purposes.
st

1 .@staticmethod
In

• Does not require self or cls.

• It belongs to the class but does not access or modify class attributes.

• Used for utility/helper functions.

34
Example: @staticmethod

class MathOperations:

@staticmethod

a
def add(x, y):

ry
return x + y # No need to access class attributes

u
print([Link](3, 5)) # Output: 8

r.s
Here, add() is just a utility function that does not interact with the class.

de
2.@classmethod

• Takes cls as the first argument.


co
• Can modify class-level attributes.

• Used when a method needs to work on the class itself, not instances.
:@

Example: @classmethod

class Employee:

company = "Google"
am

@classmethod
gr

def change_company(cls, new_name):

[Link] = new_name # Modifies class variable


a
st

Employee.change_company("Microsoft")

print([Link]) # Output: Microsoft


In

Here, change_company() modifies the class attribute company.

Key Differences:

35
Feature @staticmethod @classmethod

Requires self or cls? No Yes (cls)

Accesses instance variables? No No

a
Modifies class variables? No Yes

ry
Called on Class or instance Class or instance

u
Use case Utility functions Class-wide modifications

r.s
Key Takeaway

• Use @staticmethod for independent utility functions.

de
• Use @classmethod when modifying class-level attributes.
co
25. What is Multiple Inheritance in Python, and How Does It
Work?
:@

Multiple Inheritance allows a class to inherit from more than one parent class.

Python supports multiple inheritance, unlike some languages (e.g., Java), which
only support single inheritance.
am

Example 1: Basic Multiple Inheritance


gr

class Engine:

def start(self):
a

return "Engine started"


st

class Wheels:
In

def roll(self):

return "Wheels rolling"

36
class Car(Engine, Wheels): # Multiple inheritance

pass

a
my_car = Car()

ry
print(my_car.start()) # Output: Engine started

print(my_car.roll()) # Output: Wheels rolling

u
Here, Car inherits from both Engine and Wheels, gaining access to both methods.

r.s
Example 2: Method Resolution Order (MRO) in Multiple Inheritance

de
If multiple parent classes have a method with the same name, Python follows MRO
(Method Resolution Order) to determine which method to execute.
class A:
co
def show(self):

return "A"
:@

class B(A):
am

def show(self):

return "B"
gr

class C(A):

def show(self):
a

return "C"
st
In

class D(B, C): # Multiple inheritance

pass

37
obj = D()

print([Link]()) # Output: B

Python follows the order defined in the class declaration (D(B, C)). It checks B first
before C.

a
ry
How to Check MRO?

u
print([Link]())

r.s
# Output: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class
'__main__.A'>, <class 'object'>]

This shows the order in which Python searches for methods when calling

de
[Link]().
co
Key Takeaway

• Multiple inheritance allows a class to inherit from multiple classes.


:@

• MRO (Method Resolution Order) decides which method is executed when there's a
conflict.

• Python follows C3 linearization (MRO algorithm) to determine the method call


am

order.

Final Summary
gr

Concept Key Takeaways


a

Allows the same method to work differently across


Polymorphism
different objects.
st

@staticmethod is for utility functions, @classmethod


@staticmethod vs
modifies class attributes.
In

@classmethod
Allows inheriting from multiple classes, follows MRO for
method resolution.
Multiple Inheritance

38
Advanced Python:
[Link]’sBuilt-in Data Types?

a
Pythonprovidesseveral built-in datatypescategorizedasnumeric, sequence, set,

ry
mapping, and boolean types.

u
Numeric Types

int → Integer numbers

r.s


float → Floating-point numbers

de
• complex → Complex numbers

Example:

a = 10 # int
co
b = 3.14 # float

c = 2 + 3j # complex
:@

Sequence Types

• list → Ordered, mutable collection


am


tuple → Ordered, immutable collection

range → Represents a sequence of numbers

Example:
gr

lst = [1, 2, 3] # List

tup = (4, 5, 6) # Tuple


a

rng = range(1, 5) # 1, 2, 3, 4
st

Set Types
In

• set → Unordered collection of unique elements



frozenset → Immutable set

Example:

39
s={1, 2, 3, 3} # {1, 2, 3}

fs=frozenset([4, 5, 6]) # Immutable

Mapping Type

a
• dict → Key-value pairs

ry
Example:

d = {"name": "Alice", "age": 25}

u
Boolean Type

r.s
• bool → True or False

Example:

de
x = True

y = False
co
Binary Data Types

• bytes, bytearray, memoryview


:@

Example:

b = b"hello" # Bytes

ba = bytearray(5) # Mutable byte sequence


am

Key Takeaway: Python has diverse data types for different needs, from numbers and
sequences to mappings and binary data.
gr

27. What is the Global Interpreter Lock (GIL) in Python, and How
a

Does It Affect Multi-threading?


st

The Global Interpreter Lock (GIL) is a mutex that allows only one thread to execute
Python bytecode at a time in CPython.
In

Why Does GIL Exist?

• Python's memory management uses reference counting.



GIL prevents race conditions when multiple threads modify the same object.

40
• Ensures thread safety.

Impact of GIL on Multi-threading

• CPU-bound tasks (e.g., computations) are slow in multi-threading due to GIL.

a
• I/O-bound tasks (e.g., file I/O, network calls) are not affected much.

ry
Example: GIL Effect on Multi-threading

import threading

u
r.s
defcount():

for_ in range(1000000):

de
pass # CPU-intensive task
co
t1=[Link](target=count)

t2=[Link](target=count)
:@

[Link]()

[Link]()
am

[Link]()

[Link]()
gr

Even with two threads, execution time does not improve due to GIL.
a

Workarounds for GIL


st

Use multiprocessing (creates separate processes):

from multiprocessing import Process


In

p1 = Process(target=count)

p2 = Process(target=count)

41
[Link]()

[Link]()

a
ry
[Link]()

[Link]()

u
Use Jython or PyPy (alternative Python implementations without GIL).

r.s
Use async programming for I/O-bound tasks.
Key Takeaway: GIL limits multi-threading for CPU-bound tasks but not for I/O-bound

de
tasks. Use multiprocessing for parallelism.

28. What is the Difference Between map() and filter() in Python?


co
Both map() and filter() are higher-order functions used for processing iterables.
:@

map() Function

• Applies a function to each element in an iterable.

• Returns a map object (which can be converted to a list, tuple, etc.).


am

Example:

nums = [1, 2, 3, 4]

squared = map(lambda x: x**2, nums)


gr

print(list(squared)) # Output: [1, 4, 9, 16]


a

Used for transforming each element.


st

filter() Function
In

• Filters elements based on a function that returns True or False.

• Returns a filter object.

Example:

42
nums = [1, 2, 3, 4, 5, 6]

evens = filter(lambda x: x % 2 == 0, nums)

print(list(evens)) # Output: [2, 4, 6]

a
Used for selecting elements based on a condition.

ry
Key Differences:

u
Feature map() filter()

r.s
Purpose Transforms elements Filters elements

Returns New iterable with modified values New iterable with filtered values

de
Function Output Function returns any valueco Function must return True/False

Key Takeaway: Use map() for transformation, and filter() for selection.
:@

29. Explain How to Handle Exceptions in Python with


try/except/finally.
Python uses try/except/finally to handle runtime errors (exceptions).
am

Basic Structure

try:
gr

# Code that may raise an exception


x = 10 / 0
a

except ZeroDivisionError as e:
st

print(f"Error: {e}")

finally:
In

print("This will always execute.")

The finally block runs regardless of whether an exception occurs.

43
Handling Multiple Exceptions

try:

x = int("abc") # ValueError

a
except (ZeroDivisionError, ValueError) as e:

ry
print(f"Handled error: {e}")

u
Using else with try/except

r.s
try:

x = 10 / 2 # No error

de
except ZeroDivisionError:

print("Cannot divide by zero")


co
else:

print("No errors occurred!") # Runs if no exception occurs


:@

Key Takeaway: Always handle exceptions properly to prevent crashes. Use finally for
cleanup actions.
am

30. How Would You Implement Concurrency in Python?


Python provides three main ways to achieve concurrency:
gr

[Link]-threading (for I/O-bound tasks)

import threading
a
st

defprint_numbers():

foriin range(5):
In

print(i)

t1=[Link](target=print_numbers)

44
[Link]()

[Link]()

Best for I/O-bound tasks like file reading, API calls.

a
ry
[Link]-processing (for CPU-bound tasks)

from multiprocessing import Process

u
r.s
def compute():

print(sum(range(1000000)))

de
p= Process(target=compute)
co
[Link]()

[Link]()
:@

Best for CPU-bound tasks (parallel processing).

[Link] Programming (asyncio)


am

import asyncio
gr

async def task():

print("Task started")
a

await [Link](2) # Simulate I/O


st

print("Task finished")
In

[Link](task())

Best for handling thousands of concurrent tasks efficiently (e.g., API requests, DB
queries).

45
Final Summary

Concept Key Takeaways

a
Python Data
Includes numeric, sequence, set, mapping, and boolean types.

ry
Types
GIL
Allows only one thread at a time; use multiprocessing to bypass it.

u
map() vs filter()
map() transforms elements; filter() selects elements.

r.s
Exception
Handling Use try/except/finally to prevent crashes.

de
Use threading for I/O-bound, multiprocessing for CPU-bound, and
Concurrency
async for high-performance tasks.
co
:@
am
a gr
st
In

46

You might also like