×

Abstraction and Encapsulation in OOPS – Python

Abstraction in Python

Abstraction means partial implementation of functionality. In Abstraction, we can define a function without complete functionality in a class so that it can be defined completely in its child class.

Using abstraction we can provide code reusability and reduce the complexity of the code. Abstraction also provides more security.

In Python, we can implement abstraction using abstract classes and abstract methods, where both of them are inherited from the abstract base class module( abc ).

Let’s check how to declare an abstract method, abstract class, and the properties associated with them.

abstract method

A method is known as the abstract method if it’s declared but without proper implementation. Such types of methods are known as abstract methods.

Child classes are responsible to provide an implementation of the parent class abstract methods.

In Python, we declare the abstract method by using the @abstractmethod decorator. This decorator is present in abc module so we need to import it.

In the below example, the method m1 is an abstract method in the parent class. We are declaring the m1 as an abstract method using the @abstractmethod decorator.

from abc import abstractmethod
class parent():
    @abstractmethod
    def m1(self):
        pass

Since the abstract method m1 is partially implemented in the parent class, the child class that inherits the parent class is responsible for complete declaration.

from abc import ABC, abstractmethod
class parent():
    @abstractmethod
    def m1(self):
        pass
class child(parent):
    def m1(self):
        print("hi welcome")

obj = child()
obj.m2()

# Output
hi welcome

abstract class

If the class is partially implemented and is inherited from the ABC class then such type of partially implemented class is known as the abstract class. abstract classes may have one or more abstract methods in them.

from abc import ABC, abstractmethod
class parent(ABC):
    @abstractmethod
    def m1(self):
        pass
class child(parent):
    def m1(self):
        print("hi")

obj = child()
obj.m1()

# Output
hi

Important conclusions about abstract class and abstract method

If an abstract class contains an abstract method then object or instantiation of the class is not possible because it has not completed implementation.

But if the abstract class has no abstract method then it can be instantiated.

In the below example, the parent class is an abstract class and has an abstract method in it. This means it’s not completely implemented.

So such classes cannot be instantiated. If we try to create an object for this class it returns an error.

from abc import ABC, abstractmethod
class parent(ABC):
    @abstractmethod
    def m1(self):
        pass
obj = parent()

# Output
TypeError: Can't instantiate abstract class parent with abstract methods m1

In the below example, the parent class is an abstract class but has no abstract method in it. Such classes can be instantiated.

from abc import ABC, abstractmethod
class parent(ABC):
    pass
obj = parent()

If an abstract class has an abstract method and the child class that inherits the parent abstract class has to definitely implement all the abstract methods that are in the parent class. If we don’t implement all these abstract methods then it returns TypeError.

Note:- An abstract class with an abstract method provides guidelines to implement methods for a child class.

In the below example, the parent class is an abstract class and has abstract methods m1, m2 in it. The child class that inherits the parent class has to definitely implement the abstract methods m1 and m2.

from abc import ABC, abstractmethod
class parent(ABC):
    @abstractmethod
    def m1(self):
        pass
    @abstractmethod
    def m2(self):
        pass 
class child(parent):
    def m1(self):
        print("hi")
    def m2(self):
        print("hello")

obj = child()
obj.m1()
obj.m2()

# Output
hi
hello

If we don’t implement the abstract methods of an abstract class then python returns TypeError.

from abc import abstractmethod
class parent(ABC):
    @abstractmethod
    def m1(self):
        pass
class child(parent):
    def func(self):
        print("hi")


obj = child()
obj.func()

# Output
TypeError: Can't instantiate abstract class child with abstract methods m1

But if the parent class is not an abstract class but has abstract methods then the child class does not have to definitely implement the abstract methods from the parent class.

In the below example, the parent class is not an abstract class but has an abstract method m1. The child class does not have to definitely implement this abstract method. It’s completely optional for declaring such abstract methods.

from abc import abstractmethod
class parent():
    @abstractmethod
    def m1(self):
        pass
class child(parent):
    def m1(self):
        print("hi")


obj = child()
obj.m1()

# Output
hi
from abc import abstractmethod
class parent():
    @abstractmethod
    def m1(self):
        pass
class child(parent):
    def func(self):
        print("hi")


obj = child()
obj.func()

# Output
hi

Interface Classes

In python, we do not have a specific concept about interface class, but the properties of an interface class can be implemented.

If an Abstract class contains only abstract methods in it then this class is known as interface class. Interface classes provide service requirements specifications.

In the below example, employee class is an abstract class with just abstract methods as its members. When a child class inherits the employee class it has to implement these name and age abstract methods as mandatory.

from abc import ABC, abstractmethod
class employee(ABC):
    @abstractmethod
    def name(self):
        pass
    @abstractmethod
    def age(self):
        pass

Encapsulation in Python

Encapsulation is a concept of wrapping the data and methods into a single entity. Every Class in Python is an example of Encapsulation. Because we wrap the methods and data under a single entity known as class.

In Python, encapsulation is one of the fundamental concepts and it can also be referred to as implementing data hiding and abstraction concepts together.

In the below example, we are wrapping up the value variable and method(m1) under a single class, this concept is known as encapsulation. Based on requirements we can change the access modifiers of these variables and methods.

class employee:
    def __init__(self):
        self.age = 25
    
    def age(self):
        print(self.age)
      

obj = employee()
obj.age()

# Output
25

Similar to other languages such as Java, we don’t have any public, private, protected keywords but the concepts related to these terms can be implemented.

Public Members

Public members are those that are accessible from inside and outside of the class. These members are wrapped into a single entity so that they can be accessed from both inside and outside of the entity.

By default, In python every variable, every method is a public member. We don’t have to explicitly declare them as public or change the access modifier into public.

In the below example, company is a class that has members inside it. All these members of the class are by default public and can be accessed inside and outside using the reference variable.

class company:
    def __init__(self):
        self.revenue = "75 lacs"
    
    def members(self):
        print("The number of employees are 150")
        print("Revenue generated:- ", self.revenue)
      

obj = company()
obj.members()

# Output
The number of employees are 150
Revenue generated:- 75 lacs

Private Members

Private Members of a class are those that can be accessed from within the class itself and cannot be accessed from outside of the class or its subclass. Private members are declared differently when compared to public members.

Private Members of the class can be declared using double underscore( "__" ) preceding their respective names.

# Private variables
__age, __name, __value

# Private methods
__func(), __m1()

These private members can be accessed from inside the class directly. But if we try to access them from outside of the class then AttributeError is returned.

In the below example, we have employee class and self.__name and self.__age as private variables and __details() as a private method. These private variables and methods can be accessed from within the class. But if we try to access them from outside then PVM throws an error.

class employee:
  def __init__(self, name, age):
    # private variables
    self.__name = name
    self.__age = age
  
  # private method
  def __details(self):
    print("age is", self.__age)

  def name(self):
    print("name :- ", self.__name)
    # accessing method from inside of class
    self.__details()

obj = employee("john", 25)
obj.name()

# Output
name :-  john
age is 25
class employee:
  def __init__(self, name, age):
    # private variables
    self.__name = name
    self.__age = age

  # private method
  def __details(self):
    print("age is", self.__age)

  def name(self):
    print("name :- ", self.__name)

obj = employee("john", 25)
obj.__details()

# output
'employee' object has no attribute '__details'


print(obj.__name)

# Output
'employee' object has no attribute '__name'

If we want access to these private members outside of the class then we need to use the objectreference._classname__variablename.

class employee:
  def __init__(self, name, age):
    # private variables
    self.__name = name
    self.__age = age
  
  # private method
  def __details(self):
    print("joined in 2015")


obj = employee("john", 25)
obj._employee__details()
print(obj._employee__name)
print(obj._employee__age)

# Output
joined in 2015
john
25

Protected Members

Protected Members are those that can be accessed from within the class and its sub-classes. These protected members cannot be accessed from outside of the class.

Protected Members of the class can be declared using underscore( "_" ) preceding their respective names.

# Protected variables
_age, _name

# Protected methods
_func(), _m1()

These private members can be accessed from inside the class and also can be accessed by its child class. But if we try to access them from outside of the class then AttributeError is returned.

In the below example, we have student class and self._name and self._marks as protected variables and _details() as a protected method. These protected variables and methods can be accessed from within the class and its subclass. But if we try to access them from outside then PVM throws an error.

class student:
    def __init__(self, name, marks):
        # protected variables
        self._name = name
        self._marks = marks
  
    # protected method
    def _details(self):
        print("name is", self._name)

class age(student):
    def info(self):
        self._details()
        print("age is 15")

obj = age("john", 90)
obj.info()

# Output
name is john
age is 15

Thus the protected members of a class can be accessed from its child class object_reference directly.