In this module, we will explore two important concepts: encapsulation and abstraction. We will discuss access specifiers, encapsulation through getters and setters, and abstraction using abstract classes and interfaces.
Access specifiers define the visibility and accessibility of class members (attributes and methods) in Python. They help control the level of encapsulation and protect the internal state of an object.
In Python, we have three access specifiers:
- Public: Public members are accessible from anywhere, both inside and outside the class.
- Private: Private members are accessible only within the class. They are denoted by prefixing the attribute or method name with double underscores (
__
). - Protected: Protected members are accessible within the class and its subclasses. They are denoted by prefixing the attribute or method name with a single underscore (
_
).
Here's an example to illustrate access specifiers:
class Person:
def __init__(self, name):
self.name = name # Public attribute
self.__age = 25 # Private attribute
self._gender = "Female" # Protected attribute
def display(self):
print(f"Name: {self.name}")
print(f"Age: {self.__age}")
print(f"Gender: {self._gender}")
person = Person("Alice")
person.display()
print(person.name) # Output: Alice
print(person._gender) # Output: Female
print(person.__age) # Error: 'Person' object has no attribute '__age'
In this example, we define a Person
class with attributes name
, __age
, and _gender
. The name
attribute is public, the __age
attribute is private, and the _gender
attribute is protected.
Inside the class, we can access all the attributes. However, outside the class, we can only access the public attribute name
and the protected attribute _gender
. Attempting to access the private attribute __age
outside the class will result in an error.
Encapsulation is the practice of hiding internal implementation details of a class and providing controlled access to class members. Getters and setters are methods used to access and modify the values of private attributes, ensuring data integrity and encapsulation.
Here's an example to demonstrate encapsulation using getters and setters:
class BankAccount:
def __init__(self):
self.__balance = 0 # Private attribute
def get_balance(self):
return self.__balance
def deposit(self, amount):
self.__balance += amount
def withdraw(self, amount):
if amount <= self.__balance:
self.__balance -= amount
else:
print("Insufficient balance!")
account = BankAccount()
account.deposit(100)
print(account.get_balance()) # Output: 100
account.withdraw(50)
print(account.get_balance()) # Output: 50
In this example, we define a BankAccount
class with a private attribute __balance
. We provide a getter method get_balance
to retrieve the balance and two other methods, deposit
and withdraw
, to modify the balance.
Using getters and setters, we can control access to private attributes and ensure that the data is accessed and modified appropriately.
Abstraction is a concept that focuses on hiding complex implementation details and exposing only the essential functionalities to the user. Abstract classes and interfaces are key tools for achieving abstraction in Python.
An abstract class cannot be instantiated and serves as a blueprint for derived classes. It can contain both abstract methods (methods without implementation) and concrete methods.
Here's an example of an abstract class:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def calculate_area(self):
pass
class Rectangle(Shape):
def __init__(self, length, width):
self.length = length
self.width = width
def calculate_area(self):
return self.length * self.width
rectangle = Rectangle(5, 3)
print(rectangle.calculate_area()) # Output: 15
In this example, we define an abstract class Shape
using the ABC
module. The calculate_area
method is marked as abstract using the @abstractmethod
decorator. Any class inheriting from Shape
must provide an implementation for this method.
We then define a concrete class Rectangle
that inherits from Shape
and provides an implementation for the calculate_area
method.
Interfaces, on the other hand, are a collection of method signatures. In Python, interfaces are not explicitly defined like in some other languages, but they can be simulated using abstract classes or duck typing.