Posted on 6/19/2025 1:12:46 PM by Admin

Object-Oriented Programming in Python: Encapsulation and Abstraction

So far, we have learned how classes and objects make programmers' lives easier by allowing them to write general definitions for the objects and instantiating them for implementation. We also learned how to extend classes and inherit their properties and behaviors in other classes. Now, it's time to see how to protect data from being accessed and hide the complex logic from the end user.

Encapsulation and abstraction are the other two fundamental pillars of object-oriented programming that ensure data safety. Encapsulation involves enclosing data and functions within classes and using access specifiers to control their access. On the other hand, abstraction implies hiding the complex program logic from the end user, so only the necessary information is accessible to them. In this article, we will learn how to implement these concepts in our programs.


Encapsulation: Bundling Data and Methods

Encapsulation is the process of enclosing data and methods within a single unit, known as a class. A key aspect of this concept is data hiding, i.e., restricting direct access to the object's data and manipulating it only through the defined methods.

Benefits of Encapsulation

  • Ensures that an object's internal data is not accessed or manipulated by external code, either accidentally or maliciously (data protection).
  • Since the related data and methods are enclosed in classes, any changes made to these attributes do not directly affect any outside code interacting with that class.

"Private" and "Protected" Members in Python

Unlike other programming languages (such as Java, C#, etc.), Python does not have strict access specifiers to prevent external access to attributes. Instead, it follows some conventions to suggest how data and methods should be accessed. By default, all the class members are public in Python.

Public Members

The public members are those with no leading underscore in their names. These members can be accessed from anywhere in the program.


class MyClass:
    def public_method(self):
        print("I am public.")

Private Members

Private members should only be accessed within the class itself. To make a member private, double-leading underscore (__) must be used in the member's name. This makes Python perform a process called name mangling, which makes it hard to access the attributes from outside the class. Its main purpose is to avoid name clashes between subclasses rather than providing strict security to class members.


class MyClass:
    def __init__(self):
        self.__private_data = "Secret info"
    def __private_method(self):
        print("This is a private method.")

obj = MyClass()
#
# print(obj.__private_data) # This would raise an AttributeError
#
# print(obj._MyClass__private_data) # This technically works, showing it's not truly private

The Getters and Setters (Accessor and Mutator Methods)

The Getters and Setters are the public methods that allow you to read and write values of the private and protected attributes. The getter methods are called Accessors, and the setters are known as Mutators. These methods allow you to validate logic, log, and perform calculations wherever the data is accessed while maintaining data integrity.


class BankAccount:
    def __init__(self, initial_balance):
        if initial_balance < 0:
            print("Invalid initial balance!")
        self.__balance = initial_balance # "Private" attribute

    def get_balance(self): # Getter method
        """Returns the current account balance."""
        return self.__balance

    def deposit(self, amount): # Setter-like method
        """Deposits a positive amount into the account."""
        if amount > 0:
            self.__balance += amount
            print(f"Deposited {amount}. New balance: {self.__balance}")
        else:
            print("Deposit amount must be positive.")

account = BankAccount(300)  # Initial Balance 300
account.deposit(500)  # Deposited 500
account.get_balance()  # Inquiring the balance

Output:


Deposited 500.
New balance: 800

The @property Decorator

Python provides a more efficient way to implement getters, setters, and deleters, specifically through the use of the @property decorator. It allows you to access member methods as attributes, keeping the code cleaner and retaining control.


class Celsius:
    def __init__(self, temperature=0):
        self._temperature = temperature  # Use protected convention

    @property # The getter method
    def temperature(self):
        print("Getting value...")
        return self._temperature

    @temperature.setter # The setter method
    def temperature(self, value):
        if value < -273.15:
            print("Temperature below absolute zero is not possible. ")
        print("Setting value...")
        self._temperature = value

c = Celsius(25)
print(c.temperature) # Calls the getter: "Getting value... \n 25"
c.temperature = 30   # Calls the setter: "Setting value... \n"
print(c.temperature) # Calls the getter: "Getting value... \n 30"

Output:


Getting value...
25
Setting value...
Getting value...
30


Abstraction: Hiding Complexity

Abstraction is the process of hiding complex logical details from the user and providing access to only the necessary details. It ensures that users only know what an object does rather than how it does it. We interact with the object at a higher level without knowing its intricate internal details.

To understand this concept, you can consider a car. When you drive a car, you use gears, brakes, accelerator, and clutch. You know what each part does, but you don't have to understand how it works. The car's design conceals these details, providing access to the necessary information through a straightforward interface.

Benefits of Abstraction

  • Abstraction makes complex systems easier to understand and use by hiding complex details (simplicity).
  • Changes made to the internal components of an abstracted component do not affect the external users (maintainability).
  • Protects data from being accessed and changed by the users (protection).

Abstract Methods and Abstract Classes

Abstract methods and classes are typically incomplete, meaning they lack implementation details. Users can implement them as needed. An abstract class cannot be instantiated on its own. Instead, it provides a blueprint for other classes. An abstract method is declared inside an abstract class but does not have any implementation. The inheritor of the abstract class implements this method.

In Python, there is a separate module for creating abstract classes and methods, i.e., abc module. You can import it and use abstract classes in your programs.


from abc import ABC, abstractmethod

class Shape(ABC): # Declaring Shape as an Abstract Base Class
    @abstractmethod # This method must be implemented by subclasses
    def area(self):
        pass # No implementation here

    @abstractmethod # This method must also be implemented
    def perimeter(self):
        pass

class Circle(Shape): # Circle is a concrete subclass of Shape
    def __init__(self, radius):
        self.radius = radius

    def area(self): # Must implement abstract method 'area'
        return 3.14159 * self.radius**2

    def perimeter(self): # Must implement abstract method 'perimeter'
        return 2 * 3.14159 * self.radius

# You CANNOT create an object of an abstract class directly:

# my_shape = Shape() # This would raise a TypeError

my_circle = Circle(5)
print(f"Circle area: {my_circle.area()}")
print(f"Circle perimeter: {my_circle.perimeter()}")

Output:


Circle area: 78.53975

Circle perimeter: 31.4159

You have now explored and mastered all four pillars of the object-oriented programming paradigm. The encapsulation allows you to enclose related members into classes, protecting them from external access. The abstraction helps you hide complex logic from the user, providing a simple interface. The encapsulation and abstraction work hand-in-hand. Polymorphism allows you to create different forms of one method, and inheritance allows you to derive more classes from an existing class to avoid code redundancy and repetition. In the next article, we will explore comprehensions, an advanced data structure in Python.


Sharpen Your Skills with These Next Guides