Chapter 2 — Data Abstraction: Exercise Solutions Guide
How to Use This Page
1. Attempt every exercise question on your own first — use your Samacheer Kalvi textbook and class notes. 2. Compare your answers with the solutions below only after you have written your own. 3. Read the explanations for MCQs and True/False items — understanding why an answer is correct is more important than the answer itself. 4. Practise the short and detailed answers by writing them in your own words; do not memorise verbatim. 5. Use the Exam Checklist at the end for a quick pre-exam revision round.
Key Concepts (Quick Recap)
- Data abstraction is the principle of hiding implementation details and exposing only the essential operations (interface).
- An Abstract Data Type (ADT) defines a data type by its behaviour (operations) without specifying how data is stored or operations are implemented.
- Constructors are functions that create/build an instance of an ADT (e.g.,
make_pair(x, y)). - Selectors are functions that retrieve individual components from an ADT (e.g.,
first(pair), second(pair)). - Encapsulation bundles data and methods into a single unit (class) and restricts direct access to internal data.
- The interface of an ADT specifies what operations are available; the implementation specifies how they work.
- Python classes provide encapsulation through attributes and methods; private attributes use
_ or __ prefix. - Data hiding ensures the internal representation is not directly accessible; users interact only through methods.
- Abstraction allows changing the implementation without affecting code that uses the ADT.
- A tuple can serve as a simple ADT — its internal structure is hidden if accessed only through constructors and selectors.
- Python's
@property decorator provides a way to control access to instance attributes (getter/setter abstraction). - The key benefit: separation of concerns — the user of a data type does not need to know how it is implemented internally.
I. Choose the Best Answer (MCQs)
1. Which of the following best describes data abstraction? (a) Showing all implementation details to the user (b) Hiding implementation details and exposing only essential operations (c) Writing efficient code using loops (d) Storing data in a database
Answer(b) Hiding implementation details and exposing only essential operations Data abstraction focuses on presenting only the necessary interface to the user while hiding the internal workings. This allows users to interact with data without knowing how it is stored or processed internally.
2. In an Abstract Data Type (ADT), what does a constructor do? (a) Destroys an object (b) Creates or builds an instance of the data type (c) Selects a component from the data (d) Prints the data
Answer(b) Creates or builds an instance of the data type A constructor is a function that creates an instance of an ADT. For example, make_point(x, y) creates a point. The user does not need to know how the point is stored internally.
3. Which of the following is a selector for a pair ADT? (a) make_pair(x, y) (b) first(pair) (c) pair = [3, 4] (d) del pair
Answer(b) first(pair) Selectors extract components from an ADT. first(pair) retrieves the first element. make_pair is a constructor, [3, 4] is direct list creation, and del deletes the variable.
4. What does encapsulation achieve in object-oriented programming? (a) Faster execution of code (b) Bundling data and methods together and restricting direct access (c) Removing all methods from a class (d) Making all attributes public
Answer(b) Bundling data and methods together and restricting direct access Encapsulation combines data (attributes) and methods (behaviours) into a single unit (class) and restricts direct access to the internal data, typically using private attributes and public methods.
5. In Python, how do you indicate that an attribute should be treated as private? (a) Use the private keyword (b) Prefix the attribute name with double underscore __ (c) Prefix with @ (d) Use the sealed keyword
Answer(b) Prefix the attribute name with double underscore __ Python uses name mangling for attributes starting with double underscore __. For example, self.__balance becomes _ClassName__balance, making it harder to access directly. Python does not have a private or sealed keyword.
6. Which of the following is an example of data abstraction in daily life? (a) Opening the bonnet of a car to see the engine (b) Using a TV remote without knowing its internal circuitry (c) Reading the source code of a library (d) Debugging a program
Answer(b) Using a TV remote without knowing its internal circuitry A TV remote abstracts the complexity of the TV's internal circuitry. You press buttons (interface) without needing to know how the infrared signals work (implementation). This is the essence of data abstraction.
7. What happens if you change the internal representation of an ADT but keep the interface the same? (a) All code using the ADT breaks (b) Code using the ADT through its interface continues to work (c) The ADT is deleted (d) The constructors stop working
Answer(b) Code using the ADT through its interface continues to work The whole point of data abstraction is that code that uses the ADT through its interface (constructors and selectors) is unaffected by changes to the internal representation. Only the implementation of constructors and selectors needs updating.
8. Which Python feature best supports data abstraction? (a) for loops (b) Classes with private attributes and public methods (c) The print() function (d) List indexing
Answer(b) Classes with private attributes and public methods Python classes allow bundling data (private attributes) with methods (public interface), which is the primary mechanism for data abstraction. Private attributes (__attr) hide internal data, and public methods provide the interface.
9. What is the output of the following code?
def make_pair(x, y):
return (x, y)
def first(p):
return p[0]
def second(p):
return p[1]
p = make_pair(10, 20)
print(first(p) + second(p))
Answer
(a) 30 (b) (10, 20) (c) 1020 (d) Error
Answer(a) 30 make_pair(10, 20) creates a tuple (10, 20). first(p) returns 10, second(p) returns 20. Their sum is 30.
10. Which of the following correctly defines a class with encapsulation in Python? (a) class Car: color = "red" (b) class Car: __color = "red"; def get_color(self): return self.__color (c) def Car(): __color = "red" (d) class Car: pass
Answer(b) Option (b) uses __color as a private attribute and provides a public method get_color() to access it. This is encapsulation — the data is hidden and accessed through a method.
II. Fill in the Blanks
1. The principle of hiding implementation details and exposing only essential operations is called data abstraction.
2. A function that creates an instance of an ADT is called a constructor.
3. A function that retrieves a component from an ADT is called a selector.
4. In Python, attributes prefixed with a double underscore are subject to name mangling, making them harder to access directly.
5. The bundling of data and methods into a single unit is called encapsulation.
6. An Abstract Data Type (ADT) defines a data type by its behaviour without specifying the implementation.
7. The @property decorator in Python is used to define getter methods that control access to private attributes.
8. Changing the internal representation of an ADT without affecting the interface is an example of data abstraction.
9. In a pair ADT, make_pair is the constructor and first/second are the selectors.
10. The separation of "what" (interface) from "how" (implementation) is the core idea of abstraction.
III. True or False
1. Data abstraction means showing all internal details of data to the user. False. Data abstraction means hiding implementation details and exposing only the essential operations (interface). The user interacts through the interface, not the internal representation.
2. Constructors and selectors together form the interface of an ADT. True. Constructors create ADT instances, and selectors access components. Together they define what operations are available without revealing how they work internally.
3. Python enforces strict access control on private attributes. False. Python does not enforce strict access control. Attributes with __ prefix are name-mangled (e.g., __balance becomes _ClassName__balance) but can still be accessed. It is a convention, not enforcement.
4. Encapsulation and data abstraction mean the same thing. False. Encapsulation is bundling data and methods together and restricting direct access. Data abstraction is hiding implementation details and exposing only the interface. They are related but distinct — encapsulation is a technique; abstraction is a principle.
5. If the internal representation of an ADT changes, all code using it must be rewritten. False. The key advantage of data abstraction is that if the interface (constructors and selectors) stays the same, code that uses the ADT does not need to change even if the internal representation changes.
6. A list can be used as the internal representation of a pair ADT. True. A pair ADT can use a list [x, y] internally. The user interacts only through make_pair(), first(), and second(), not knowing the internal representation.
7. In Python, self.__attribute can be directly accessed as obj.__attribute from outside the class. False. Due to name mangling, self.__attribute becomes self._ClassName__attribute. Accessing obj.__attribute from outside raises an AttributeError. It must be accessed as obj._ClassName__attribute.
8. The @property decorator allows controlled access to a private attribute. True. The @property decorator turns a method into a property, allowing controlled access. You can add validation logic in the getter or setter without changing how the attribute is accessed.
9. A selector function needs to know the internal representation of the ADT. True. The selector's implementation depends on how the data is stored internally. However, the user of the selector does not need to know this — only the selector's implementation does.
10. Abstraction reduces complexity by showing only relevant details. True. Abstraction reduces complexity by exposing only the necessary details and hiding the rest. This makes systems easier to understand, use, and maintain.
IV. Match the Following
Set A
| A | B |
|---|
| 1. Constructor | c. Creates an instance of an ADT |
| 2. Selector | e. Retrieves a component from an ADT |
| 3. Encapsulation | a. Bundling data and methods into one unit |
4. __attribute | d. Python convention for private attribute |
| 5. Interface | b. Set of operations available to the user |
Set B
| A | B |
|---|
| 1. Data abstraction | d. Hiding implementation details |
2. @property | e. Decorator for getter/setter methods |
| 3. Name mangling | c. Converts __x to _ClassName__x |
| 4. Tuple as pair | a. An implementation choice for an ADT |
| 5. ADT | b. Defined by behaviour, not implementation |
V. Short Answer Questions
1. What is data abstraction? Give a real-life example.
Answer
Data abstraction is the principle of hiding the implementation details of data and exposing only the essential operations. Users interact with data through a well-defined interface without knowing how it is stored or processed internally.
Real-life example — ATM machine:
- You insert a card, enter a PIN, and select an amount. The ATM dispenses cash.
- You do not know (or need to know) how the ATM connects to the bank's servers, verifies your account, or updates the balance.
- The ATM's buttons and screen are the interface; the banking network is the hidden implementation.
Programming example:
# User knows only make_pair(), first(), second()
def make_pair(x, y):
return (x, y) # Could change to [x, y] or {"a":x, "b":y}
def first(p):
return p[0]
def second(p):
return p[1]
p = make_pair(10, 20)
print(first(p)) # 10
print(second(p)) # 20
2. Explain constructors and selectors with an example.
Answer
Constructors create an instance of an ADT. Selectors retrieve individual components from that instance.
# Student ADT
# Constructor: creates a student record
def make_student(name, roll_no, marks):
return (name, roll_no, marks)
# Selectors: retrieve components
def get_name(student):
return student[0]
def get_roll_no(student):
return student[1]
def get_marks(student):
return student[2]
# Usage
s = make_student("Kavi", 101, 95)
print(get_name(s)) # Kavi
print(get_roll_no(s)) # 101
print(get_marks(s)) # 95
The user creates a student with make_student() and accesses data through selectors. The internal tuple representation is hidden.
3. Differentiate between data abstraction and encapsulation.
Answer
| Feature | Data Abstraction | Encapsulation |
|---|
| Definition | Hiding implementation details, showing only the interface | Bundling data and methods into a single unit and restricting access |
| Purpose | Reduce complexity, show only relevant details | Protect data from unauthorized access |
| Focus | What the data type does (behaviour) | How the data is bundled and protected |
| Mechanism | ADT, interface, constructors, selectors | Classes, private/public access modifiers |
| Example | Using len() without knowing how it counts | Using __balance with get_balance() method |
| Relationship | Abstraction is a design principle | Encapsulation is an implementation technique |
4. What is an Abstract Data Type (ADT)? Explain with an example.
Answer
An Abstract Data Type (ADT) is a mathematical model for a data type defined by its behaviour — the operations that can be performed on it — without specifying how those operations are implemented or how data is stored internally.
Example — Stack ADT:
- Operations (interface):
push(item), pop(), peek(), is_empty(), size() - Behaviour: Last-In, First-Out (LIFO)
- Implementation (hidden): Could use a list, linked list, or array — the user does not know or care.
# Stack ADT implementation using a list (hidden from user)
class Stack:
def __init__(self):
self.__items = [] # Private attribute (encapsulation)
def push(self, item): # Interface method
self.__items.append(item)
def pop(self): # Interface method
if not self.is_empty():
return self.__items.pop()
return None
def peek(self): # Interface method
if not self.is_empty():
return self.__items[-1]
return None
def is_empty(self): # Interface method
return len(self.__items) == 0
def size(self): # Interface method
return len(self.__items)
# User interacts only through the interface
s = Stack()
s.push(10)
s.push(20)
s.push(30)
print(s.pop()) # 30
print(s.peek()) # 20
print(s.size()) # 2
5. How does Python support encapsulation? Explain with an example.
Answer
Python supports encapsulation through classes, private attributes, and public methods.
- Private attributes use
__ prefix (name mangling makes direct access difficult). - Public methods provide controlled access to private data.
@property decorator allows getter/setter-style access.
class BankAccount:
def __init__(self, owner, balance):
self.__owner = owner # Private attribute
self.__balance = balance # Private attribute
# Getter methods (interface)
def get_owner(self):
return self.__owner
def get_balance(self):
return self.__balance
# Controlled modification
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return f"Deposited {amount}. Balance: {self.__balance}"
return "Invalid amount"
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return f"Withdrew {amount}. Balance: {self.__balance}"
return "Insufficient balance or invalid amount"
# Usage — only interface methods are accessible
acc = BankAccount("Kavi", 1000)
print(acc.get_balance()) # 1000
print(acc.deposit(500)) # Deposited 500. Balance: 1500
print(acc.withdraw(200)) # Withdrew 200. Balance: 1300
# acc.__balance = 0 ← Does NOT modify the actual attribute (name mangled)
6. What are the advantages of data abstraction?
Answer
1. Simplifies complexity: Users interact with a simple interface without understanding internal details. 2. Easier maintenance: Implementation can change without affecting code that uses the interface. 3. Reduces errors: Internal data is protected from accidental modification. 4. Improves modularity: Each module exposes only what others need. 5. Enhances security: Sensitive data is hidden behind access-controlled methods. 6. Promotes reusability: Well-defined ADTs can be reused across programs.
VI. Detailed Answer Questions
1. Explain data abstraction in detail with examples of constructors, selectors, and how the internal representation can change without affecting the user.
Answer
Data abstraction separates the what (interface) from the how (implementation) of a data type. The user interacts only through constructors and selectors; the internal storage is hidden.
Example — A Point ADT:
Version 1 — Using a tuple internally:
# Constructor
def make_point(x, y):
return (x, y) # Internal: tuple
# Selectors
def x_coord(point):
return point[0]
def y_coord(point):
return point[1]
# Other operations
def distance_from_origin(point):
return (x_coord(point)**2 + y_coord(point)**2) ** 0.5
Version 2 — Using a dictionary internally (implementation changed):
# Constructor
def make_point(x, y):
return {"x": x, "y": y} # Internal: dictionary
# Selectors
def x_coord(point):
return point["x"]
def y_coord(point):
return point["y"]
# distance_from_origin stays the same — uses only selectors
def distance_from_origin(point):
return (x_coord(point)**2 + y_coord(point)**2) ** 0.5
User code (works identically in both versions):
p = make_point(3, 4)
print(x_coord(p)) # 3
print(y_coord(p)) # 4
print(distance_from_origin(p)) # 5.0
Notice that the user code does not change even though the internal representation changed from a tuple to a dictionary. This is the power of data abstraction.
2. Explain encapsulation in Python using classes with private attributes, getters, setters, and the @property decorator.
Answer
Encapsulation in Python is achieved by bundling data and methods in a class and controlling access through private attributes and public methods.
Approach 1 — Using getter and setter methods:
class Student:
def __init__(self, name, marks):
self.__name = name # Private attribute
self.__marks = marks # Private attribute
# Getters
def get_name(self):
return self.__name
def get_marks(self):
return self.__marks
# Setters (with validation)
def set_name(self, name):
if len(name) > 0:
self.__name = name
else:
print("Name cannot be empty")
def set_marks(self, marks):
if 0 <= marks <= 100:
self.__marks = marks
else:
print("Marks must be between 0 and 100")
s = Student("Kavi", 85)
print(s.get_name()) # Kavi
s.set_marks(95) # Valid
s.set_marks(150) # Marks must be between 0 and 100
Approach 2 — Using @property decorator:
class Student:
def __init__(self, name, marks):
self.__name = name
self.marks = marks # Uses the setter via property
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
if len(name) > 0:
self.__name = name
else:
raise ValueError("Name cannot be empty")
@property
def marks(self):
return self.__marks
@marks.setter
def marks(self, marks):
if 0 <= marks <= 100:
self.__marks = marks
else:
raise ValueError("Marks must be between 0 and 100")
s = Student("Bala", 78)
print(s.name) # Bala (calls the @property getter)
s.name = "Arun" # (calls the @name.setter)
print(s.marks) # 78
# s.marks = 150 # Raises ValueError
Advantages of encapsulation:
- Data is protected from accidental modification.
- Validation logic is centralised in setters.
- Internal representation can change without affecting the user.
- Access control enforces the principle of least privilege.
3. Explain the difference between data abstraction and encapsulation with examples. How do they complement each other?
Answer
| Aspect | Data Abstraction | Encapsulation |
|---|
| Definition | Hiding implementation details; exposing only the interface | Bundling data and methods; restricting direct access |
| Goal | Show what a data type does | Protect how data is stored and modified |
| Mechanism | ADTs, interfaces, constructors, selectors | Classes, private attributes, access modifiers |
| Focus | Design-level concept | Implementation-level technique |
| Example | Using len(list) without knowing if it stores length or counts items | Using __balance with get_balance() in a BankAccount class |
How they complement each other:
Abstraction and encapsulation work together. Abstraction defines what operations are available; encapsulation ensures those operations are the only way to interact with data.
class Temperature:
"""Abstraction: user sees only Celsius access.
Encapsulation: internal storage is in Kelvin (hidden)."""
def __init__(self, celsius):
self.__kelvin = celsius + 273.15 # Private: stored in Kelvin
# Abstraction: interface in Celsius (user-friendly)
@property
def celsius(self):
return self.__kelvin - 273.15
@celsius.setter
def celsius(self, value):
if value >= -273.15:
self.__kelvin = value + 273.15
else:
raise ValueError("Below absolute zero!")
@property
def fahrenheit(self):
return self.celsius * 9/5 + 32
# User interacts only in Celsius or Fahrenheit
t = Temperature(25)
print(t.celsius) # 25.0
print(t.fahrenheit) # 77.0
t.celsius = 100
print(t.celsius) # 100.0
# t.__kelvin # AttributeError (encapsulated)
- Abstraction: The user sees Celsius and Fahrenheit (the interface) — they do not need to know data is stored in Kelvin.
- Encapsulation:
__kelvin is private; the user cannot modify it directly, ensuring the invariant (Kelvin >= 0) is maintained.
4. Implement a Student ADT with constructors, selectors, and operations. Show how data abstraction allows changing the internal representation.
Answer
Version 1 — Internal representation: tuple
# Constructor
def make_student(name, roll_no, marks):
return (name, roll_no, marks)
# Selectors
def get_name(student):
return student[0]
def get_roll_no(student):
return student[1]
def get_marks(student):
return student[2]
# Operations (use only selectors, never direct indexing)
def get_grade(student):
marks = get_marks(student)
if marks >= 90: return "A"
elif marks >= 80: return "B"
elif marks >= 70: return "C"
elif marks >= 60: return "D"
else: return "F"
def display_student(student):
print(f"Name: {get_name(student)}")
print(f"Roll No: {get_roll_no(student)}")
print(f"Marks: {get_marks(student)}")
print(f"Grade: {get_grade(student)}")
Version 2 — Internal representation: dictionary (implementation changed)
# Constructor (different internal structure)
def make_student(name, roll_no, marks):
return {"name": name, "roll": roll_no, "marks": marks}
# Selectors (updated for dictionary)
def get_name(student):
return student["name"]
def get_roll_no(student):
return student["roll"]
def get_marks(student):
return student["marks"]
# get_grade and display_student remain EXACTLY THE SAME
# They use selectors, not direct indexing
def get_grade(student):
marks = get_marks(student)
if marks >= 90: return "A"
elif marks >= 80: return "B"
elif marks >= 70: return "C"
elif marks >= 60: return "D"
else: return "F"
def display_student(student):
print(f"Name: {get_name(student)}")
print(f"Roll No: {get_roll_no(student)}")
print(f"Marks: {get_marks(student)}")
print(f"Grade: {get_grade(student)}")
User code (unchanged in both versions):
s = make_student("Kavi", 101, 92)
display_student(s)
# Name: Kavi
# Roll No: 101
# Marks: 92
# Grade: A
This demonstrates that changing the internal representation (tuple to dictionary) does not affect any code that uses the ADT through its interface (constructors and selectors).
5. What is an Abstract Data Type (ADT)? Explain Stack ADT and Queue ADT with their operations and Python implementations.
Answer
An Abstract Data Type (ADT) is a data type defined by its behaviour — the set of operations available — rather than by its internal implementation. Different implementations can be used as long as they provide the same interface.
Stack ADT — Last In, First Out (LIFO)
| Operation | Description |
|---|
push(item) | Add item to top |
pop() | Remove and return top item |
peek() | Return top item without removing |
is_empty() | Check if stack is empty |
size() | Return number of items |
class Stack:
def __init__(self):
self.__items = []
def push(self, item):
self.__items.append(item)
def pop(self):
if not self.is_empty():
return self.__items.pop()
return None
def peek(self):
if not self.is_empty():
return self.__items[-1]
return None
def is_empty(self):
return len(self.__items) == 0
def size(self):
return len(self.__items)
s = Stack()
s.push(10)
s.push(20)
s.push(30)
print(s.pop()) # 30
print(s.peek()) # 20
print(s.size()) # 2
Queue ADT — First In, First Out (FIFO)
| Operation | Description |
|---|
enqueue(item) | Add item to rear |
dequeue() | Remove and return front item |
front() | Return front item without removing |
is_empty() | Check if queue is empty |
size() | Return number of items |
class Queue:
def __init__(self):
self.__items = []
def enqueue(self, item):
self.__items.append(item)
def dequeue(self):
if not self.is_empty():
return self.__items.pop(0)
return None
def front(self):
if not self.is_empty():
return self.__items[0]
return None
def is_empty(self):
return len(self.__items) == 0
def size(self):
return len(self.__items)
q = Queue()
q.enqueue("A")
q.enqueue("B")
q.enqueue("C")
print(q.dequeue()) # A
print(q.front()) # B
print(q.size()) # 2
Key ADT characteristics:
- Operations are defined by behaviour, not implementation.
- The user does not know or care if the stack/queue uses a list, linked list, or array internally.
- Implementation can be swapped without affecting user code.
Common Mistakes
1. Confusing abstraction with encapsulation: Abstraction hides what is irrelevant (implementation details); encapsulation hides what is private (internal data). They are related but distinct concepts.
2. Accessing internal representation directly instead of using selectors: Always use first(pair) instead of pair[0]. Direct access breaks abstraction — if the internal representation changes, your code fails.
3. Forgetting to make attributes private: Using self.balance instead of self.__balance exposes internal data, violating encapsulation. Always use __ for attributes that should not be directly accessible.
4. Not updating selectors when internal representation changes: When you change how data is stored, you must update the selectors to match. The operations that use selectors, however, remain unchanged.
5. Thinking Python enforces true privacy: Python's __attribute name mangling (_ClassName__attribute) is a convention, not enforcement. It can still be accessed if someone really tries. True privacy requires discipline.
6. Writing selectors that are too specific: A selector should return meaningful data, not expose the internal structure. Avoid returning the entire internal data structure when only a component is needed.
7. Skipping validation in setters: Encapsulation benefits include validation. If your setter method does not validate, you lose one of the main advantages of encapsulation.
Exam Checklist
- [ ] Can define data abstraction and explain its purpose
- [ ] Can identify constructors and selectors in an ADT
- [ ] Can implement a simple ADT using functions (constructors and selectors)
- [ ] Can explain the difference between abstraction and encapsulation
- [ ] Can use Python classes with private attributes (
__) for encapsulation - [ ] Can implement getter and setter methods, including
@property - [ ] Can explain how changing internal representation does not affect user code
- [ ] Can implement Stack and Queue ADTs with their operations
- [ ] Can give real-life examples of data abstraction (ATM, car dashboard, TV remote)
- [ ] Know that Python name mangling converts
__attr to _ClassName__attr