Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around objects and classes. It emphasizes encapsulation, inheritance, polymorphism, and abstraction. Below, we’ll break down the concepts and provide code examples in Python.
A class is a blueprint for creating objects. It defines the structure and behavior of objects. An object is an instance of a class.
class Dog:
# Class attribute (shared by all instances)
species = "Canis familiaris"
# Instance attributes (unique to each instance)
def __init__(self, name, age):
self.name = name
self.age = age
# Creating objects (instances of the class)
dog1 = Dog("Buddy", 5)
dog2 = Dog("Max", 3)
print(dog1.name) # Output: Buddy
print(dog2.age) # Output: 3
print(dog1.species) # Output: Canis familiaris
Attributes are variables that belong to an object or class. Methods are functions that belong to an object or class.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
# Method
def bark(self):
return f"{self.name} says woof!"
dog1 = Dog("Buddy", 5)
print(dog1.bark()) # Output: Buddy says woof!
Encapsulation is the bundling of data (attributes) and methods that operate on the data into a single unit (class). It also restricts direct access to some of an object's components, which is achieved using private attributes (e.g., _ or __ prefix in Python).
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
account = BankAccount(1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
# print(account.__balance) # Error: AttributeError (private attribute)
Inheritance allows a class (child) to inherit attributes and methods from another class (parent). It promotes code reuse.
# Parent class
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "Sound"
# Child class
class Dog(Animal):
def speak(self):
return f"{self.name} says woof!"
dog = Dog("Buddy")
print(dog.speak()) # Output: Buddy says woof!
Polymorphism allows objects of different classes to be treated as objects of a common superclass. It can be achieved through method overriding or duck typing.
class Cat(Animal):
def speak(self):
return f"{self.name} says meow!"
animals = [Dog("Buddy"), Cat("Whiskers")]
for animal in animals:
print(animal.speak())
# Output:
# Buddy says woof!
# Whiskers says meow!
A constructor (__init__) initializes an object when it is created. A destructor (__del__) is called when an object is destroyed.
class Person:
def __init__(self, name):
self.name = name
print(f"{self.name} created.")
def __del__(self):
print(f"{self.name} destroyed.")
person = Person("Alice")
# Output: Alice created.
del person # Output: Alice destroyed.
self Keywordself refers to the current instance of the class. It is used to access attributes and methods of the object.
class Car:
def __init__(self, brand):
self.brand = brand
def display(self):
print(f"This car is a {self.brand}.")
car = Car("Toyota")
car.display() # Output: This car is a Toyota.
Class methods are bound to the class and not the instance. They use the @classmethod decorator and take cls as the first parameter. Static methods are utility methods that don't depend on the instance or class. They use the @staticmethod decorator.
class Math:
@classmethod
def add(cls, a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
print(Math.add(2, 3)) # Output: 5
print(Math.multiply(2, 3)) # Output: 6
Single inheritance: A class inherits from one parent class. Multiple inheritance: A class inherits from multiple parent classes.
# Single inheritance
class Bird(Animal):
def speak(self):
return f"{self.name} says chirp!"
# Multiple inheritance
class Bat(Animal, Bird):
pass
bat = Bat("Batty")
print(bat.speak()) # Output: Batty says chirp!
super()Method overriding allows a child class to provide a specific implementation of a method already defined in the parent class. super() is used to call a method from the parent class.
class Animal:
def speak(self):
return "Sound"
class Dog(Animal):
def speak(self):
return super().speak() + " but specifically woof!"
dog = Dog()
print(dog.speak()) # Output: Sound but specifically woof!
Abstract classes cannot be instantiated and often contain one or more abstract methods (methods without implementation). Interfaces define a contract for classes (Python uses abstract classes for this purpose).
from abc import ABC, abstractmethod
# Abstract class
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
circle = Circle(5)
print(circle.area()) # Output: 78.5
Data hiding restricts access to certain attributes or methods using private variables (__ prefix).
class Secret:
def __init__(self, secret):
self.__secret = secret
def reveal(self):
return self.__secret
s = Secret("Confidential")
# print(s.__secret) # Error: AttributeError
print(s.reveal()) # Output: Confidential
Property decorators (@property, @<attribute>.setter, @<attribute>.deleter) allow controlled access to attributes.
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero is not possible.")
self._celsius = value
temp = Temperature(25)
print(temp.celsius) # Output: 25
temp.celsius = 30
print(temp.celsius) # Output: 30