OOP
Object-Oriented Programming (OOP) is a programming paradigm based on the concept of objects. Objects are instances of classes, which can have attributes (data) and methods (functions). OOP helps organize code, making it reusable and easier to understand.
Key Concepts of OOP
- Class: A blueprint for creating objects. It defines attributes and methods.
- Object: An instance of a class.
- Attributes: Variables that belong to a class or object.
- Methods: Functions defined inside a class that operate on the object’s attributes.
- Inheritance: Allows a class to inherit attributes and methods from another class.
- Polymorphism: Allows methods in different classes to have the same name but behave differently.
- Encapsulation: Hides the internal details of an object and only exposes necessary parts.
- Abstraction: Hides complex implementation details and shows only essential features.
Defining a Class
A class is created using the class keyword.
Example:
class Person: # Class attribute species = "Human" # Constructor method to initialize object attributes def __init__(self, name, age): self.name = name self.age = age # Method to display details def greet(self): print(f"Hello, my name is {self.name} and I am {self.age} years old.")
Creating an Object
An object is created by calling the class like a function.
Example:
# Creating an object of the Person class person1 = Person("Alice", 25) # Accessing attributes and methods print(person1.species) # Output: Human print(person1.name) # Output: Alice person1.greet() # Output: Hello, my name is Alice and I am 25 years old.
Inheritance
Inheritance allows a class (child) to inherit the attributes and methods of another class (parent).
Example:
# Parent class class Animal: def __init__(self, name): self.name = name def speak(self): print(f"{self.name} makes a sound.") # Child class class Dog(Animal): def speak(self): print(f"{self.name} barks.") # Creating objects animal = Animal("Generic Animal") animal.speak() # Output: Generic Animal makes a sound. dog = Dog("Buddy") dog.speak() # Output: Buddy barks.
Polymorphism
Polymorphism allows methods to have the same name but behave differently depending on the class.
Example:
class Cat(Animal): def speak(self): print(f"{self.name} meows.") # Using polymorphism animals = [Dog("Buddy"), Cat("Kitty")] for animal in animals: animal.speak()
Encapsulation
Encapsulation is achieved by making attributes private (using _ or __) and providing public methods to access or modify them.
Example:
class BankAccount: def __init__(self, owner, balance): self.owner = owner self.__balance = balance # Private attribute def deposit(self, amount): self.__balance += amount print(f"Deposited {amount}. New balance: {self.__balance}") def withdraw(self, amount): if amount > self.__balance: print("Insufficient funds!") else: self.__balance -= amount print(f"Withdrew {amount}. New balance: {self.__balance}") # Creating an object account = BankAccount("Alice", 1000) account.deposit(500) # Output: Deposited 500. New balance: 1500 account.withdraw(2000) # Output: Insufficient funds!
Abstraction
Abstraction is implemented using abstract classes, which are classes that cannot be instantiated directly. Use the abc module for abstraction.
Example:
from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass @abstractmethod def perimeter(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height def perimeter(self): return 2 * (self.width + self.height) # Creating an object rect = Rectangle(5, 10) print("Area:", rect.area()) # Output: Area: 50 print("Perimeter:", rect.perimeter()) # Output: Perimeter: 30
Special Methods (Magic Methods)
Special methods (also called magic or dunder methods) allow you to define how objects behave with operators like +, ==, etc.
Example:
class Point: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Point(self.x + other.x, self.y + other.y) def __str__(self): return f"Point({self.x}, {self.y})" p1 = Point(1, 2) p2 = Point(3, 4) p3 = p1 + p2 print(p3) # Output: Point(4, 6)
Class Methods
- Class methods are methods that belong to the class, not the instance of the class.
- They are defined using the @classmethod decorator.
- Instead of self, they take cls as the first parameter, which represents the class itself.
- Useful for modifying or accessing class-level attributes.
Example:
class Person: count = 0 # Class attribute @classmethod def increment_count(cls): cls.count += 1 # Using the class method Person.increment_count() print(Person.count) # Output: 1
When to Use Class Methods?
- When you need to work with class attributes or create alternative constructors.
Static Methods
- Static methods do not depend on the instance or class.
- They are defined using the @staticmethod decorator.
- They behave like regular functions but are defined inside a class for logical grouping.
Example:
class Math: @staticmethod def add(a, b): return a + b # Using the static method result = Math.add(3, 5) print(result) # Output: 8
When to Use Static Methods?
- When you need utility methods that don’t depend on class or instance data.
Data Classes
- Introduced in Python 3.7 via the dataclasses module.
- Data classes simplify creating classes used for storing data.
- They automatically generate methods like __init__, __repr__, and __eq__.
How to Create a Data Class?
Use the @dataclass decorator.
Example:
from dataclasses import dataclass @dataclass class Person: name: str age: int # Creating an object of the data class p = Person("Alice", 30) print(p.name) # Output: Alice print(p) # Output: Person(name='Alice', age=30)
When to Use Data Classes?
When you need simple classes to store and manage data without writing boilerplate code.