Object Oriented Programming in Python

Object-Oriented Programming in Python

Introduction

Object-Oriented programming (OOP) is the most popular programming paradigm based on the concept of Classes and Objects, which contains data members (fields) and member functions (methods). It can translate every real-time living and non-living object into programming.

In OOP, objects are instances of classes, which define the attributes and methods that the objects will have. Classes can inherit attributes and methods from other classes, which can help to reduce code duplication and make it easier to maintain and extend code.

Some key concepts of OOP include:

  • Encapsulation: The practice of hiding the implementation details of an object's behavior and providing a public interface for interacting with the object.
  • Abstraction: The process of simplifying complex systems by breaking them down into smaller, more manageable parts, and modeling those parts as objects.
  • Inheritance: The ability of a class to inherit attributes and methods from another class, which can help to reduce code duplication and make it easier to create specialized classes.
  • Polymorphism: The ability of different objects to respond to the same message in different ways, based on their specific implementations of the message's corresponding method.
Key Concepts of Object-Oriented Programming
Key Concepts of Object-Oriented Programming

In this tutorial, we will try to learn about Object-Oriented Programming from the very basics and will implement all the concepts through Python Programming.

Why Object-Oriented Programming?

We got a brief idea of OOP but that's not enough to understand the whole concept. We will dig it more here. Let's start with the concept of the object through real-life examples.

In simple words, you're an object, I'm an object, every person is an object and every individual object has some attributes and behavior. For example, if we consider people as objects then they have some attributes like Name, Surname, Age, Gender, etc. at the same time they carry some behavior like playing, drawing, singing, dancing, and many more.

I'm assuming you are familiar with Procedural Programming (where a program is written as a series of steps or procedures. Each procedure performs a specific task and the program is organized into modules, or sub-programs or functions, that call the procedures, and it is a top-to-down approach).

Consider a real-life example of an employee data set where every employee has Emp. ID, Name, Surname, Age, Contact, and address. These are nothing but attributes and the values can be easily stored using variables in a program. We can write functions to do some kind of data manipulation with those values.

It's a traditional way to do so. But think about what happens if all the same variables and functions will use for 100 employees. In simple words, It's not gonna work as we think because every employee has his/her own identity and it is defined by attributes and behavior.

It means every employee must have his/her own variables and functions and they should not be shared with other employees. In this scenario, Object-Oriented Programming is the solution.

I hope now you got an idea of OOP and why it's so important in Programming.

What is Class in Object-Oriented Programming?

In object-oriented programming (OOP), a class is a blueprint or template that defines the attributes (data) and methods (functions) that objects of that class can have. A class is a user-defined data type that is used to create objects, which are instances of that class.

A class defines the structure and behavior of objects, and it encapsulates related data and behavior into a single unit. The attributes of a class are typically represented by member variables or instance variables, while the methods of a class are represented by member functions.

For example, consider a class named "Person" that defines the attributes and methods of a person. The "Person" class may have attributes such as name, age, gender, and address, and methods such as walking, talking and eating. Objects of the "Person" class can be created by instantiating the class, which creates a new instance of the class with its own set of attributes and methods.

Classes can also inherit attributes and methods from other classes, which is known as inheritance. Inheritance allows for the creation of specialized classes that extend or modify the behavior of a base class, without having to rewrite the code for the base class.

Overall, classes are an essential feature of OOP and provide a way to organize code into reusable and modular units, which can simplify the development and maintenance of complex software systems.

I hope, now the idea of classes and objects is clear to you.

Define Classes and Objects in Python

Here, we will learn how to define classes and objects in python. We will take the instance of Employee Dataset to implement a real-world problem in programming. 

Define an Empty Class

First, we will create an empty class named "Employee".


class Employee:
pass

Define an Object of a Class

Defining an object of a class is pretty straightforward. Here is an example. 


class Employee:
pass

obj = Employee()

The __init__() function

We have seen how to create an object of a class. Here, we will talk about Constructor in Python. In simple, a constructor is a special method in object-oriented programming that is used to initialize objects of a class. When an object is created, the constructor is called automatically and is used to set the initial values of the object's attributes. The constructor typically takes arguments that correspond to the attributes of the object being created.

In most object-oriented programming languages, the name of the constructor method is the same as the name of the class. In python, the "__init__()" function plays the role of a constructor. We must provide the 'self' as the first parameter to the "__init__()" function. It represents the instance of the current class and it allows to access all the attributes and methods defined within that class.

Let's understand the whole concept through an example.


class Employee:
def __init__(self):
self.id = "101124"
self.name = "David"
self.surname = "Jones"
self.age = 32
self.contact = "1234567890"
self.address = "Illinois"
self.designation = "Project Manager"

obj = Employee()

print("ID:",obj.id)
print("Name:",obj.name)
print("Surname:",obj.surname)
print("Age:",obj.age)
print("Contact:",obj.contact)
print("Address:",obj.address)
print("Designation:",obj.designation)

Output

ID: 101124
Name: David
Surname: Jones
Age: 32
Contact: 1234567890
Address: Illinois
Designation: Project Manager

Define more functions inside a class

In the previous example, we declared the "__init__()" function to store attribute values (inside a program, called variables) and printed that data from outside of the class using the class object.

Here, we will delegate the printing task to a function. We will define a different method (or function) within the "Employee" class to print the data of those variables separately.


class Employee:
def __init__(self):
self.id = "101124"
self.name = "David"
self.surname = "Jones"
self.age = 32
self.contact = "1234567890"
self.address = "Illinois"
self.designation = "Project Manager"

def display(self):
print("ID:",obj.id)
print("Name:",obj.name)
print("Surname:",obj.surname)
print("Age:",obj.age)
print("Contact:",obj.contact)
print("Address:",obj.address)
print("Designation:",obj.designation)

obj = Employee()
obj.display()

Output

ID: 101124
Name: David
Surname: Jones
Age: 32
Contact: 1234567890
Address: Illinois
Designation: Project Manager

Pass arguments to a class method

So far, we were mentioning the 'self' keyword inside a class function to let the program know that it belongs to that class. Now we will pass additional arguments to a class method. To do so, we must declare corresponding parameters while declaring the function.

As you saw earlier, we stored the values of the variables inside the "__init__()" function. Here, we'll pass those values as arguments to the "__init__()" function instead of following the previous method.


class Employee:
def __init__(self, id, name, surname, age, contact, address, desig):
self.id = id
self.name = name
self.surname = surname
self.age = age
self.contact = contact
self.address = address
self.designation = desig

def display(self):
print("ID:",obj.id)
print("Name:",obj.name)
print("Surname:",obj.surname)
print("Age:",obj.age)
print("Contact:",obj.contact)
print("Address:",obj.address)
print("Designation:",obj.designation)

obj = Employee("101124", "David", "Jones", 32, \
"1234567890", "Illinois", "Project Manager")

obj.display()

Output

ID: 101124
Name: David
Surname: Jones
Age: 32
Contact: 1234567890
Address: Illinois
Designation: Project Manager

Private members

In Object-Oriented Programming, "private members" refer to the attributes or methods of a class that are only accessible within the class itself, and not from outside the class. Private members are often used to encapsulate implementation details and prevent external code from directly accessing or modifying the internal state of an object.

To declare private members in python, we use a naming convention that indicates that the member is intended to be private (In C++ we use a visibility modifier such as "private"), such as a leading underscores (two underscores before).


class MyClass:
def __init__(self):
self.__private_attribute = 48

def __private_method(self):
print("This is a private method")

def public_method(self):
print("This is a public method")
self.__private_method()

obj = MyClass()
obj.public_method()

Output

This is a public method
This is a private method

In this example, the attribute "__private_attribute" and method "__private_method()" are declared as private using the leading underscores naming convention. This means that they are intended to be used only within the 'MyClass' class and are not intended to be accessed from outside the class.

The "public_method", on the other hand, is a method that can be accessed from outside the class. It can also access the private attribute and method of the class, but only from within the class.

Using private members in Object-Oriented Programming helps to enforce the principle of encapsulation, which means that the internal state of an object is hidden from external code and can only be accessed and modified through a defined set of methods or functions. This helps to create more robust and maintainable code by preventing unintended access or modification of the internal state of objects.

Encapsulation

Data encapsulation in Object-Oriented Programming (OOP) refers to the practice of hiding the implementation details of an object's data (attributes) and behavior (methods) from the outside world and providing a public interface for interacting with the object.

Encapsulation is one of the fundamental principles of OOP and is used to protect the internal state of an object from being modified by code outside of the object's methods. This helps to ensure that the object's data is always in a valid state and helps to prevent unintended side effects that can occur when code directly manipulates an object's data.

To achieve encapsulation in OOP, attributes of an object are typically marked as private or protected, which means they can only be accessed and modified by the object's own methods or by methods of derived classes (in the case of protected attributes). The object's methods provide a public interface for interacting with the object, which can be used to read and modify the object's attributes in a controlled and safe way.

For example, consider a class representing a bank account. The balance attribute should be kept private to prevent outside code from directly modifying it, which could lead to inconsistencies in the account's state. Instead, the class should provide public methods such as "deposit" and "withdraw" that can update the balance in a controlled way.

Here is an example of Encapsulation in Python.


class BankAccount:
def __init__(self):
# __balance is private
self.__balance = 0

def deposit(self, amount):
self.__balance += amount

def withdraw(self, amount):
if self.__balance >= amount:
self.__balance -= amount
else:
print("Insufficient funds")

def get_balance(self):
return self.__balance

obj = BankAccount()
obj.deposit(54000)
obj.deposit(28000)

print(obj.get_balance())

obj.withdraw(8250)

print(obj.get_balance())

Output

82000
73750

In this example, the double underscores before the attribute name makes it private. This means that the balance attribute cannot be accessed or modified directly from outside the "BankAccount" class. Instead, the class provides public methods such as "deposit", "withdraw", and "get_balance" that can access and modify the balance attribute in a controlled way.

Encapsulation is important for creating robust and maintainable code, as it helps to ensure that objects are used in a consistent and safe way, and allows for changes to the implementation details of an object's behavior without affecting the code that uses the object's public interface.

Abstraction

Data abstraction in object-oriented programming (OOP) refers to the process of simplifying complex systems by breaking them down into smaller, more manageable parts, and modeling those parts as objects.

Abstraction is one of the fundamental principles of OOP and is used to reduce the complexity of a system by hiding unnecessary details and focusing on the essential features. Abstraction allows programmers to create models of real-world systems that can be easily understood, maintained, and modified over time.

In OOP, abstraction is achieved by defining classes that encapsulate both data (attributes) and behavior (methods), and by creating objects that represent instances of those classes. The objects provide a simplified, abstract view of the system, and their methods provide a way to interact with the system in a controlled and meaningful way.

Abstraction is important for creating modular and reusable code, as it allows for the creation of classes and objects that can be used in a wide range of contexts without requiring detailed knowledge of their internal workings. Abstraction also helps to reduce the coupling between different parts of a system, which makes it easier to modify and extend the system over time without causing unintended side effects.

Here is an example, that represents Abstraction in Python.


class Adder:
def __init__(self):
self.__total = 0

def addNumber(self, number):
self.__total += number

def getTotal(self):
return self.__total

if __name__ == "__main__":
obj = Adder()

obj.addNumber(14)
obj.addNumber(22)
obj.addNumber(15)

print(f"Total: {obj.getTotal()}")

In the above example, we declared the "__total" variable as private which is accessible only through its own class member functions, and in the end, we applied this technique to print the result.

Overall, data abstraction is a powerful technique for creating robust and maintainable code, and it is a key feature of modern programming languages and frameworks that support OOP.

Inheritance

In object-oriented programming (OOP), inheritance is a mechanism that allows a new class (the "derived" or "subclass") to be based on an existing class (the "base" or "super class"). Inheritance enables a derived class to inherit the properties and behaviors of the base class, while also allowing the derived class to override or extend those properties and behaviors as needed.

When a subclass is derived from a super class, it automatically inherits all the public and protected properties and methods of the super class. The subclass can then modify or extend those properties and methods, or add new ones of its own.

Consider a real-life situation in which a child inherits some of the parent's characteristics at birth. For example, a mother has blonde hair. That's why her child also has blonde hair. There could be many more such examples.

Now come to the point of overriding. The child can modify the derived characteristics (For example, he/she can change the color of his/her hair from blonde to something else) or add more.

Inheritance is the mechanism through which we can translate this real-life relationship between child and mother into programming.

Here is an example, that represents Inheritance in Python.

# Parent Class
class Mother:
def __init__(self):
self.name = "Julia"
self.age = "35"
self.height = "5.4"
self.blood = "O+"
self.hair = "blonde"
self.eye = "brown"

# Child Class
class Daughter(Mother):
def __init__(self):
# Inheriting an instance of Mother Class
Mother.__init__(self)

self._name = "Nancy"
self._age = "8"
self._height = "4.1"
self._blood = "A+"

# Storing a field value of Mother Class
self._hair = self.hair
self._eye = "black"

def printResult(self):
print("Name:",self._name)
print("Age:",self._age)
print("Height:",self._height)
print("Blood:",self._blood)
print("Hair:",self._hair)
print("Eye:",self._eye)

obj = Daughter()
obj.printResult()

Output

Name: Nancy
Age: 8
Height: 4.1
Blood: A+
Hair: blonde
Eye: black 

Inheritance is a key concept in OOP because it enables code reuse and promotes a hierarchical structure for classes, making the code easier to understand and maintain. It also allows for polymorphism, which means that objects of the derived class can be treated as objects of the base class, making the code more flexible and adaptable.

Polymorphism

In object-oriented programming (OOP), polymorphism refers to the ability of different objects to be treated as if they are of the same type, even if they are actually instances of different classes. Polymorphism allows code to be written in a way that is more generic and flexible, making it easier to reuse and maintain.

There are two main types of polymorphism in OOP: Compile-time (or static) polymorphism and Runtime (or dynamic) polymorphism. 

Compile-time Polymorphism is achieved through Method Overloading (More details below), which allows multiple methods in a class to have the same name but different parameters. When the method is called, the compiler selects the appropriate version of the method based on the arguments passed. 

Runtime Polymorphism, on the other hand, is achieved through Method Overriding (More details below), which occurs when a subclass provides its own implementation of a method that is already defined in the super class. When the method is called on an object of the subclass, the overridden method in the subclass is executed instead of the original method in the super class.

Here is an example of Polymorphism in Python.


# 'Bird' is Super class
class Bird:
def Parent(self):
print("Parent: Bird Class")

def flying(self):
print("Most birds can fly")

# Subclass of 'Bird' class
class Owl(Bird):
def __init__(self):
print("This is Owl Class")

def flying(self):
print("Owls can fly")

# Subclass of 'Bird' class
class Penguin(Bird):
def __init__(self):
print("This is Penguin Class")

def flying(self):
print("Penguins can't fly")

obj = Owl()
obj.flying()
obj.Parent()

In the above example, "Bird" is the parent class. "Owl" and "Penguin" are child or sub classes and each subclass provides its own implementation of a method that is already defined in the parent or super class "Bird".

Polymorphism is a powerful concept in OOP because it enables code to be written in a more generic and reusable way, making it easier to maintain and update. It also allows for the creation of more flexible and adaptable code, which can be particularly useful in large, complex software systems.

Method Overloading

Method Overloading is a feature in object-oriented programming that allows a class to have multiple methods with the same name but different parameters. This means that you can define several methods with the same name in a class, as long as each method takes different types or numbers of parameters.

When a method is called, the compiler determines which method to execute based on the number and types of arguments passed. The appropriate method is selected at compile-time and the appropriate method code is executed at runtime.

For example, consider a class that has a method called "calculateArea". This method can be overloaded to accept different parameters such as the length and width of a rectangle, the radius of a circle, or the base and height of a triangle. Each method would have the same name "calculateArea", but a different parameter list.

Here is an example, that represents Method Overloading in Python.


class Calculation:
def addition(self, a, b, c = 0):
print(a + b + c)

obj = Calculation()
obj.addition(5, 7)
obj.addition(4, 18, 6)

Output

12
28

In the above example, the function "addition" is called two times with different arguments. On the other side, the function is implemented just once with their parameters where c is set to zero (0) by default.

Another way to implement the method is to use *args. This is the most efficient way because in this case, you can pass any number of arguments to the function instead of following the constraint. Here is an example.


class Calculation:
def addition(self, *args):
result = 0
for num in args:
result += num

print(result)

obj = Calculation()
obj.addition(5, 7)
obj.addition(4, 18, 6)
obj.addition(6, 19, 8, 4)

Output

12
28
37

Note that method overloading is different from method overriding, where a subclass provides a specific implementation of a method that is already provided by its parent class.

Method Overriding

Method Overriding is a feature in object-oriented programming that allows a subclass to provide a specific implementation of a method that is already provided by its parent class. In other words, when a method in a subclass has the same name, return type, and parameter list as a method in its parent class, the subclass method is said to override the parent class method.

When a method is called on an instance of the subclass, the method in the subclass is executed instead of the method in the parent class. This allows the subclass to provide its own implementation of the method that is more specific to its needs.


class Animal:
def makeSound(self):
print("Animals make sound")

class Cat(Animal):
def makeSound(self):
print("Meow")

obj = Cat()
obj.makeSound()

Output

Meow

In the above example, the "Cat" class extends the "Animal" class and overrides its "makeSound()" method. When the "makeSound()" method is called on an instance of the "Cat" class, the overridden method in the "Cat" class is executed instead of the method in the "Animal" class.

Summary

In this tutorial, our topic of discussion was Object-Oriented Programming through Python Programming. We covered every key concept related to Object-Oriented Programming and many more.

A quick recap:

  • Concept of Object-Oriented Programming
  • Defining classes and objects in python (with various programming examples)
  • Encapsulation
  • Abstraction
  • Inheritance
  • Polymorphism
  • Method Overloading
  • Method Overriding

Object-Oriented programming enables code to be written in a more generic and reusable way, making it easier to maintain and update. This allows for more flexible and adaptable code generation, which can be particularly useful in large, complex software systems.

The invention of Object-Oriented Programming is a boon to the world of programming but did you know OOP is a more recent development than procedural programming? It was first developed in the early 1970s, but it did not become popular until the late 1980s.

Subhankar Rakshit

Meet Subhankar Rakshit, a Computer Science postgraduate (M.Sc.) and the creator of PySeek. Subhankar is a programmer, specializes in Python language. With a several years of experience under his belt, he has developed a deep understanding of software development. He enjoys writing blogs on various topics related to Computer Science, Python Programming, and Software Development.

Post a Comment (0)
Previous Post Next Post