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
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
Methods
- Methods- What it can do
Attributes
- Attribute- Characteristics
What are the main principles of OOP?
Encapsulation
Inheritance
Abstraction
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.
Subscribe to my newsletter
Read articles from Manoja Abewardhana directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by