Object Oriented Programming
✕Introduction to OOP
- Imagine, if you need to write code for FIFA game using function-based approach, it's a big mess.
- OOP allows us to model complex systems in a more organized and modular way using classes and objects.
- Objects represent real-world entities that have attributes and methods.
- Classes are blueprints for creating objects, defining their attributes and behaviors.
- For FIFA game, we can have classes like
Player,Team,Match, etc. Each class can have its own attributes and methods. - Analogy:
Attribute=>Variable,Method=>Function - For class
Player, attributes can bename,age,position,countryand methods can berun(),kick(),pass_ball(),tackle(),shoot(), etc.
Principles of OOP
- There are four main principles that govern object-oriented programming.
- Allows a class to inherit attributes and methods from another class.
- Reduces code duplication and promotes reusability.
- Parent class (superclass) and child class (subclass).
- Supports method overriding and overloading.
- Enables one interface to be used for multiple underlying data types.
- Binds data and methods together within a class.
- Controls access to internal data through public and private methods.
- Protects data from unauthorized access.
- Hides complex implementation details.
- Focuses on what an object does rather than how it does it.
Inheritance
Polymorphism
Encapsulation
Abstraction
Creating Classes and Objects
- We use
classkeyword to define a class (name in CameCase). - Attribute is defined inside
__init__method (constructor) and is accessed usingself. class Car: def __init__(self, brand, model, color="blue"): self.brand = brand self.model = model self.color = color def display_info(self): return f"{self.brand} {self.model} in {self.color}" # Creating an object of Car class my_car = Car("Toyota", "Corolla", "Red")# Constructir automatically calledprint(my_car.display_info()) # Output: Toyota Corolla in Red
Example:
Inheritance
- Allows a new class to inherit attributes and methods from an existing class.
- Type: Single
(A => B), Multiple([A, B] => C), Multilevel(A => B => C). class People: def __init__(self, name, dob): self.name = name self.dob = datetime.strptime(dob, "%Y-%m-%d").date() @property def age(self): return (date.today() - self.dob).days // 365 def is_adult(self): return self.age >= 18 class Employee(People): def __init__(self, name, dob, employee_id, department): super().__init__(name, dob) self.employee_id = employee_id self.department = department # Creating an object of Employee class employee_1 = Employee("Rabindra", "1993-01-01", "E001", "IT") print(employee_1.name) # Inherited from People class print(employee_1.age) # Inherited from People class print(employee_1.employee_id) # Defined in Employee class print(employee_1.is_adult()) # Defined in Employee class
Example:
Polymorphism
- Allows objects of different classes to be treated as objects of a common superclass.
class Nepali: def __init__(self, name): self.name = name def greet(self): return f"Namaste, {self.name}!" class English: def __init__(self, name): self.name = name def greet(self): return f"Hello, {self.name}!" # Polymorphism in action nepali_person = Nepali("Rabindra") english_person = English("John") print(nepali_person.greet()) # Output: Namaste, Rabindra! print(english_person.greet()) # Output: Hello, John!
Example:
Method Overriding
- Allows a subclass to redefine method of a superclass.
class Animal: def speak(self): return "Animal speaks" class Dog(Animal): def speak(self): return "Woof!" # Method overriding in action my_dog = Dog() my_animal = Animal() print(my_dog.speak()) # Output: Woof! print(my_animal.speak()) # Output: Animal speaks
Example:
Operator Overloading
- Allows us to define custom behavior for operators when applied to objects of a class.
+adds when applied on number but concates when applied on string.class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Vector2D(self.x + other.x, self.y + other.y) def __repr__(self): return f"Vector2D({self.x}, {self.y})" # Operator overloading in action v1 = Vector2D(1, 2) v2 = Vector2D(3, 4) v3 = v1 + v2 # Uses the __add__ method print(v3) # Output: Vector2D(4, 6)
Example:
Operation Notation
| Operator | Name | Internal Notation |
|---|---|---|
| + | Addition | __add__ |
| - | Subtraction | __sub__ |
| * | Multiplication | __mul__ |
| / | Division | __truediv__ |
| // | Floor Division | __floordiv__ |
| % | Modulus | __mod__ |
| == | Equality | __eq__ |
| != | Inequality | __ne__ |
| < | Less Than | __lt__ |
| > | Greater Than | __gt__ |
| <= | Less Than or Equal To | __le__ |
| >= | Greater Than or Equal To | __ge__ |
| ** | Exponent | __pow__ |
| print( ) | Print Function | __str__ or __repr__ |
| len( ) | Length Function | __len__ |
Common Operators and their Internal Notation
Encapsulation
- Bundles data and methods that operate on that data within a single unit (class).
- Controls access to internal data through public and private methods.
- Private attributes names are prefixed by
__, method is by_. - Private attributes and methods are not accessible from outside the class.
- Data is protected from unauthorized access and modification.
- Name Mangling Technique can be used to access private attributes outside the class.
class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.__balance = balance# Private attributedef deposit(self, amount): if amount > 0: self.__balance += amount else: print("Deposit amount must be positive") def withdraw(self, amount): if 0 < amount <= self.__balance: self.__balance -= amount else: print("Invalid withdrawal amount") def get_balance(self): return self.__balance# Public method to access private attribute # Encapsulation in actionaccount = BankAccount("Rabindra", 1000) account.deposit(500) account.withdraw(200) print(account.get_balance())# Output: 1300print(account.__balance)# AttributeError: 'BankAccount' object has no attribute '__balance'
Example:
Abstraction
- Hides complex implementation details and shows only essential features of an object.
- Allows users to interact with objects through a simplified interface.
- Promotes modularity and maintainability.
- Example: When we drive a car, we interact with the steering wheel, pedals, and gear shift without needing to understand the complex mechanics of how the engine works.
- We know
.lower()method converts string to lowercase but we don't need to know how it works internally to use it.
