askvity

How to Implement Abstraction in Python?

Published in Object-Oriented Programming 4 mins read

Abstraction in Python allows you to hide complex implementation details and expose only the essential information to the user. This simplifies the interaction with objects and systems, making code easier to understand and maintain. You can achieve abstraction through abstract classes and interfaces (using the abc module) and through well-designed classes that hide internal workings.

Here's how to implement abstraction in Python:

1. Using Abstract Classes and Methods (abc Module)

The abc (Abstract Base Classes) module provides the infrastructure for defining abstract base classes in Python. An abstract class cannot be instantiated directly; it serves as a blueprint for other classes. Abstract methods, declared within an abstract class, must be implemented by any concrete (non-abstract) subclass.

Steps:

  1. Import the abc module: from abc import ABC, abstractmethod
  2. Create an abstract class: Inherit from ABC.
  3. Define abstract methods: Use the @abstractmethod decorator. These methods have no implementation in the abstract class.
  4. Create concrete subclasses: Inherit from the abstract class and provide implementations for all abstract methods. If a subclass doesn't implement all abstract methods, it will also be considered an abstract class.

Example:

from abc import ABC, abstractmethod

class Shape(ABC):  # Abstract class
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Square(Shape):  # Concrete class inheriting from Shape
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side * self.side

    def perimeter(self):
        return 4 * self.side

class Circle(Shape): #Concrete class inheriting from Shape
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius * self.radius

    def perimeter(self):
        return 2 * 3.14 * self.radius


# shape = Shape() # This would cause a TypeError because Shape is an abstract class

square = Square(5)
print("Square Area:", square.area())       # Output: Square Area: 25
print("Square Perimeter:", square.perimeter()) # Output: Square Perimeter: 20

circle = Circle(7)
print("Circle Area:", circle.area())
print("Circle Perimeter:", circle.perimeter())

Explanation:

  • Shape is an abstract class with abstract methods area and perimeter.
  • Square and Circle are concrete classes that inherit from Shape and implement the area and perimeter methods.
  • Attempting to instantiate Shape directly would result in a TypeError.

2. Hiding Implementation Details (Encapsulation)

Another way to achieve abstraction is through encapsulation, which involves hiding the internal state of an object and providing methods to interact with it.

Techniques:

  • Naming conventions: Use underscores (_ or __) to indicate that an attribute or method is intended for internal use and should not be accessed directly from outside the class. Python doesn't enforce true private members, but these conventions signal intent. A single underscore is a convention meaning "protected". A double underscore enables name mangling.
  • Getters and setters (properties): Provide methods (getters) to access the value of an attribute and methods (setters) to modify it. This allows you to control how the attribute is accessed and modified, potentially adding validation or side effects.

Example:

class BankAccount:
    def __init__(self, account_number, balance):
        self._account_number = account_number  # Protected attribute
        self.__balance = balance #Name mangled attribute

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"Deposited {amount}. New balance: {self.__balance}")
        else:
            print("Invalid deposit amount.")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"Withdrew {amount}. New balance: {self.__balance}")
        else:
            print("Insufficient funds or invalid withdrawal amount.")

    def get_balance(self):
        return self.__balance

    def get_account_number(self):
        return self._account_number


account = BankAccount("1234567890", 1000)
account.deposit(500)        # Output: Deposited 500. New balance: 1500
account.withdraw(200)       # Output: Withdrew 200. New balance: 1300
print("Balance:", account.get_balance())  # Output: Balance: 1300
print("Account Number:", account.get_account_number()) # Output: Account Number: 1234567890

#print(account.__balance) # This would raise an AttributeError due to name mangling.
print(account._BankAccount__balance) #This would work, but it is a discouraged practice.

Explanation:

  • _account_number is a "protected" attribute. It's generally understood that this is for internal use, even though it's technically accessible from outside the class.
  • __balance is a name-mangled attribute. Python renames it to _BankAccount__balance to make it harder (but not impossible) to access directly from outside the class. This provides stronger, though still imperfect, protection.
  • get_balance() and get_account_number() are getter methods that provide controlled access to the account's balance and account number.

Benefits of Abstraction:

  • Simplifies code: By hiding complex details, abstraction makes code easier to understand and use.
  • Reduces dependencies: Changes to the internal implementation of a class do not affect code that uses the class, as long as the public interface remains the same.
  • Increases reusability: Abstract classes and interfaces can be reused in multiple contexts.
  • Improves maintainability: Changes to the implementation details are localized and do not require changes to other parts of the code.

In summary, abstraction in Python is achieved by defining abstract classes and methods and by hiding internal implementation details through encapsulation. These techniques help to create more robust, maintainable, and reusable code.

Related Articles