Intro to OOP with Python

Abu Taher Muhammad
5 min readOct 24, 2023

--

Object-oriented programming (OOP) is a programming paradigm that organizes software design on the concept of data, or objects, rather than functions and logic. It is one of the computer programming paradigms defined by the early programmers; Afterward, there are so many other best paradigms ware contributed to the programmer’s society over time. As its name suggests, OOP follows object-styled programming with classes. Where a single class represents a single task/topic containing all the parameters & methods related to it. Which makes the project more cleaner and organized.

For instance, in an animal classification project, we can create an Animal class containing features that are common to all the animals.

class Animal:
def __init__(self):
pass

def make_sound(self):
print(f"Universal sound! is this really possible? 🤔")

Structure of OOP

  • Class is the actual blueprint of data or you may call Data Type defined by the user.
  • Object is the instance of data. You can think of it as the final version of the data generated after instantiating the class.
  • Methods are the functions that are defined inside the class. These give us the opportunity to manipulate data in an object or might say these are the functions encapsulated from the global scope which is only meant for specific data. And it can only be used by instantiating the object class.
  • Attribute defined in a class object represents the state of an object.

4 pillars of OOP

i. Inheritance

ii. Polymorphism

iii. Encapsulation and

iv. Data Abstraction

Inheritance:

As the name suggests this principle enables classes to inherit attributes and methods from other classes in a unique hierarchy while maintaining a higher level of accuracy through data.

Do we really have to do that? Maybe yes or maybe not! depends on the specific needs of your program. In a large program for animal classification, we might have numerous classes with many attributes and methods. While it’s possible to define attributes in each class (Firstly, we are denying some best practices here like DRY coding), this approach can eventually lead to repetition and inconsistencies. For example, if you need to change a core attribute, you’d have to do it manually in every class where that attribute is used. That’s hence a lot of work! In contrast, using inheritance can simplify this process. By updating the parent class that defines the attribute, all inherited classes are automatically updated. This approach follows the DRY (Don’t Repeat Yourself) principle, making it more efficient and maintaining consistency in your code.

Let’s apply what we’ve learned by updating our example code. We classify the animal kingdom into two categories based on the presence or absence of a notochord. Therefore, we create two classes: NonChordata and Chordata, which are derived from a common Animal class containing shared attributes and methods. This represents the use of inheritance in object-oriented programming.

Here is an example code equivalent to the concept we provided above:

# Parent class
class Animal:
def __init__(self):
pass

def make_sound(self):
print(f"Universal sound! is this really possible? 🤔")

# Child class for Non Chordata animal group
class NonChordata(Animal):
def __init__(self):
self.notochord = False

# Child class for Chordata animal group
class Chordata(Animal):
def __init(self):
self.notochord = True

Polymorphism:

Poly means many, and morph means shape. Therefore, polymorphism denotes many shapes, signifying that a method inherited from the parent class can exhibit different behaviors in various classes. For instance, if we have a method in the Animal class called make_sound, which allows animals to produce sounds, we understand that different animals generate distinct sounds based on their classification. Consequently, we must adjust the sound in each class, exemplifying the concept of polymorphism.

Here is the example:

class Animal:
def __init__(self):
pass

def make_sound(self):
print(f"Universan sound! is this really possible? 🤔")

# Non Chordata animal group
class NonChordata(Animal):
def __init__(self):
self.notochord = False
super().)__init__()

def make_sound(self):
print(f"I sound like a NonChordata animal! Let\'s assume it is possible 🤔")

# Chordata animal group
class Chordata(Animal):
def __init(self):
self.notochord = True
super().)__init__()

def make_sound(self):
print(f"I sound like a Chordata animal! Let\'s assume it is possible 🤔")

Encapsulation:

The word Encapsulation lets us know ****that something should be isolated. This principle states whether a piece of information defined in a class should be contained inside or exposed to the outside. As an example, there might be someone who will eventually/accidentally change the value of the notochord attribute. This will lead us to a whole different meaning than what the data is meant for. That’s what Encapsulation forces for data security to avoid unintended data corruption.

To prevent such behavior outside of the class scope, we add two underscores (__) before the attribute. Now no one will be able to change the value of the __notochord attribute outside of the class.

Here is our updated code with encapsulation:

class Animal:
def __init__(self):
pass

def make_sound(self):
print(f"Universan sound! is this really possible? 🤔")

# Non Chordata animal group
class NonChordata(Animal):
def __init__(self):
self.__notochord = False
super().__init__()

def make_sound(self):
print(f"I sound like a NonChordata animal! Let\'s assume it is possible 🤔")

# Chordata animal group
class Chordata(Animal):
def __init(self):
self.__notochord = True
super().__init__()

def make_sound(self):
print(f"I sound like a Chordata animal! Let\'s assume it is possible 🤔")

Setter & Getter

Now, a new question arises: how can we make an intended change to an attribute? To address this, we have something called “setter” and “getter.” These provide us with the ability to modify the __notochord attribute.

Setter will let us set changes to the attribute and Getter to read the value.

class Chordata(Animal):
def __init(self):
selft.__notochord = True

@property
def notochord(self):
return self.__notochord

@notochord.setter
def notochord(self, value):
self.__notochord = value

With this setup, we no longer have direct access to the __notochord attribute. To read and update the value, we can use the following:

animal_1 = Chordata()

# Reading the value
print(animal_1.notochord)

# Updating the value
animal_1.notochord = False

Abstraction:

Here, we encounter another crucial concept that provides a method for establishing an abstract framework for our classes. Suppose we desire to include a make_sound method in all classes derived from the Animal class, with every derived class strictly adhering to this rule. In this scenario, we can employ the abstraction principle to accomplish this goal.

Here is example where implementing abstraction to our main example:

# Importing `ABS` and `abstractmethod` decorator from the `abs` module.
from abc import ABS, abstractmethod

class Animal(ABS):
def __init__(self):
pass

@abstractmethod
def make_sound(self):
print(f"Universan sound! is this really possible? 🤔")

# Non Chordata animal group
class NonChordata(Animal):
def __init__(self):
self.__notochord = False
super().)__init__()

def make_sound(self):
print(f"I sound like a NonChordata animal! Let\'s assume it is possible 🤔")

# Chordata animal group
class Chordata(Animal):
def __init(self):
self.__notochord = True
super().)__init__()

def make_sound(self):
print(f"I sound like a Chordata animal! Let\'s assume it is possible 🤔")

OOP provides a structured and organized approach to programming, leading to cleaner and more maintainable code. It encourages best practices such as the DRY (Don’t Repeat Yourself) principle and abstraction. A powerful paradigm that promotes code re-usability, modularity, and clarity, making it an essential tool in modern software development.

If you have any questions or need further assistance, please don’t hesitate to reach out. You can contact me through the comment box below or via email at muhammad@dot9.dev.

Remember to make the most of every moment in life and enjoy the journey.

Visit my portfolio below:

--

--

Responses (2)