Preparing for a Python interview in 2025?
Whether you’re a fresher or an experienced professional, having the right knowledge is key to success.
At Codegnan, we help you master Python with hands-on training and real-world projects. With 30,000+ students trained and 1,250+ hiring partners, we know what it takes to land a tech job.
In this guide, we cover the most important Python interview questions from freshers to advanced levels.
Python interview questions for freshers
💡 Looking to upskill your Python skills? Explore our training courses:
- Online Python programming course
- Python course in Bangalore
- Python course in Hyderabad
- Python course in Vijayawada
1. What is Python, and what are its key features?
Python is a high-level, interpreted programming language known for its simplicity and readability. It supports multiple programming paradigms, such as procedural, object-oriented, and functional programming. Python is widely used in web development, data science, automation, AI, and more.
Key Features:
- Easy to Learn – Simple syntax like English.
- Interpreted – Executes code line by line.
- Dynamically Typed – No need to declare variable types.
- Cross-Platform – Runs on Windows, Mac, and Linux.
- Extensive Libraries – Supports NumPy, Pandas, TensorFlow, etc.
2. Explain the difference between Python lists and tuples.
Lists and tuples both store multiple values, but they have key differences:
Lists | Tuples |
Lists are mutable, meaning you can change, add, or remove elements after creation. | Tuples are immutable, meaning that once created, their values cannot be changed. |
Lists use square brackets [ ] | Tuples use parentheses ( ) |
Lists are little slower in execution due to constant changes of data | Tuples are faster than lists because they don’t allow changes |
Use lists when data needs to be modified. | Use tuples when data should remain constant |
3. What is __init__() in Python?
__init__() in Python is a special method (also called a constructor) in Python classes. It runs automatically when an object is created. It helps initialise object attributes. When you create an object from a class, Python automatically calls __init__() to prepare the object with the values you provide.
Example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Alice", 30)
print(person.name, person.age) # Output: Alice 30
💡 Explanation: The __init__() method streamlines object creation by automatically setting attributes when an object is instantiated. This avoids the need for separate setup calls and keeps the code cleaner.
4. What are mutable and immutable data types in Python?
- Mutable data types can change after creation. Example: Lists, Dictionaries, Sets.
For example,
my_list = [1, 2, 3]
my_list.append(4) # List changes
print(my_list)
💡 Explanation: This example showcases mutable objects that can be changed.
- Immutable data types cannot change after creation. Example: Tuples, Strings, Integers, Floats.
Example:
text = “hello”
text[0] = “H”
💡 Explanation: This example showcases Immutable objects remain the same after creation.
5. How do list, dictionary, and tuple comprehensions work? Provide examples.
Comprehensions in Python offer a compact way to generate new collections from existing ones. Comprehensions simplify your code by replacing lengthy loops with concise expressions that are easy to read.
- List comprehensions create lists using a single line that includes an expression, a loop, and an optional condition.
- Dictionary comprehensions use a similar syntax to build dictionaries by defining key-value pairs.
- Although there is no direct tuple comprehension, you can create a generator expression and convert it to a tuple.
Examples :
# List comprehension
squares = [x**2 for x in range(5)]
print(squares) # [0, 1, 4, 9, 16]
# Dictionary comprehension
square_dict = {x: x**2 for x in range(5)}
print(square_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Tuple comprehension (using generator expression)
square_tuple = tuple(x**2 for x in range(5))
print(square_tuple) # (0, 1, 4, 9, 16)
💡 Explanation: The list comprehension [x**2 for x in range(5)] loops through numbers 0-4 and squares each.
The dictionary comprehension {x: x**2 for x in range(5)} creates key-value pairs.
Since () creates a generator, we use tuple() to get a tuple.
6. What is the Global Interpreter Lock (GIL) in Python?
The Global Interpreter Lock (GIL) is a mechanism in CPython that ensures only one thread executes Python bytecode at a time. This means that even on multi-core processors, Python threads do not run in parallel for CPU-bound tasks. However, GIL does not affect multi-processing or I/O-bound tasks.
Example:
import threading
counter = 0
def increment():
global counter
for _ in range(1000000):
counter += 1
t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start()
t2.start()
t1.join()
t2.join()
print("Counter:", counter) # Output may be less than 2000000 due to GIL
💡 Explanation: Python threads don’t run in true parallel because of the GIL. The threads take turns holding the GIL, which can cause race conditions and slow down multi-threaded programs. To achieve true parallelism, use the multiprocessing module instead.
7. What are Python’s built-in data types?
Python provides several built-in data types to store different kinds of values:
- Numeric types: int, float, complex
- Sequence types: list, tuple, range
- Text type: str
- Set types: set, frozenset
- Mapping type: dict
- Boolean type: bool
- Binary types: bytes, bytearray, memoryview
Examples:
x = 10 # int
y = 3.14 # float
z = "Hello" # str
l = [1, 2, 3] # list
t = (4, 5, 6) # tuple
s = {7, 8, 9} # set
d = {"a": 1} # dict
b = True # bool
print(type(x), type(y), type(z), type(l), type(t), type(s), type(d), type(b))
8. How does Python handle type conversion (casting)?
Python allows automatic (implicit) and manual (explicit) type conversion. Implicit conversion happens when Python converts a smaller type to a larger type, while explicit conversion (casting) is done using built-in functions like int(), float(), str(), etc.
Example:
# Implicit conversion
a = 5 # int
b = 2.5 # float
c = a + b # float
print(c, type(c)) # Output: 7.5 <class 'float'>
# Explicit conversion
x = "10"
y = int(x) + 5
print(y, type(y)) # Output: 15 <class 'int'>
💡 Explanation: Python avoids data loss by automatically converting types where possible. However, explicit conversion is needed when converting between incompatible types like str to int. You must check compatibility before casting to avoid errors.
- What is the difference between is and == in Python?
The “is” operator checks whether two variables refer to the same object in memory, while “==” checks whether their values are equal.
Example:
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a == c) # True (values are the same)
print(a is c) # False (different memory locations)
print(a is b) # True (same memory location)
💡 Explanation: Even though a and c have the same values, they are stored in different locations in memory. The “is” operator checks memory identity, while “==” checks value equality. This is important when dealing with mutable objects like lists and dictionaries.
9. How can you read and write files in Python?
Python provides the open() function to read and write files. It supports different modes:
- “r” → Read (default)
- “w” → Write (overwrites file)
- “a” → Append (adds to existing content)
- “rb”, “wb” → Read/write in binary mode
Examples :
Writing to a File
with open("example.txt", "w") as file:
file.write("Hello, Python!")
Reading from a File
with open("example.txt", "r") as file:
content = file.read()
print(content) # Output: Hello, Python!
💡 Explanation: The “with” statement ensures the file closes automatically. The “w” mode overwrites existing content, and the “r” mode reads the file’s content.
PYTHON RESOURCES TO HELP IN YOUR CAREER:
- Python course syllabus: course fees, duration, and eligibility
- Python projects for freshers and experienced students
- In-demand Python career paths
Python interview questions for Intermediate
10. Explain common searching and graph traversal algorithms in Python.
The common searching algorithms in Python include Linear Search and Binary Search, and the common graph traversal algorithms are Depth-First Search (DFS) and Breadth-First Search (BFS).
- Linear Search checks every element one by one and works on unsorted lists.
- Binary Search works on sorted lists by dividing the list in half repeatedly.
- DFS explores a graph by going deep along one branch before backtracking.
- BFS explores all neighbours of a node before moving deeper.
Examples :
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
arr = [1, 3, 5, 7, 9, 11]
print(binary_search(arr, 5)) # Output: 2
💡 Explanation: Binary search only works on sorted lists. It repeatedly divides the list in half, checking if the middle element matches the target. If not, it narrows the search to either the left or right half, making it O(log n) in efficiency.
11. What is a KeyError in Python, and how can you handle it?
A KeyError occurs when you try to access a dictionary key that doesn’t exist. This often happens when fetching values from dictionaries without checking if the key exists. You can prevent it using .get() or a try-except block.
Examples:
student = {"name": "Alice", "age": 12}
print(student["grade"]) # Raises KeyError
# Handling with .get()
print(student.get("grade", "Not available")) # Output: Not available
# Handling with try-except
try:
print(student["grade"])
except KeyError:
print("Key not found!")
💡 Explanation: When you access student[“grade”], Python throws a KeyError because “grade” is not in the dictionary. Using get() prevents this by returning a default value. The try-except block catches the error and lets the program continue running without crashing.
12. How does Python manage memory, and what is the role of garbage collection?
Python manages memory automatically using reference counting and garbage collection. When an object has no references, Python’s garbage collector (GC) deletes it to free up memory. The “gc” module can control and manually trigger garbage collection.
Examples:
import gc
class Demo:
def __del__(self):
print("Object deleted")
obj = Demo()
del obj # Deletes the object immediately
gc.collect() # Manually trigger garbage collection
💡 Explanation: When del obj runs, Python removes the object if no references exist. The __del__() method shows when deletion happens. The gc.collect() function forces Python to clean up unused objects. Python’s garbage collector prevents memory leaks by removing objects that are no longer used.
13. What is the difference between shallow and deep copy in Python?
The difference between shallow and deep copy in Python is-
A shallow copy creates a new object, but copies references to the original elements, meaning changes to mutable elements affect both copies. It’s made using copy.copy().
A deep copy creates a new object and recursively copies all elements, ensuring complete independence from the original. It’s made using copy.deepcopy().
Example:
import copy
original = [[1, 2, 3], [4, 5, 6]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
shallow[0][0] = 99 # Affects both original and shallow
deep[1][1] = 88 # Affects only deep copy
print(original) # Output: [[99, 2, 3], [4, 5, 6]]
print(deep) # Output: [[1, 2, 3], [4, 88, 6]]
💡 Explanation: The copy.copy() creates a shallow copy where only references to nested lists are copied. When changes are made on the nested elements it reflects in both original and copy. The copy.deepcopy() creates a completely new copy, so modifying it does not affect the original.
14. How can you use Python’s collections module to simplify coding?
The collections module provides optimised alternatives to built-in data structures, making code more readable and efficient. It includes Counter, defaultdict, OrderedDict, deque, and namedtuple.
Example:
from collections import Counter
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
count = Counter(words)
print(count) # Output: Counter({'apple': 3, 'banana': 2, 'orange': 1})
💡 Explanation: The Counter counts how often each item appears in a list, saving effort compared to writing a loop. Instead of manually tracking counts, Counter does it in one line. Other useful tools include deque for efficient queue operations and defaultdict for avoiding KeyErrors with missing dictionary keys.
15. What is the difference between args and kwargs in function definitions?
*args allows a function to accept any number of positional arguments, while **kwargs allows it to accept any number of keyword arguments (key-value pairs).
Example:
def greet(*args, **kwargs):
for name in args:
print(f"Hello, {name}!")
for key, value in kwargs.items():
print(f"{key}: {value}")
greet("Alice", "Bob", age=25, country="USA")
💡 Explanation: In the example, *args collects “Alice” and “Bob”, treating them as a tuple. **kwargs collects age=25 and country=”USA”, treating them as a dictionary. *args is useful for handling multiple values without defining them one by one, while **kwargs helps when you need named parameters.
16. Explain Python’s exception handling with an example.
Python exception handling mechanism helps manage runtime errors using try, except, finally, and else. This prevents crashes and helps manage errors gracefully.
Examples:
try:
result = 10 / 0
except ZeroDivisionError:
print("Error: Cannot divide by zero!")
else:
print("Division successful.")
finally:
print("Execution completed.")
💡 Explanation: The try block runs the code, and if an error occurs, it moves to the except block. If the user enters 0, the ZeroDivisionError block runs. If a non-number is entered, ValueError is handled. The finally block runs no matter what, ensuring cleanup or a final message.
17. What are Python generators, and how do they differ from iterators?
In Python, generators are special functions that allow you to create iterators in a simple way. They use the yield keyword to produce a sequence of values lazily, meaning they generate each value only when needed, which saves memory.
An iterator, on the other hand, is an object that implements the __iter__() and __next__() methods, allowing you to traverse through all the values.
While all generators are iterators, not all iterators are generators. Generators provide a convenient way to implement the iterator protocol without the need to write a separate class with __iter__() and __next__() methods.
Example:
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
gen = count_up_to(5)
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
💡 Explanation: The count_up_to() function generates numbers up to n one at a time. Unlike lists, which store all values, generators pause execution after yield, resuming when the next() is called. This saves memory when working with large datasets.
18. What is the purpose of the zip() function in Python?
The zip() function combines multiple iterable (like lists or tuples) element-wise into pairs, triplets, or more. It stops when the shortest iterable is exhausted. This function is useful when you need to process multiple lists together in a loop.
Examples:
names = ["Alice", "Bob", "Charlie"]
scores = [90, 85, 95]
zipped = zip(names, scores)
print(list(zipped)) # Output: [('Alice', 90), ('Bob', 85), ('Charlie', 95)]
💡 Explanation: The zip() function pairs elements from names and scores, creating tuples like (“Alice”, 90). If the lists have different lengths, zip() stops at the shortest one. This is useful for processing related data together, like combining student names with scores.
19. What are lambda functions, and how are they used in Python?
Lambda functions in Python are small, anonymous functions defined using the lambda keyword. They can have multiple arguments but only one expression, which is evaluated and returned. They are useful when you need short, throwaway functions without defining a full function using def.
Examples:
# A lambda function that adds 5 to a number
add_five = lambda x: x + 5
print(add_five(10)) # Output: 15
💡 Explanation: Here, lambda x: x + 5 creates a function that takes x as input and returns x + 5.
Lambda functions are commonly used with functions like map(), filter(), and sorted().
Example:
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers))
print(squared) # Output: [1, 4, 9, 16]
💡 Explanation: Here, lambda x: x**2 squares each number in the numbers list. map() applies this function to all elements. Lambda functions make the code shorter and cleaner compared to defining a separate function.
Python Interview Questions for Experienced Candidates
20. What is monkey patching in Python?
Monkey patching is a way to change or modify a module or class at runtime without altering its original code. It allows us to override methods dynamically, but it should be used carefully as it can lead to unexpected behaviour.
Example:
class Animal:
def sound(self):
return "Some sound"
# Define a new function to replace the existing method
def new_sound():
return "Meow"
# Apply monkey patch
Animal.sound = new_sound
# Test the patched class
a = Animal()
print(a.sound()) # Output: Meow
💡 Explanation: In this example, the Animal class originally had a sound method that returned “Some sound.” However, using monkey patching, we replaced it with new_sound, which returns “Meow.” This shows how we can modify existing classes dynamically. However, using monkey patching without proper control can lead to difficult debugging and maintenance issues.
20. How does Python with statement work?
The with statement simplifies resource management by ensuring that files or connections are properly closed after use. It is commonly used when working with files, sockets, or database connections to prevent resource leaks.
Example:
with open(“example.txt”, “w”) as file:
file.write(“Hello, World!”) # File is automatically closed after this block
💡 Explanation: In this example, the with statement opens a file named “example.txt” in write mode. Once the block of code inside with is executed, Python automatically closes the file, even if an error occurs. This prevents issues like file corruption or memory leaks, making code cleaner and more reliable.
21. Why would you use else in a try/except construct?
The else block in a try/except construct is used when no exceptions occur. It ensures that code inside else runs only if the try block executes successfully, keeping error-handling separate from normal execution.
Example:
try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid input! Please enter a number.")
else:
print(f"You entered {number}, which is a valid number.")
💡 Explanation: Here, if the user enters a valid number, the else block executes and prints the entered number. If an exception (ValueError) occurs, the except block runs instead. This helps in writing clean, structured error-handling code.
22. What are Python decorators, and how do they work?
A decorator in Python is a function that modifies another function without changing its actual code. It allows adding extra functionality like logging, authentication, or timing execution.
Example:
def greet_decorator(func):
def wrapper():
print("Hello!")
func()
print("Goodbye!")
return wrapper
@greet_decorator
def say_name():
print("My name is John.")
say_name()
💡 Explanation: Here, greet_decorator takes say_name() as input and modifies it. When say_name() is called, it prints “Hello!” first, then the function output, and finally “Goodbye!”. This is useful for adding functionality without modifying existing functions directly.
23. What are context managers in Python, and how can you create one?
Context managers in Python handle resource management, like opening and closing files automatically. The with statement is used to ensure that resources are released properly after use. You can create a context manager using a class with __enter__() and __exit__() methods or by using the contextlib module’s contextmanager decorator.
Example:
class FileManager:
def __init__(self, filename, mode):
self.file = open(filename, mode)
def __enter__(self):
return self.file
def __exit__(self, exc_type, exc_value, traceback):
self.file.close()
with FileManager("example.txt", "w") as file:
file.write("Hello, Context Manager!")
💡 Explanation: The FileManager class opens a file when __enter__() is called and returns the file object. The __exit__() method ensures the file closes automatically, even if an error occurs. The with statement simplifies resource management, reducing errors like forgetting to close files.
24. What are metaclasses, and how do they differ from regular classes?
A metaclass in Python is a class that defines how other classes behave. While regular classes create objects, metaclasses create and modify classes themselves.
Example:
class Meta(type):
def __new__(cls, name, bases, dct):
print(f"Creating class: {name}")
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=Meta):
pass
💡 Explanation: Meta is a metaclass because it inherits from type. When MyClass is defined, Meta controls its creation. The __new__ method prints a message, showing how the metaclass customizes class creation.
25. How would you implement an LRU cache in Python?
An LRU (Least Recently Used) cache removes the least recently used items when it reaches its maximum size. In Python, we can use the functools.lru_cache decorator or implement it manually using OrderedDict from collections.
Example:
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key: int):
if key not in self.cache:
return -1
self.cache.move_to_end(key) # Mark as recently used
return self.cache[key]
def put(self, key: int, value: int):
if key in self.cache:
self.cache.move_to_end(key)
elif len(self.cache) >= self.capacity:
self.cache.popitem(last=False) # Remove LRU item
self.cache[key] = value
# Usage
lru = LRUCache(2)
lru.put(1, "A")
lru.put(2, "B")
print(lru.get(1)) # Output: "A"
lru.put(3, "C") # Removes key 2 (least recently used)
print(lru.get(2)) # Output: -1
💡 Explanation: An OrderedDict maintains the order of inserted items. When accessing a key using get(), we move it to the end to mark it as recently used. If the cache exceeds its capacity, the popitem(last=False) method removes the least recently used item. This ensures our cache stays within the limit while keeping the most recent items accessible.
26. How can you optimize Python code for performance?
You can optimize Python code by using efficient data structures like dictionaries and sets, avoiding unnecessary loops, and using built-in functions. Anyone can use list comprehensions instead of loops, and prefer generators for large data processing.
The multiprocessing and threading modules help with parallel execution. Profiling tools like cProfile can identify slow parts of your code. You can use external libraries like NumPy to speed up numerical computations.
Example:
# Using list comprehension instead of a loop
squares = [x**2 for x in range(10)]
print(squares)
💡 Explanation: In this example, we generate squares of numbers from 0 to 9 in a single line, making it both readable and efficient.
27. How do you implement multithreading and multiprocessing in Python?
Python provides two modules for handling multithreading and multiprocessing:
- threading module: It is used for multithreading, where multiple threads share the same memory and run within a single process. “threading” module is best for I/O-bound tasks like downloading files, reading from a database, or handling user inputs.
- multiprocessing module: It is used for multiprocessing, where separate processes run independently with their own memory. “multiprocessing” module is best for CPU-intensive tasks like data processing, image processing, and computations.
Example :
import threading
def print_numbers():
for i in range(5):
print(f"Number: {i}")
# Creating a thread
thread = threading.Thread(target=print_numbers)
# Starting the thread
thread.start()
# Waiting for the thread to finish
thread.join()
print("Thread execution completed")
💡 Explanation: The threading.Thread(target=print_numbers) creates a new thread to execute the print_numbers function. The start() begins the thread execution, and join() ensures the main program waits until the thread finishes.
28. Explain how you would design a Python-based web scraper.
To design a Python-based web scraper, first, you can choose a library like BeautifulSoup or Scrapy to extract data from websites. Then, use requests to fetch HTML pages and BeautifulSoup to parse them. You can identify the structure of the target webpage using browser developer tools (Inspect Element).
Then, extract data by finding HTML elements (like <div>, <span>, or <table>) and finally store the results in a file (CSV, JSON) or database. You can handle errors like timeouts and blockages using headers, proxies, or delays.
Example:
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
headers = {"User-Agent": "Mozilla/5.0"} # Prevent blocking
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")
titles = soup.find_all("h2")
for title in titles:
print(title.text)
💡 Explanation: This script sends a request to example.com and gets its HTML content. It uses BeautifulSoup to parse the page and extract all <h2> elements (titles). The headers prevent detection as a bot. This is useful for gathering information like product prices, news headlines, or job listings.
Bonus Python Interview Questions
29. What are the differences between Python 2 and Python 3?
Python 2 and Python 3 have several key differences. Python 2 is outdated and no longer maintained, while Python 3 is the present and future of Python.
One major difference is how they handle print. In Python 2, print is a statement (print “Hello”), whereas in Python 3, it is a function (print(“Hello”)).
Another key difference is integer division. In Python 2, dividing integers (5/2) gives 2, while in Python 3, it gives 2.5 due to floating-point division. Python 3 also improves Unicode handling, making string management easier.
30. How do you merge two dictionaries in Python?
In Python, you can merge two dictionaries using the update() method or the | operator (introduced in Python 3.9). These methods combine key-value pairs from both dictionaries. If there are duplicate keys, the values in the second dictionary overwrite those in the first dictionary.
Example:
# Using update() method
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
dict1.update(dict2)
print(dict1) # {'a': 1, 'b': 3, 'c': 4}
# Using | operator (Python 3.9+)
merged_dict = dict1 | dict2
print(merged_dict) # {'a': 1, 'b': 3, 'c': 4}
💡 Explanation: The update() method modifies the original dictionary by adding or updating key-value pairs. The | operator creates a new dictionary without changing the original ones. The second dictionary’s values overwrite duplicate keys from the first dictionary. These methods help when combining data from multiple sources.
30. Explain the difference between deep copy and shallow copy in Python.
A shallow copy creates a new object, but it only copies references to the original elements. A deep copy, however, creates a completely independent copy of the original object, including nested structures. This means that modifying a deep copy does not affect the original object, but modifying a shallow copy might.
Example:
import copy
# Original list with a nested list
original = [[1, 2], [3, 4]]
# Shallow copy
shallow = copy.copy(original)
# Deep copy
deep = copy.deepcopy(original)
# Modify the nested list in the original
original[0][0] = 99
print(shallow) # [[99, 2], [3, 4]] (affected)
print(deep) # [[1, 2], [3, 4]] (not affected)
💡 Explanation: In a shallow copy, the outer list is copied, but the inner lists remain references to the original. When we change the original[0][0], it also changes in the shallow copy. A deep copy, however, creates a completely separate copy of all elements, so changes in the original do not affect it. Use copy.deepcopy() when you need to ensure changes do not propagate back to the original object.
31. What is the difference between staticmethod and classmethod in Python?
A staticmethod is a method that does not depend on the class instance. It does not use self or cls. A classmethod, on the other hand, takes cls as the first argument and can modify class-level variables.
Example:
class Example:
class_variable = "I am a class variable"
@staticmethod
def static_method():
return "I do not use class variables."
@classmethod
def class_method(cls):
return f"I can access: {cls.class_variable}"
# Calling methods
print(Example.static_method()) # No class interaction
print(Example.class_method()) # Accesses class variables
💡 Explanation: A staticmethod behaves like a regular function inside a class. It does not need any reference to the class itself. A classmethod, however, has access to class variables and can modify them. This is useful when you need a method that operates at the class level rather than the instance level.
32. How does Python’s memory management work?
Python manages memory using an automatic memory management system. It has a built-in garbage collector that removes unused objects to free memory. Python uses reference counting and garbage collection to manage memory efficiently.
Example:
import sys
a = [1, 2, 3]
b = a # Reference count increases
print(sys.getrefcount(a)) # Output: 3 (1 for b, 1 for print, 1 internal reference)
del b # Reference count decreases
print(sys.getrefcount(a)) # Output: 2
💡 Explanation: Python tracks how many references an object has. When an object’s reference count drops to zero, Python automatically deletes it. The garbage collector helps clean up cyclic references (objects referencing each other). This system allows Python to handle memory efficiently without requiring manual deallocation.
33. What is the difference between sort() and sorted() in Python?
The sort() method sorts a list in place, meaning it modifies the original list and does not return a new one. The sorted() function, on the other hand, returns a new sorted list without changing the original list.
Example:
numbers = [5, 2, 9, 1]
numbers.sort() # Sorts in place
print(numbers) # Output: [1, 2, 5, 9]
numbers = [5, 2, 9, 1]
sorted_numbers = sorted(numbers) # Returns a new sorted list
print(sorted_numbers) # Output: [1, 2, 5, 9]
print(numbers) # Original list remains unchanged: [5, 2, 9, 1]
💡 Explanation: The sort() is useful when you want to change the list directly. It does not return anything (None). sorted() is useful when you need to keep the original list intact while working with a sorted copy. Both methods support sorting with the key argument, allowing custom sorting logic.
34. How does Python’s map(), filter(), and reduce() functions work?
Python provides map(), filter(), and reduce() to process data efficiently.
- map(func, iterable) applies a function to each item in an iterable and returns a new iterable.
- filter(func, iterable) keeps items where func(item) is True.
- reduce(func, iterable) (from functools) repeatedly applies func() to reduce values to a single result.
Example :
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# Using map to double each number
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # Output: [2, 4, 6, 8, 10]
# Using filter to get even numbers
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # Output: [2, 4]
# Using reduce to find the product of all numbers
product = reduce(lambda x, y: x * y, numbers)
print(product) # Output: 120
💡 Explanation: The map() is great for transforming data without using loops and filter() helps extract only relevant elements.
The reduce() processes all elements to compute a single result. It is not a built-in function; you must import it from functools.
35. Explain the use of Python’s any() and all() functions with examples.
Python provides the any() and all() functions to check conditions in an iterable like a list or tuple.
- any(iterable) returns True if at least one element is True. If all elements are False, it returns False.
- all(iterable) returns True only if all elements are True. If any element is False, it returns False.
These functions help in filtering and validating data quickly.
Example:
numbers = [0, 1, 2, 3]
print(any(numbers)) # True, because 1, 2, and 3 are True
print(all(numbers)) # False, because 0 is False
values = [True, True, True]
print(all(values)) # True, all values are True
💡 Explanation: In the first example, any(numbers) returns True because at least one non-zero value exists. However, “all(numbers)” returns False because 0 is considered False.
In the second example, “all(values)” is True since all elements are True. These functions are useful for checking conditions efficiently in Python programs.
36. How can you implement a singleton class in Python?
A singleton class ensures that only one instance of the class exists throughout the program. You can implement it using the __new__ method or a class variable.
Example:
class Singleton:
_instance = None # Class-level variable to store the instance
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# Test Singleton
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2) # Output: True
💡 Explanation: In the example, _instance is a class variable that stores the single instance of the class. When we create an object, __new__ checks if an instance exists. If not, it creates one; otherwise, it returns the existing one. This ensures that all objects refer to the same instance.
37. How do you reverse a linked list in Python?
You can reverse a linked list in Python using an iterative or recursive approach. The most common method is using an iterative approach by modifying the next pointers of the nodes.
Example:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
temp = self.head
while temp.next:
temp = temp.next
temp.next = new_node
def reverse(self):
prev = None
current = self.head
while current:
next_node = current.next
current.next = prev
prev = current
current = next_node
self.head = prev
def print_list(self):
temp = self.head
while temp:
print(temp.data, end=" -> ")
temp = temp.next
print("None")
# Example Usage
ll = LinkedList()
ll.append(1)
ll.append(2)
ll.append(3)
ll.append(4)
print("Original List:")
ll.print_list()
ll.reverse()
print("Reversed List:")
ll.print_list()
💡 Explanation: In this example, we first create a linked list using the Node and LinkedList classes. The reverse() function works by iterating through the list and reversing the next pointers of each node. We use three pointers: prev (to store the previous node), current (to store the current node), and next_node (to store the next node before changing links).
By updating current.next to prev, we effectively reverse the list. The function continues until all links are reversed, and we update the head to the last node.

Sairam Uppugundla is the CEO and founder of Codegnan IT Solutions. With a strong background in Computer Science and over 10 years of experience, he is committed to bridging the gap between academia and industry.
Sairam Uppugundla’s expertise spans Python, Software Development, Data Analysis, AWS, Big Data, Machine Learning, Natural Language Processing (NLP) and more.
He previously worked as a Board Of Studies Member at PB Siddhartha College of Arts and Science. With expertise in data science, he was involved in designing the Curriculum for the BSc data Science Branch. Also, he worked as a Data Science consultant for Andhra Pradesh State Skill Development Corporation (APSSDC).