Python
Encapsulation

Encapsulation in Python

Encapsulation is one of the fundamental concepts in object-oriented programming (OOP). It is the bundling of data and methods into a single unit, a class. This allows us to hide the implementation details of the data and only expose the methods that are needed to interact with it. To prevent accidental change, an object’s variable can only be changed by an object’s method. Those types of variables are known as private variable.

Levels of access control in Python

  1. Public: Members (attributes and methods) declared at the top level of a class are accessible from anywhere. By convention, public members are named without any leading underscores.

  2. Protected: Members with a single leading underscore _ are considered protected. They are intended to indicate that a member should not be accessed outside the class or its subclasses, but this is more of a convention than a strict enforcement. This means, the value of the protected attribute can still be changed from the instance object.

  3. Private: Members with double leading underscores __ are considered private. These members are name-mangled to make them harder to access from outside the class. While they can still be accessed, their value can't be changed directly through instance object.

The example below will help you understand these concepts:

class Person:
  def __init__(self, name, age, location):
    self.name = name # public attribute
    self._age = age # protected attribute
    self.__location = location # private attribute
 
  def display_details(self):
    print(self.name, self._age, self.__location)
 
# making instance of Perosn class
person1 = Person("John", 25, "NY")
person1.display_details()
 
# changing public and protected attribute
person1.name = "Ben" # This changes the name outside the class
person1._age = 35 # This changes the age outside the class
person1.display_details()
 
# try changing private attribute
person1.__location = "Chicago" # no change will happen
person1.display_details()
 
# Output:
John 25 NY 
Ben 35 NY 
Ben 35 NY

In the example above, the name , _age and __location attributes have public, protected and private access. As we discussed, we can modify the public and protected attributes value outside the class but not for private attribute. In the next topic, we will learn how to modify these private attributes using setter methods.

Accessor and Mutator Methods (Getters and Setters)

Python encourages the use of accessor and mutator methods to interact with encapsulated attributes. Accessor methods (also known as getters) allow you to retrieve the value of an attribute, while mutator methods (setters) enable you to modify the value of an attribute while applying necessary validations or operations.

Here's an example demonstrating encapsulation with accessor and mutator methods:

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance
    
    def get_balance(self):
        return self.__balance
    
    def set_balance(self, new_balance):
        if new_balance >= 0:
            self.__balance = new_balance
        else:
            print("Balance cannot be negative.")
    
account = BankAccount(1000)
print(account.get_balance())  # Accessing balance using accessor
# Output: 1000
account.set_balance(1500)     # Modifying balance using mutator
print(account.get_balance())
# Output: 1500

Benefits of Encapsulation

  1. Data Protection: Encapsulation prevents direct access to internal data from outside the class, reducing the risk of unintended modifications.

  2. Flexibility: You can change the internal implementation of a class without affecting its external interface, as long as the accessor and mutator methods remain consistent.

  3. Code Organization: Encapsulation helps keep the code organized and modular by grouping related attributes and methods within a class.

  4. Validation and Control: Using mutator methods allows you to apply validation and control logic when modifying attributes, ensuring data integrity.

  5. Inheritance: Encapsulation provides a solid foundation for inheritance, as you can control which attributes and methods are inherited and which are not.