Object-Oriented Programming (OOP) is a programming paradigm that uses Classes and Objects to organize code, making it reusable, modular, and easy to scale.
- Class: A blueprint or template for creating objects.
- Object: An instance of a class.
class Dog:
pass
my_dog = Dog() # Instantiating a new objectThe __init__() function is a special method (constructor) that runs automatically every time a new object is created from a class.
self: Represents the current instance of the class and is used to access instance variables.
class Dog:
def __init__(self, name):
self.name = name # Instance variable- Instance Variables: Unique to each object instance, defined inside
__init__(). - Class Variables: Shared by all instances of a class, defined directly in the class body.
class Dog:
species = "Canis lupus familiaris" # Class variable (shared)
def __init__(self, name):
self.name = name # Instance variable (unique)
dog1 = Dog("Buddy")
dog2 = Dog("Max")
print(dog1.species) # Output: Canis lupus familiaris
print(dog1.name) # Output: Buddy
print(dog2.name) # Output: MaxDunder (double underscore) methods are special built-in methods.
__str__: Controls whatprint(object)orstr(object)displays (user-friendly string).__repr__: Controls the official string representation of the object (often used for debugging).
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def __str__(self):
return f"Dog named {self.name} ({self.breed})"
def __repr__(self):
return f"Dog(name='{self.name}', breed='{self.breed}')"
my_dog = Dog("Buddy", "Labrador")
print(my_dog) # Output via __str__: Dog named Buddy (Labrador)
print(repr(my_dog)) # Output via __repr__: Dog(name='Buddy', breed='Labrador')Inheritance allows a Child class to inherit attributes and methods from a Parent class. Use the super() function to call the parent class's constructor or methods.
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
return f"{self.name} is eating."
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # Calling parent's __init__
self.breed = breed
def bark(self):
return f"{self.name} says Woof!"
my_dog = Dog("Buddy", "Labrador")
print(my_dog.eat()) # Output: Buddy is eating. (Inherited method)
print(my_dog.bark()) # Output: Buddy says Woof!@classmethod: Receives the class (cls) as the first argument instead of the instance (self). Can access class variables and modify class state.@staticmethod: Does not receiveselforcls. It behaves like a normal function utility attached to the class namespace.
class Calculator:
@staticmethod
def add(a, b):
return a + b
print(Calculator.add(5, 10)) # Output: 15 (called directly on Class)The @property decorator allows you to access a method like a normal attribute, while still enabling validation checks when setting values.
class Circle:
def __init__(self, radius):
self._radius = radius # _ prefix implies internal/protected attribute
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
c = Circle(5)
print(c.radius) # Output: 5 (accessed as an attribute, not c.radius())
c.radius = 10 # Valid update
# c.radius = -1 # Raises ValueError: Radius cannot be negativeEvery instance method must accept self as its first parameter to represent the object calling it. If you forget to include self in the method signature, Python will raise a TypeError when you call the method on an instance.
# Incorrect:
class Dog:
def bark():
return "Woof!"
# Correct:
class Dog:
def bark(self):
return "Woof!"