Intermediate - Python Developer Interview: OOP Concepts

Intermediate - Python Developer Interview: OOP Concepts

Part4

Photo by Amélie Mourichon on Unsplash

Encapsulation, Abstraction, Inheritance, and Polymorphism are the basic pillars of Object-Oriented Programming. In this article, I will be taking you through these concepts and how those are implemented in Python.

Before explaining these concepts we should know some basics, those are:

  • Class:

It is the way to define the user defined object type. It has set of methods and attributes which are applicable to similar kind of objects.

  • Object

It is instantiation of class, which carry forwards all the characteristics of class.

  • Methods

Methods are nothing but the functions which perform some activity in class for the particular object.

I would like to explain these basics through an example, below example depicts Employee class, where headquarter is class variable and init method acts as constructor here, init method is called whenever there is new object creation.

Class variable is common to all the objects, that is each object will have same class variable value, and also we can access this with the help of class name as shown in below example:

# Class for Employee
class Employee:

    # Class Variable
    headquarter = "Mumbai"         

    # The init method or constructor
    def __init__(self, empid):
        # Instance Variable   
        self.empid = empid 

    def display(self):
        print("Employee has employee id",self.empid)

# Instantiation of class
a = Employee(10001) #a is object of class Employee
b = Employee(10002) #b is object of class Employee

print(a.headquarter)  # prints "Mumbai"
print(b.headquarter)  # prints "Mumbai"
a.display()    # To access the method of class
print(b.empid) # To access the attributes of object

# Class attributes can be accessed using class name as well
print(Employee.headquarter) # prints "Mumbai"

Encapsulation in Python

Encapsulation is nothing but preventing the access of data. To protect the accidental changes in variables we need to have access restrictions on data. There are three types of access.

  • Public : read and write access to all

  • Protected : read and write access to the owner class and derived class

  • Private : read and write access to the owner class

Below example explains the access types, and how to access public, protected and private variables and methods.

If you see, salary is private variable and can be accessed only inside class, if you try to access through object you will get "AttributeError". Protected variables can be accessed using object as shown in below example, but to access the private variables/methods we need to use special syntax

<obj>._<class name>__<private variable/method>
class Employee:

    __salary = 900000   # private variable
    def __init__(self,name):
        self.name = name        # public variable
        _department = "sales" #protected variable        

    def call_employee(self):                  # public method
        print( "Calling",self.name)

    def __bonus(self):             # private method
        self.__salary += 80000 
        print( "Total salary with bonus: ", self.__salary )

emp = Employee("Gayu") #object instantiation

print("Salary is",emp._Employee__salary) #accessing private variable

emp._Employee__bonus() #accessing private method

emp._department = "Marketing" #accessing protected variable

print( "New department is",emp._department )

print("Name of employee is",emp.name)   #accessing public variable

emp.call_employee() #accessing public method

Output:

Salary is 900000
Total salary with bonus:  980000
New department is Marketing
Name of employee is Gayu
Calling Gayu

Inheritance in Python

There are five types of inheritance:

  • Single inheritance : single parent class for single child class

  • Multilevel inheritance : parent->child->grandchild relationship

  • Hierarchical inheritance : single parent class and multiple child class

  • Multiple inheritance : multiple parent class for one child class

  • Hybrid inheritance : combines more than one form of inheritance

Untitled7.png

Below example shows the single inheritance. Here Person is base class and Employee is derived class from it. If you miss to call the base class init method in child class then you won't be able to access the fname and lname parameters in any method of the child class.

#parent class
class Person:    
    def __init__(self, fname, lname):
        self.firstname = fname
        self.lastname = lname

    def info(self):
        print(self.firstname, self.lastname)

#child class
class Employee(Person):
    def __init__(self, fname, lname, company):
        super().__init__(fname, lname) #calling base class constructor
        self.company = company

#method overriding
    def info(self):
        print("company for "+self.firstname, self.lastname +" is "+self.company")


x = Person("gayatri", "kale")
x.info()

y = Employee("andy", "beifong", "randomcompany")
y.info()

Output:

gayatri kale
company for andy beifong is randomcompany

The syntax while applying all other inheritance is similar to above example, only in case of multiple inheritance syntax would be :

class Base1:
    pass

class Base2:
    pass

class MultiDerived(Base1, Base2):
    pass

Note: In case of inheritance if you don't want to provide the access of some variables to the base class then you can make those variables private variables by appending double underscores as explained in earlier section.

Abstraction in Python

Abstraction is used to hide the implementation details. Just like vehicles, you know how to drive them but you don't know how they internally work. In similar way abstraction helps to reduce the complexity by exposing only relevant information. In Python, abstraction is implemented through class named 'ABC' from the 'abc' module. A method becomes abstract when decorated with the keyword @abstractmethod.

Abstract methods does not have any implementation details in parent class. Base class having the abstract method cannot be instantiated.

Note : All the child classes inherited from parent class having abstract method should contain the abstract method or else we will get the error.

from abc import ABC, abstractmethod

class Game(ABC): #abstract class
    @abstractmethod
    def player_no(self): #abstract method
        pass

class Football(Game):
  players = 11
  def player_no(self): #overriding abstract method
        print("Total players in Football", self.players)
  def points(self):
      print("Touchdown, Field Goal, Safety, Two point conversion, Extra point ")

class Baseball(Game):
  players = 9
  def player_no(self): #overriding abstract method
        print("Total players in Baseball", self.players)


foo = Football() #object created for the class 'Football'
ba = Baseball() #object created for the class 'Baseball
foo.player_no()
ba.player_no()
foo.points()

Output:

Total players in Football 11
Total players in Baseball 9
Touchdown, Field Goal, Safety, Two point conversion, Extra point

Polymorphism in Python

In layman's terms we can say 'same name with different functionality'. Below example explains the polymorphism. area() function executes differently for different type of objects. Method overriding which we have done in inheritance earlier that is also considered as example of polymorphism.

class Square:
    def __init__(self,side):
        self.side = side

    def area(self):  #polymorphism implementation
        print("area of square: ",self.side*self.side)

class Rectangle:
    def __init__(self,length,breadth):
        self.length = length
        self.breadth = breadth

    def area(self): #polymorphism implementation
        print("area of square: ",self.length*self.breadth)

sq = Square(4)
rec = Rectangle(3,4)
sq.area()
rec.area()

Output:

area of square:  16
area of square:  12

That's all I have for this article. Do follow the series if you want to explore more important concepts for Python Developer Interview.