Pic Credit @analyticsindiamag

Object-Oriented Programming (OOP) is a programming paradigm that revolves around the concept of “objects.” An object is a self-contained unit that consists of data (attributes) and the methods (functions) that operate on that data. OOP aims to model real-world entities and their interactions in a structured and organized manner, making it easier to design, develop, and maintain software systems.

What is Class and Object?

A class is a blueprint or template that defines the structure and behavior of objects. Objects are instances of classes. Classes define attributes (data) and methods (functions) that operate on the data.

class Student: # this is class
pass

student = Student() # student is object
student.name = "Ajay"
student.age = 25
print("student name is {}, and age is {}.".format(student.name, student.age))

What is constructor in python(__init__ method)?

In Python, the __init__ method is a special method (also called a "dunder" method, short for "double underscore") that is used to initialize the attributes of an object when an instance of a class is created. It is the constructor method of a class and is automatically called when you create a new object of that class.

The __init__ method is used to set up the initial state of an object by assigning values to its attributes. It takes at least one parameter, self, which refers to the instance of the object being created. Additional parameters can be provided to initialize the attributes with specific values.

What is self in Python class?

In Python, self is a special variable that refers to the instance of the class. It is used as the first parameter in method definitions within a class. When you create an object (instance) of a class and call a method on that object, Python automatically passes the object itself as the self parameter to the method. This allows you to access and manipulate the attributes and methods of that specific instance within the method.

What is inheritance in Python?

inheritance is a technique by which we can get the properties(attributes and methods) of parent or base class in the child or derived class.

The main idea behind inheritance is to promote code reusability and to establish a hierarchy of classes, where common attributes and behaviors can be defined in a higher-level class and then specialized or extended in lower-level classes. This allows you to avoid redundant code and promotes a more organized and modular approach to programming.

class Animal:
def __init__(self, name) -> None:
self.name = name

def legs(self):
return "{} has 4 legs.".format(self.name)

def get_name(self):
return self.name

def sound(self):
pass # This is meant to be overridden in derived classes

class Camels(Animal):
def __init__(self, name) -> None:
super().__init__(name)

def sound(self):
return "make grunt sound."


camel = Camels("Camel")
print(camel.legs())
print(camel.sound())

In the above example Animal class is the parent or base class and Camels is child or derived class. They inherit name attribute and sound method but can provide their own implementation of sound method.

Type of inheritance in Python

There are many types of inheritance, here i am mentioning few

  1. Single inheritance

In this inheritance there is one parent class and there is one child class which inherit parent class.

class Parent:
pass

class Child(Parent):
pass

2. Multiple inheritance

In this inheritance one child class inherits multiple parent class. This allow child class to get properties (attributes and methods) from different parent classes.

class Parent1:
pass

class Parent2:
pass

class Child(Parent1, Parent2):
pass

3. Multilevel inheritance

In this inheritance child class has multi level of parent classes means derived class inherits from base class another class inherits from derived class and so on. This creates a chain of inheritance.

class Parent1:
pass

class Child1(Parent1):
pass

class Child2(Child1):
pass

Explain 4 pillars of OOP in python

In Python, the four pillars of object-oriented programming (OOP) are a subset of the SOLID principles that are particularly relevant to Python’s dynamic and flexible nature. These four pillars help guide the design and implementation of Python code in an object-oriented manner. The four pillars are:

  1. Encapsulation: Encapsulation is the practice of bundling data (attributes) and the methods (functions) that operate on that data into a single unit called a class. This concept helps hide the internal details of an object’s implementation and provides a clear interface for interacting with the object. In Python, encapsulation is achieved by using private and protected access modifiers and naming conventions (e.g., _variable and __variable) to indicate the intended level of access.
class BankAccount:
def __init__(self, account_number, balance):
self._account_number = account_number # Protected attribute
self.__balance = balance # Private attribute

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

def withdraw(self, amount):
if amount > 0 and amount <= self.__balance:
self.__balance -= amount

def get_balance(self):
return self.__balance

# Protected method
def _internal_processing(self):
print("Internal processing...")

# Creating an instance of the BankAccount class
account = BankAccount("123456789", 1000)

# Accessing protected attributes (not enforced, just a convention)
print("Account Number:", account._account_number)

# Accessing private attributes (name mangling is applied)
# Note: This is not recommended in practice; it's just to demonstrate access
print("Balance:", account._BankAccount__balance)

# Using public methods to interact with the object's data
account.deposit(500)
account.withdraw(200)

print("Balance:", account.get_balance())

# Calling a protected method (not enforced, just a convention)
account._internal_processing()

2. Abstraction: Abstraction involves simplifying complex reality by modeling classes based on the essential properties and behaviors they possess. Abstract classes and interfaces allow you to define a common interface that concrete classes can implement. Python supports abstraction through abstract base classes (defined using the abc module) and abstract methods. These provide a way to ensure that certain methods are implemented in subclasses, promoting a clear and consistent interface.

from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def area(self):
pass

class Circle(Shape):
def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14159 * self.radius * self.radius

class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height

def area(self):
return self.width * self.height

# You cannot instantiate an abstract class directly
# shape = Shape() # This would raise an error

circle = Circle(5)
rectangle = Rectangle(4, 6)

print("Circle area:", circle.area()) # Output: Circle area: 78.53975
print("Rectangle area:", rectangle.area()) # Output: Rectangle area: 24

3. Inheritance: Inheritance allows you to create a new class that inherits attributes and methods from an existing class. This enables you to build a hierarchy of classes where common behaviors are defined in higher-level classes and specialized behaviors are defined in lower-level classes. Python supports both single and multiple inheritance, allowing a class to inherit from one or more parent classes. Inheritance helps in code reuse and creating relationships between classes.

4. Polymorphism: Polymorphism is the ability of different classes to be treated as instances of a common superclass. This allows objects of different classes to be used interchangeably based on their shared interface. In Python, polymorphism is achieved through method overriding and method overloading. Method overriding allows a subclass to provide a specific implementation of a method inherited from a superclass, and method overloading involves defining multiple methods with the same name but different parameter lists.

class Animal:
def make_sound(self):
pass

class Dog(Animal):
def make_sound(self):
return "Woof!"

class Cat(Animal):
def make_sound(self):
return "Meow!"

def animal_sounds(animal):
return animal.make_sound()

# Creating instances of different classes
dog = Dog()
cat = Cat()

# Calling the same method on different objects
print("Dog sound:", animal_sounds(dog)) # Output: Dog sound: Woof!
print("Cat sound:", animal_sounds(cat)) # Output: Cat sound: Meow!

While these are the four main pillars of OOP in Python, it’s important to note that they are closely related to the SOLID principles and work together to promote clean and maintainable code. By understanding and applying these principles, you can create more organized and flexible codebases in Python’s object-oriented paradigm.

What is Composition in Python?

Composition is a fundamental concept in object-oriented programming (OOP) that involves constructing complex objects by combining simpler objects. It is an alternative to inheritance for building relationships between classes. In composition, a class can contain instances of other classes as its attributes, allowing you to create more modular and flexible code.

Composition allows you to create complex objects by assembling them from smaller, reusable components. This can result in a more flexible design compared to relying solely on class hierarchies through inheritance. With composition, you can change the behavior of a class by changing its components, rather than altering the inheritance hierarchy.

class Engine:
def start(self):
print("Engine started")

def stop(self):
print("Engine stopped")

class Car:
def __init__(self):
self.engine = Engine()

def start(self):
print("Car starting...")
self.engine.start()

def stop(self):
print("Car stopping...")
self.engine.stop()

# Creating a Car instance and using composition
my_car = Car()
my_car.start() # Output: Car starting... Engine started
my_car.stop() # Output: Car stopping... Engine stopped

In this example, the Car class contains an instance of the Engine class as its attribute. This composition allows the Car class to reuse the behavior of the Engine class without directly inheriting from it. By using composition, you can achieve a more modular and maintainable design.

Composition is especially useful when you want to build complex objects that can be easily customized by combining different components. It promotes code reuse and flexibility by allowing you to change the behavior of a class by modifying its components or by swapping them out with different components.

Thanks for reading this blog hope you have liked it. If yes then please clap and follow me for more blogs like this.

Did you enjoy this article? If so, get more similar content by subscribing to Coding With Krp Ajay, my YouTube channel!. Thanks for reading. Happy coding.

The same blog will also be available at https://ajaykrp.me/