Unit 3: Data Structures, Classes, and Inheritance - Subjective Questions
CSR101 — Python Programming • Practice Questions with Detailed Answers
20 questions
Explain the creation, indexing, slicing, and manipulation of lists in Python. Illustrate your answer with suitable examples.
Lists are ordered and mutable collections that can store elements of different data types. They are created using square brackets.
- Creation:
numbers = [10, 20, 30, 40] - Indexing:
numbers[0]returns10, whilenumbers[-1]returns40. - Slicing:
numbers[1:3]returns[20, 30]. - Adding elements:
append()adds one item, whileextend()adds multiple items. - Inserting elements:
insert(index, value)places an item at a specified position. - Removing elements:
remove(value),pop(index), anddelcan be used. - Other useful methods include
sort(),reverse(),count(), andindex().
Example: values = [3, 1, 2]; values.append(4) produces [3, 1, 2, 4], and values.sort() produces [1, 2, 3, 4]. Since lists are mutable, their elements can be changed after creation.
What is list comprehension? Explain its syntax and compare it with a conventional for loop using examples involving filtering and transformation.
A list comprehension is a concise way to create a list from an iterable. Its general syntax is:
[expression for item in iterable if condition]
Example using a conventional loop:
squares = []
for n in range(1, 6):
squares.append(n * n)
Equivalent list comprehension:
squares = [n * n for n in range(1, 6)]
Filtering example:
even_squares = [n * n for n in range(1, 11) if n % 2 == 0]
This produces [4, 16, 36, 64, 100]. List comprehensions are generally more compact and readable for simple operations. However, a conventional loop is preferable when the logic contains multiple steps, complex conditions, exception handling, or side effects.
Distinguish between lists and tuples in Python. Discuss their syntax, mutability, performance, and appropriate use cases.
Lists and tuples are both ordered sequences, but they differ in important ways:
- Syntax: Lists use square brackets, such as
[1, 2, 3]; tuples usually use parentheses, such as(1, 2, 3). - Mutability: Lists are mutable, so elements can be added, removed, or changed. Tuples are immutable after creation.
- Methods: Lists provide methods such as
append(),remove(), andsort(). Tuples mainly providecount()andindex(). - Performance: Tuples generally require less memory and may be slightly faster for iteration because they are immutable.
- Hashability: A tuple containing only hashable values can be used as a dictionary key or set element, whereas a list cannot.
- Use cases: Use lists for dynamic collections and tuples for fixed records or constant groups of values.
Example: point = (10, 20) is suitable for a fixed coordinate, while shopping_cart = ["pen", "book"] is suitable for a collection that changes.
Explain dictionaries and sets in Python, including their creation, manipulation, uniqueness rules, and mutability.
Dictionaries store data as key-value pairs, while sets store unordered collections of unique elements.
Dictionary example: student = {"name": "Ravi", "age": 20}.
- Values are accessed using keys, such as
student["name"]. - A new item can be added with
student["grade"] = "A". - Existing values can be updated by assigning to their keys.
- Items can be removed using
pop(),popitem(), ordel. - Dictionary keys must be unique and hashable.
Set example: colors = {"red", "blue", "red"} produces {"red", "blue"} because sets discard duplicates.
- Sets support
add(),update(),remove(), anddiscard(). - Mathematical operations include union, intersection, difference, and symmetric difference.
- Dictionaries and sets are mutable, but their keys and elements must generally be immutable and hashable. A
frozensetis an immutable version of a set.
Explain the concepts of mutability, aliasing, shallow copying, and deep copying in Python. Use examples to show how changes affect nested data structures.
An object is mutable if it can be changed after creation, as with lists, dictionaries, and sets. An object is immutable if it cannot be changed, as with integers, strings, and tuples containing immutable elements.
Aliasing occurs when two variables refer to the same object:
a = [1, 2]
b = a
b.append(3) also changes a, because both names reference the same list.
A shallow copy creates a new outer object but keeps references to nested objects:
b = a.copy() or b = copy.copy(a).
A deep copy recursively copies nested objects:
b = copy.deepcopy(a).
For a nested list, changing an inner list in a shallow copy may affect the original, while a deep copy prevents this shared-reference problem. Shallow copying is efficient when nested objects are not modified; deep copying is safer when independent nested structures are required.
Describe the working of map(), filter(), and reduce() in Python. Provide examples and explain how they support functional programming.
The functions map(), filter(), and reduce() process iterable data using functions.
map()applies a function to every item. Example:list(map(lambda x: x * 2, [1, 2, 3]))returns[2, 4, 6].filter()selects items for which a condition is true. Example:list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4]))returns[2, 4].reduce()repeatedly combines elements to produce one result. It is imported fromfunctools:
reduce(lambda x, y: x + y, [1, 2, 3, 4])returns10.
These functions can be combined with lambda expressions and iterables to express data transformations declaratively. In Python 3, map() and filter() return iterators, so list() is often used to view all results. They can improve conciseness, although list comprehensions are often more readable for simple transformations.
What is a Cartesian product? Explain how it can be generated in Python using nested loops and itertools.product().
The Cartesian product of two collections contains every possible ordered pair formed by taking one element from the first collection and one from the second. If the collections have sizes and , the product contains pairs.
Using nested loops:
A = [1, 2]
B = ["x", "y"]
result = [(a, b) for a in A for b in B]
The result is [(1, "x"), (1, "y"), (2, "x"), (2, "y")].
Using itertools.product():
from itertools import product
result = list(product(A, B))
The optional repeat argument creates products of a collection with itself. For example, product([0, 1], repeat=3) produces all binary sequences of length , giving combinations.
Discuss the purpose of the itertools module. Explain the use of at least four important functions with suitable examples.
The itertools module provides memory-efficient iterator-building tools for combinatorial and sequential processing.
count(start, step)generates an infinite arithmetic sequence. Example:count(10, 2)generates .cycle(iterable)repeatedly produces elements from an iterable.repeat(value, times)repeats a value a specified number of times.chain(a, b)treats multiple iterables as one sequence.product(A, B)generates Cartesian products.permutations(items, r)generates ordered arrangements.combinations(items, r)generates unordered selections.groupby(iterable, key)groups consecutive elements having the same key.
For example, list(combinations([1, 2, 3], 2)) returns [(1, 2), (1, 3), (2, 3)]. Most itertools functions return iterators, which helps process large data sets without storing every result in memory.
Explain the functions enumerate() and zip(). Show how they can be used to process multiple sequences efficiently.
enumerate() adds an index to each item of an iterable. It is useful when both the position and value are needed.
Example:
names = ["Asha", "Bala", "Chitra"]
for index, name in enumerate(names, start=1):
print(index, name)
This prints each name with a one-based index.
zip() combines corresponding elements from two or more iterables into tuples:
names = ["Asha", "Bala"]
marks = [85, 90]
for name, mark in zip(names, marks):
print(name, mark)
The result pairs ("Asha", 85) and ("Bala", 90). By default, zip() stops when the shortest iterable ends. These functions make loops clearer than manually managing indexes or accessing several sequences independently.
Explain eval() and exec() in Python. Compare their purposes, demonstrate their syntax, and discuss the security risks involved.
eval() evaluates a single Python expression and returns its result:
result = eval("3 * (4 + 2)")
The value of result is .
exec() executes a string containing one or more Python statements:
code = "x = 10\\ny = 20\\nprint(x + y)"
exec(code)
Differences include:
eval()accepts only an expression and produces a value.exec()can execute statements such as assignments, loops, function definitions, and imports, but it does not normally return a useful value.
Both functions can execute arbitrary code. If their input comes from an untrusted user, an attacker may read files, alter data, or execute system commands. Therefore, they should generally be avoided with external input. Safer alternatives include ast.literal_eval() for evaluating safe literal structures and explicit parsing or validation.
Define a class, object, attribute, method, and instance in Python. Explain how these concepts are related using a practical example.
A class is a blueprint that defines data and behavior. An object is a concrete entity created from a class. An instance is another term for an object belonging to a particular class.
An attribute is data associated with a class or object. A method is a function defined inside a class that describes behavior.
Example:
class Student:
school = "ABC College"
def __init__(self, name, marks):
self.name = name
self.marks = marks
def display(self):
return f"{self.name}: {self.marks}"
Here, Student is the class, name and marks are instance attributes, school is a class attribute, and display() is a method. s1 = Student("Meera", 90) creates an instance. The keyword self refers to the current instance and allows its attributes and methods to be accessed.
What is a constructor in Python? Explain the role of __init__(), instance attributes, default values, and object initialization.
A constructor is a special method used to initialize an object when it is created. In Python, __init__() is commonly called the constructor, although object allocation is technically performed by __new__().
Example:
class Rectangle:
def __init__(self, length=1, width=1):
self.length = length
self.width = width
When r = Rectangle(5, 3) is executed, Python creates an object and automatically calls __init__() with length=5 and width=3. The values become instance attributes accessible through r.length and r.width.
Constructors are useful for:
- Assigning initial values.
- Validating input.
- Establishing object invariants.
- Creating or preparing related resources.
Default arguments allow objects to be created without supplying every value, as in Rectangle().
Differentiate between instance attributes, class attributes, instance methods, class methods, and static methods.
Python classes can contain different kinds of data and behavior:
- Instance attributes: Belong to a particular object and are usually created using
self, for exampleself.name. - Class attributes: Belong to the class and are shared by instances unless an instance overrides the value.
- Instance methods: Take
selfas the first parameter and operate on instance data. - Class methods: Use the
@classmethoddecorator and takeclsas the first parameter. They operate on class-level data and are often used as alternative constructors. - Static methods: Use the
@staticmethoddecorator and do not receiveselforclsautomatically. They behave like utility functions placed inside a class.
Example:
class Account:
bank = "National Bank"
def __init__(self, owner): self.owner = owner
@classmethod
def bank_name(cls): return cls.bank
@staticmethod
def valid_amount(x): return x > 0
This separation improves organization and expresses how data and behavior are related.
Explain inheritance in Python. Distinguish between a base class and a derived class, and describe the advantages of inheritance with an example.
Inheritance allows a new class to acquire attributes and methods from an existing class. The existing class is called the base class, parent class, or superclass. The new class is called the derived class, child class, or subclass.
Example:
class Vehicle:
def start(self):
return "Vehicle started"
class Car(Vehicle):
def drive(self):
return "Car is driving"
car = Car() can call both car.start() and car.drive().
Advantages include:
- Code reuse: Common behavior is written once.
- Extensibility: A derived class can add specialized features.
- Maintainability: Changes to shared behavior can be made in the base class.
- Polymorphism: Different subclasses can provide different implementations of the same method.
Inheritance should represent a meaningful "is-a" relationship, such as a car being a vehicle.
What is method overriding? Explain how super() is used when redefining inherited methods.
Method overriding occurs when a derived class defines a method with the same name as a method in its base class. When the method is called on a derived object, the derived implementation is selected.
Example:
class Animal:
def sound(self):
return "Some sound"
class Dog(Animal):
def sound(self):
return "Bark"
Dog().sound() returns "Bark".
The super() function calls a method from the parent class. It is useful when the child wants to extend rather than completely replace inherited behavior:
class Dog(Animal):
def sound(self):
parent_sound = super().sound()
return parent_sound + " and Bark"
Overriding supports runtime polymorphism, allowing objects of different subclasses to respond differently to the same method call.
Explain multiple inheritance in Python. Discuss method resolution order and demonstrate how super() works in a multiple-inheritance hierarchy.
Multiple inheritance allows a class to inherit from more than one base class.
Example:
class A:
def show(self): return "A"
class B(A):
def show(self): return "B"
class C(A):
def show(self): return "C"
class D(B, C):
pass
For D().show(), Python follows the method resolution order (MRO) to determine which implementation is selected. The MRO can be viewed using D.mro() or D.__mro__. In this example, B is searched before C.
Python uses a consistent linearization algorithm called C3 linearization. Cooperative multiple inheritance uses super() so that each class can pass control to the next class in the MRO rather than directly naming a parent. This is especially useful in mixin designs, but class hierarchies should be kept clear to avoid ambiguity and maintenance problems.
Design a practical object-oriented program for managing student records. Explain the classes, attributes, constructors, methods, and inheritance used in your design.
A student-record system can be designed using a base Person class and a derived Student class.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, roll_no):
super().__init__(name, age)
self.roll_no = roll_no
self.marks = {}
def add_mark(self, subject, mark):
self.marks[subject] = mark
def average(self):
return sum(self.marks.values()) / len(self.marks) if self.marks else 0
def display(self):
return f"{self.roll_no}: {self.name}, Average={self.average()}"
Person stores common attributes, while Student adds a roll number and marks dictionary. The constructor initializes each object, and methods provide meaningful operations. The example demonstrates encapsulation, reuse through inheritance, and object-oriented modeling of a real-world problem.
What are decorators in Python? Explain their purpose, syntax, and working by developing a decorator that measures or displays function execution.
A decorator is a callable that takes a function or class and returns a modified or extended version without changing the original source code. Decorators are applied using the @decorator_name syntax.
Example:
from functools import wraps
def announce(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Function started")
result = func(*args, **kwargs)
print("Function completed")
return result
return wrapper
@announce
def add(a, b):
return a + b
Calling add(2, 3) invokes wrapper, which performs additional actions before and after the original function. *args and **kwargs allow the decorator to work with different argument lists. @wraps preserves metadata such as the original function name and documentation. Decorators are widely used for logging, timing, authentication, caching, and validation.
Explain generators in Python. Compare generators with ordinary functions and lists, and illustrate the use of yield with an example.
A generator is a special kind of iterator that produces values lazily. It is created by a function containing the yield statement.
Example:
def countdown(n):
while n > 0:
yield n
n -= 1
for value in countdown(3):
print(value)
The output is . Each call to yield suspends the function while preserving its local state. The next call resumes execution from that point.
Compared with a list:
- A list stores all values in memory immediately.
- A generator produces one value at a time.
- Generators are suitable for large or infinite sequences.
- Generators can be iterated only in the forward direction and are usually not reusable after exhaustion.
A generator expression, such as (x * x for x in range(10)), provides a compact way to create a generator.
Develop and explain an object-oriented example using a base class, derived classes, method overriding, and a generator to process objects.
Consider a system that processes different types of employees:
class Employee:
def __init__(self, name):
self.name = name
def salary(self):
raise NotImplementedError
class FullTimeEmployee(Employee):
def __init__(self, name, monthly_salary):
super().__init__(name)
self.monthly_salary = monthly_salary
def salary(self):
return self.monthly_salary
class PartTimeEmployee(Employee):
def __init__(self, name, hours, rate):
super().__init__(name)
self.hours = hours
self.rate = rate
def salary(self):
return self.hours * self.rate
def salary_report(employees):
for employee in employees:
yield employee.name, employee.salary()
The Employee class defines a common interface. The derived classes override salary() with specialized calculations. The generator produces report entries lazily, which is efficient when processing many employees. This example demonstrates inheritance, polymorphism, overriding, constructors, attributes, methods, and generators together.
Explain the creation, indexing, slicing, and manipulation of lists in Python. Illustrate your answer with suitable examples.
Lists are ordered and mutable collections that can store elements of different data types. They are created using square brackets.
- Creation:
numbers = [10, 20, 30, 40] - Indexing:
numbers[0]returns10, whilenumbers[-1]returns40. - Slicing:
numbers[1:3]returns[20, 30]. - Adding elements:
append()adds one item, whileextend()adds multiple items. - Inserting elements:
insert(index, value)places an item at a specified position. - Removing elements:
remove(value),pop(index), anddelcan be used. - Other useful methods include
sort(),reverse(),count(), andindex().
Example: values = [3, 1, 2]; values.append(4) produces [3, 1, 2, 4], and values.sort() produces [1, 2, 3, 4]. Since lists are mutable, their elements can be changed after creation.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →