OOP: Encapsulation and Abstraction
Encapsulation and abstraction are two foundational concepts in Object-Oriented Programming (OOP) that help make code more modular, maintainable, and secure. In this module, we will explore these concepts in detail, focusing on how encapsulation restricts access to certain parts of an object, and how abstraction simplifies complex systems by hiding implementation details.
Subtopic 1: Understanding Encapsulation
Encapsulation refers to the practice of restricting direct access to some of an object's attributes and methods, and instead exposing only necessary functionalities through public methods. This allows for better data protection and separation of concerns.
In Python, encapsulation is typically achieved using private and protected attributes/methods, though Python does not enforce strict encapsulation. It's more about convention and using underscores to signal restricted access.
Example of Encapsulation:
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.__balance = balance # Private attribute
def deposit(self, amount):
if amount > 0:
self.__balance += amount
else:
print("Deposit amount must be positive.")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Insufficient funds or invalid amount.")
def get_balance(self):
return self.__balance
# Creating an instance
account = BankAccount("John", 1000)
# Accessing public methods to interact with the private attribute
account.deposit(500)
print(account.get_balance()) # Output: 1500
account.__balance = 2000 # This won't work, as __balance is private
print(account.get_balance()) # Output: 1500 (still the correct value)
In this example:
__balanceis a private attribute, and cannot be accessed directly from outside the class. Access is controlled via thedeposit,withdraw, andget_balancemethods.
Subtopic 2: Private and Protected Members
- Private Members: These are variables and methods that are only accessible within the class. They are denoted with a double underscore prefix (
__). - Protected Members: These are variables and methods that are intended to be used by subclasses. They are denoted with a single underscore prefix (
_).
While Python does not strictly enforce the private/protected rule, using these prefixes is a convention.
Example of Private and Protected Members:
class Car:
def __init__(self, make, model):
self._make = make # Protected member
self.__model = model # Private member
def get_model(self):
return self.__model
class SportsCar(Car):
def __init__(self, make, model, speed):
super().__init__(make, model)
self.__speed = speed
def get_speed(self):
return self.__speed
# Creating an object
car = SportsCar("Ferrari", "488", 210)
# Accessing protected member from subclass
print(car._make) # Output: Ferrari (accessible, but should be treated as internal)
# Accessing private member from subclass will fail
# print(car.__model) # This will raise an AttributeError
In this example:
- The
_makeattribute is protected, meaning it can be accessed and modified by subclasses. - The
__modelattribute is private, so it cannot be accessed directly by any class or subclass outsideCar.
Subtopic 3: Property Decorators
In Python, the @property decorator allows you to define methods that can be accessed like attributes. This is useful when you want to encapsulate a computation or access logic while keeping a clean, readable interface.
Property decorators allow you to define a method as a "getter," and if needed, a "setter" for the attribute. This helps in controlling access to attributes (read-only or read-write).
Example of Property Decorators:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value > 0:
self._radius = value
else:
print("Radius must be positive.")
# Creating an object
circle = Circle(5)
# Accessing the radius using the property method
print(circle.radius) # Output: 5
# Setting a new radius
circle.radius = 10
print(circle.radius) # Output: 10
# Trying to set a negative radius (will trigger the setter's validation)
circle.radius = -5 # Output: Radius must be positive.
In this example:
- The
radiusproperty allows controlled access to the_radiusattribute. If an invalid value is provided, the setter method prevents it.
Subtopic 4: Abstract Classes and Methods
Abstraction is the concept of hiding implementation details and exposing only the essential features of an object. This can be achieved using abstract classes and abstract methods in Python. An abstract class cannot be instantiated directly, and it may contain one or more abstract methods that must be implemented by its subclasses.
To create an abstract class in Python, you use the abc (Abstract Base Class) module.
Example of Abstract Classes and Methods:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Creating objects
dog = Dog()
cat = Cat()
print(dog.speak()) # Output: Woof!
print(cat.speak()) # Output: Meow!
In this example:
Animalis an abstract class with an abstract methodspeak(), which must be implemented by any non-abstract subclass.DogandCatimplement thespeak()method, making them concrete classes.
Tasks
-
Task 1: Implement Encapsulation in a Bank Account
- Create a
BankAccountclass with private attributes forbalance. Implement methods to deposit, withdraw, and check balance. Prevent direct modification of thebalanceattribute from outside the class.
- Create a
-
Task 2: Create a Protected Member
- Create a
Vehicleclass with a protected attributespeed. Create aCarsubclass that inherits fromVehicleand displays the speed using the protected member.
- Create a
-
Task 3: Implement Property Decorators
- Create a
Personclass with a_nameattribute. Use the@propertydecorator to define anamemethod that returns the name in uppercase. Use a setter to ensure that the name is always in uppercase.
- Create a
-
Task 4: Abstract Shape Class
- Define an abstract class
Shapewith an abstract methodarea(). Create two subclasses,RectangleandCircle, that implement thearea()method. Instantiate both and calculate their area.
- Define an abstract class
-
Task 5: Private Members and Accessor Methods
- Create a
Studentclass with a private attribute__grade. Implement a method to set the grade (only if it is between 0 and 100) and another method to get the grade.
- Create a
-
Task 6: Animal Hierarchy with Abstraction
- Create an abstract
Animalclass with an abstractmake_sound()method. Create two subclasses,DogandBird, that override themake_sound()method to return the respective sounds of these animals.
- Create an abstract