Object Oriented Programming

What is OOP?

Object-oriented programming (OOP) is a programming paradigm based on the concept of objects, which can contain data and code. [Data in the form of fields (often known as attributes or properties), and code in the form of procedures (often known as methods).]

OOP focuses on the objects that developers want to manipulate rather than the logic required to manipulate them. This approach to programming is well-suited for programs that are large, complex and actively updated or maintained. This includes programs for manufacturing and design, as well as mobile applications; for example, OOP can be used for manufacturing system simulation software.

The organization of an object-oriented program also makes the method beneficial to collaborative development, where projects are divided into groups. Additional benefits of OOP include code reusability, scalability and efficiency.

What is the structure of OOP?

Classes
Classes are user-defined data types that act as the blueprint for individual objects, attributes and methods.
  • Objects are defined by a class.

  • A class is the blueprint of an object.

  • Multiple objects can be created from a class.

  • Constructor is used to create an object.

Objects
Objects are instances of a class created with specifically defined data. Objects can correspond to real-world objects or an abstract entity. When class is defined initially, the description is the only object that is defined.
Methods
Methods are functions that are defined inside a class that describe the behaviors of an object. Each method contained in class definitions starts with a reference to an instance object. Additionally, the subroutines contained in an object are called instance methods. Programmers use methods for reusability or keeping functionality encapsulated inside one object at a time.
  • Methods- What it can do
Attributes
Attributes are defined in the class template and represent the state of an object. Objects will have data stored in the attributes field. Class attributes belong to the class itself.
  • Attribute- Characteristics

What are the main principles of OOP?

  1. Encapsulation

  2. Inheritance

  3. Abstraction

  4. Polymorphism

Encapsulation

  • _ convention is used to show private attributes.
class Car:
    def __init__(self, make, model, year):
        self._make = make  # _make is a convention for a protected attribute
        self._model = model # _model is a convention for a protected attribute
        self._year = year # _year is a convention for a protected attribute
        self._odometer = 0  # This attribute is private

    # Public method to get the make of the car
    def get_make(self):
        return self._make
    # Public method to set the make of the car
    def set_make(self, make):
        self._make = make

    # Public method to get the model of the car
    def get_model(self):
        return self._model
    # Public method to set the model of the car
    def set_model(self, model):
        self._model = model

    # Public method to get the year of the car
    def get_year(self):
        return self._year
    # Public method to set the year of the car
    def set_year(self, year):
        self._year = year

    # Public method to get the odometer reading
    def get_odometer(self):
        return self._odometer
    # Public method to update the odometer reading
    def update_odometer(self, miles):
        if miles >= 0:
            self._odometer += miles
        else:
            print("Odometer cannot be rolled back.")

# Create an instance of the Car class
my_car = Car("Toyota", "Camry", 2023)

# Accessing public methods to get and set attributes
print(f"Make: {my_car.get_make()}")
print(f"Model: {my_car.get_model()}")
print(f"Year: {my_car.get_year()}")

# Updating the odometer using a public method
my_car.update_odometer(100)
print(f"Odometer Reading: {my_car.get_odometer()} miles")

# Attempting to update the odometer directly (this will not work)
# my_car._odometer = 50  # This is not the preferred way

# Using public methods to set attributes
my_car.set_make("Honda")
my_car.set_model("Accord")
my_car.set_year(2022)

# Displaying the updated information
print(f"\nUpdated Make: {my_car.get_make()}")
print(f"Updated Model: {my_car.get_model()}")
print(f"Updated Year: {my_car.get_year()}")

In this corrected code, I've added a property and setter for the breed attribute, and I've modified the way the breed attribute is set to be consistent with how it's defined in the init method. Now, the code should work as expected, printing the breed and color of the dog.

Inheritance

  • Inheritance allows child classes / sub-classes to inherit the characteristics of the parent class.

  • Child Classes can extend the Parent Classes.

  • Inheritance is to build relationships

  • Not for has-a relationship

# Base class (Parent class)
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def make_sound(self):
        pass  # This method will be overridden by subclasses


# Derived class (Subclass)
class Dog(Animal):
    def __init__(self, name, age, breed):
        # Call the constructor of the base class using super()
        super().__init__(name, age)
        self.breed = breed

    # Override the make_sound method
    def make_sound(self):
        return "Woof! Woof!"

    def fetch(self):
        return f"{self.name} is fetching the ball."


# Another derived class (Subclass)
class Cat(Animal):
    def __init__(self, name, age, color):
        # Call the constructor of the base class using super()
        super().__init__(name, age)
        self.color = color

    # Override the make_sound method
    def make_sound(self):
        return "Meow!"

    def climb_tree(self):
        return f"{self.name} is climbing a tree."


# Create instances of the derived classes
dog_instance = Dog("Buddy", 3, "Golden Retriever")
cat_instance = Cat("Whiskers", 2, "Gray")

# Accessing attributes and methods of the base class
print(f"{dog_instance.name} is {dog_instance.age} years old.")
print(f"{cat_instance.name} is {cat_instance.age} years old.")

# Accessing attributes and methods of the derived classes
print(dog_instance.make_sound())  # Calls the overridden method in Dog class
print(cat_instance.make_sound())  # Calls the overridden method in Cat class

# Accessing unique methods of each subclass
print(dog_instance.fetch())
print(cat_instance.climb_tree())

In this example, the Animal class is the base class with a basic constructor and a method make_sound that will be overridden by its subclasses. The Dog and Cat classes are derived from the Animal class and inherit its attributes and methods. Each subclass also has its own unique methods (fetch for Dog and climb_tree for Cat). The super() function is used to call the constructor of the base class within the constructor of each subclass.

Abstraction

from abc import ABC, abstractmethod

# Abstract class (contains abstract methods)
class Shape(ABC):
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

# Concrete implementation of the abstract class
class Circle(Shape):
    def __init__(self, name, radius):
        super().__init__(name)
        self.radius = radius

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

    def perimeter(self):
        return 2 * 3.14 * self.radius

# Concrete implementation of the abstract class
class Square(Shape):
    def __init__(self, name, side):
        super().__init__(name)
        self.side = side

    def area(self):
        return self.side * self.side

    def perimeter(self):
        return 4 * self.side

# Creating instances of concrete classes
circle_instance = Circle("Circle", 5)
square_instance = Square("Square", 4)

# Accessing abstract methods through instances
print(f"{circle_instance.name} - Area: {circle_instance.area()}, Perimeter: {circle_instance.perimeter()}")
print(f"{square_instance.name} - Area: {square_instance.area()}, Perimeter: {square_instance.perimeter()}")

In this example, the Shape class is an abstract class with two abstract methods: area and perimeter. Any concrete class derived from Shape must provide implementations for these abstract methods. The Circle and Square classes are concrete implementations of the abstract class Shape. They provide specific implementations for the area and perimeter methods.

Instances of the concrete classes can be created, and their methods can be called, providing a level of abstraction that allows you to work with shapes without worrying about the specific details of how the area and perimeter are calculated for each shape.

Polymorphism

# Base class
class Animal:
    def speak(self):
        pass  # This method will be overridden by subclasses

# Derived class 1
class Dog(Animal):
    def speak(self):
        return "Woof! Woof!"

# Derived class 2
class Cat(Animal):
    def speak(self):
        return "Meow!"

# Function demonstrating polymorphism
def animal_sound(animal):
    return animal.speak()

# Create instances of the derived classes
dog_instance = Dog()
cat_instance = Cat()

# Demonstrate polymorphism by calling the same function with different types of objects
print(animal_sound(dog_instance))  # Outputs: Woof! Woof!
print(animal_sound(cat_instance))  # Outputs: Meow!

In this example, the Animal class is a base class with a method speak that will be overridden by its subclasses. The Dog and Cat classes are derived from the Animal class and provide their own implementations of the speak method.

The animal_sound function demonstrates polymorphism by accepting any object of the Animal class or its subclasses. When called with a Dog or Cat instance, it invokes the appropriate speak method based on the actual type of the object.

Polymorphism allows you to write more generic and reusable code, as you can work with objects at a higher level of abstraction (e.g., treating all animals the same way) without worrying about their specific implementations.

0
Subscribe to my newsletter

Read articles from Manoja Abewardhana directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Manoja Abewardhana
Manoja Abewardhana