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