DEV Community

Cover image for Pillars of OOPS (in python)
Kedar Kodgire
Kedar Kodgire

Posted on

Pillars of OOPS (in python)

The scope of this article is to help you understand the concepts of oops quickly and not an in-depth tutorial on coding with oops.

Object-Oriented Programming system is also known as OOPS is one of the hot topics for programming interviews. I have been asked questions about this concept many times(direct and indirect questions) in the interview, and to be true, I was unable to answer the questions most of the time because I never had a clear understanding of it. But when I tried to look closely towards its advantages and try to understand them with my favorite programming language, I felt like I was an improved version of myself while writing the code and I hope it helps you improve your skills and knowledge as well.

Let's start to understanding OOPS

There are four pillars

  • Encapsulation
  • Inheritance
  • Polymorphism
  • Abstraction

Let's Have a look at it each of them

Encapsulation

encapsulation (the action of enclosing something in or as if in a capsule.)

Now, If you understood the word's meaning then you can relate the same with your code, lets look at the example.

class Cat:

    def __init__(self):
        self.sound = "meow"

    def speak(self):
        print("Cat says: {}".format(self.sound))


c = Cat()
c.speak()

# change the price
c.sound = "bow-wow"
c.speak()


#OUTPUT

Cat says: meow
Cat says: bow-wow
Enter fullscreen mode Exit fullscreen mode

In the above example, the class cat has a variable sound that is set to the default value in the constructor, and later we are also changing the value of this variable i.e. c.sound = "bow-wow", Now that you know the meaning of encapsulation as per above definition let's consider class as a capsule, do you think this code follows encapsulation? Well, it doesn't.

To avoid this we can declare the private variables, in Python, we denote private attributes using underscore as the prefix i.e single _ or double __.

class Cat:

    def __init__(self):
        self.__sound = "meow"

    def speak(self):
        print("Cat says: {}".format(self.__sound))


c = Cat()
c.speak()

# change the price
c.sound = "bow-wow"
c.speak()


#OUTPUT

Cat says: meow
Cat says: meow
Enter fullscreen mode Exit fullscreen mode

Now, it has been encapsulated. Well, this is the encapsulation.
Hence Some practical examples of this are

  • A bank application forbids (restrict) a client to change an Account's balance.
  • It is also used to hide the data - let's say you log into your email account, there is a lot of process in the backend, that you have no control over. So your password, would probably be retrieved in an encrypted form, verified and only then you are given access. You do not have any control, over how the password is verified, and this keeps it safe from misuse. (source: Quora)

Inheritance

Inherit (to receive a quality, characteristic, etc. from your parents or family.)

As you see the definition above, This allows us to define a class that inherits methods, properties from another class. Let's say you need a new class with little or no modification then you apply this concept.

The base class is called the parent class and the derived class is known as a child. It's Obvious, I know :)

Let us see an example

class Family:
    def __init__(self, family_name, number_of_members, country):
        self.family_name = family_name
        self.number_of_members = number_of_members
        self.country=country


    def member_says(self):
        print(f"Hey, I am from {self.family_name} family and there are {self.number_of_members} members in family")


class Family_detailed:
    def __init__(self, family_name, number_of_members, country):
        self.family_name = family_name
        self.number_of_members = number_of_members
        self.country=country

    def member_says(self):
        print(f"Hey, I am from {self.family_name} family and there are {self.number_of_members} members in family")

    def which_country(self):
        print(f"The {self.family_name} family has roots from {self.country}" )


a = Family("Rodrigues",5,"Peru")
b = Family_detailed("Bezos",15,"United States of America")

a.member_says()
b.member_says()
b.which_country()

#Output

Hey, I am from the Rodrigues family and there are 5 members in the family
Hey, I am from the Bezos family and there are 15 members in the family
The Bezos family has roots in the United States of America
Enter fullscreen mode Exit fullscreen mode

The purpose of the first class is to display family names and the number of members but the second class wants to display the same information along with the country. As you see there is a lot of common code here, let's use inheritance to make it simpler.

class Family:
    def __init__(self, family_name, number_of_members, country):
        self.family_name = family_name
        self.number_of_members = number_of_members
        self.country=country

    def member_says(self):
        print(f"Hey, I am from {self.family_name} family and there are {self.number_of_members} members in family")


class Family_detailed(Family):

    def which_country(self):
        print(f"The {self.family_name} family has roots from {self.country}" )


a = Family("Rodrigues",5,"Peru")
b = Family_detailed("Bezos",15,"United States of America")

a.member_says()
b.member_says()
b.which_country()

#Output

Hey, I am from the Rodrigues family and there are 5 members in the family
Hey, I am from the Bezos family and there are 15 members in the family
The Bezos family has roots in the United States of America
Enter fullscreen mode Exit fullscreen mode

Wow, as you see the code has reduced significantly. So, this was the inheritance, let's move to the next pillar.

Abstraction

Abstraction (A general idea rather than one relating to a particular object, person, or situation.)

To understand it better, let's take an example of a car, people don't see the car as thousands of individual parts, instead, they see it as a well-defined object with unique behavior. They don't need to understand the complexity of those parts and how to collaborate with each other. The same goes with a vending machine, you just want to put money in and get your favorite snack and there is no need of understanding how the system works.

in simple terms, Abstraction focuses on hiding the internal implementations of a process or method from the user. In this way, the user knows what they are doing but not how the work is being done.

This is a little different than the other pillars. Python does not grant abstract classes by default. Instead, Python comes with a module that fits the base for Abstract Base classes(ABC) and that module name is ABC. A method becomes abstract when decorated with the keyword @abstractmethod

Let's take a simple example

from abc import ABC, abstractmethod
class Company(ABC):

    def work(self):
        pass

class Manager(Company):

    def work(self):
        print("I assign work to and manage team")

class Employee(Company):

    def work(self):
        print("I complete the work assigned to me")

# Driver code
R = Manager()
R.work()

K = Employee()
K.work()
Enter fullscreen mode Exit fullscreen mode

Polymorphism

polymorphism (the condition of occurring in several different forms)

One of the awesome examples of understanding this will be Ben 10. He's one person but he can serve the world in multiple forms.

Ben 10 with his characters

Back to programming, it refers to the use of a single method/operator to represent different types in different scenarios.

Python can use multiple classes in the same way, using Polymorphism. To serve this purpose, we can create a loop that iterates through a tuple of objects. And this will allow us to call methods without looking at the class to which the object is pointing.

class Class1(): 
    def pt(self): 
        print("This function determines class 1") 

class Class2(): 
    def pt(self): 
        print("This function determines class 2.") 

obj1 = Class1() 
obj2 = Class2() 
for type in (obj1, obj2): # creating a loop to iterate through the obj1, obj2
    type.pt() 


#Output
This function determines class 1
This function determines class 2.
Enter fullscreen mode Exit fullscreen mode

similarly talking about the operators, let's look at the + operator.

> print(1+2)
3
> print ("a"+"b")
> ab
Enter fullscreen mode Exit fullscreen mode

As you see, the + operator behaves different way with different datatypes.

That's all for this post folks.
Don't forget to ❤ or 📑 this article if you enjoyed it.

Have a great day.
Happy Coding!!

Top comments (2)

Collapse
 
saranjoel profile image
SaranJoel

Good explanation

Collapse
 
kedark profile image
Kedar Kodgire

Thanks!