Introduction to OOP
This guide teaches Object-Oriented Programming (OOP) concepts to complete beginners using Python.
What is OOP?
Object-Oriented Programming is a programming paradigm that organizes code into objects.
An object combines data (attributes) and behavior (methods) together — just like things in the real world.
Core Benefits:
More organized and readable code
Easier to reuse and maintain
Better models real-world entities
Four Main Pillars of OOP:
Encapsulation (hiding/securing data and methods)
Containment (Has-a relationship)
Inheritance (Is-a relationship)
Polymorphism (Many forms)
—
OOP Basics - Classes and Objects
classDiagram
class Dog {
String name
int age
bark()
get_info()
}
A Class is a blueprint. An Object is an actual instance created from that blueprint.
OOP in Python
class Dog:
def __init__(self, name, age):
self.name = name # Attribute
self.age = age # Attribute
def bark(self):
print(f"{self.name} says Woof!")
def get_info(self):
return f"{self.name} is {self.age} years old."
# Creating objects
dog1 = Dog("Buddy", 5)
dog2 = Dog("Luna", 3)
dog1.bark()
print(dog2.get_info())
—
Encapsulation
Encapsulation means bundling data (attributes) and methods (functions) together inside a class, and protecting the internal data from direct outside access.
Think of it like a capsule or a remote control:
You only see the buttons (public interface)
The complex wiring inside is hidden (protected)
This helps prevent accidental damage to the data and makes code easier to manage.
classDiagram
class Player {
int _health
int __score
take_damage()
add_score()
get_health()
}
note for Player "Private attributes
(start with _ or __)
are hidden from outside"
Why is Encapsulation Important?
Protects data from invalid changes
Makes code more secure and maintainable
Allows you to change internal implementation without breaking other code
Encapsulation in Python
class Player:
def __init__(self, name):
self.name = name
self._health = 100 # Protected attribute (convention)
self.__score = 0 # Private attribute (name mangling)
# Public method - controlled way to change health
def take_damage(self, damage: int):
if damage > 0:
self._health -= damage
print(f"{self.name} took {damage} damage!")
if self._health <= 0:
print(f"{self.name} has been defeated!")
self._health = 0
# Public method to safely get health
def get_health(self) -> int:
return self._health
# Public method to add score
def add_score(self, points: int):
if points > 0:
self.__score += points
def get_score(self) -> int:
return self.__score
# Using the class
player1 = Player("Alex")
player1.take_damage(30)
print(f"Health: {player1.get_health()}") # Safe access
player1.add_score(250)
print(f"Score: {player1.get_score()}")
# Direct access to private attribute is discouraged:
# print(player1.__score) # This will not work easily
Containment
Containment means one class contains another class. It represents a “Has-a” relationship.
Example: A Car has an Engine.
classDiagram
class Engine {
int horsepower
start()
}
class Car {
String make
String model
Engine engine
start_car()
}
Car o-- Engine : contains
Containment in Python
class Engine:
def __init__(self, horsepower):
self.horsepower = horsepower
def start(self):
print(f"Engine ({self.horsepower}hp) is roaring to life!")
class Car:
def __init__(self, make, model, horsepower):
self.make = make
self.model = model
self.engine = Engine(horsepower) # Containment
def start_car(self):
print(f"Starting {self.make} {self.model}")
self.engine.start()
my_car = Car("Tesla", "Model 3", 350)
my_car.start_car()
—
Inheritance
Inheritance allows a new class to inherit properties and methods from an existing class. It represents an “Is-a” relationship.
classDiagram
Animal <|-- Dog
Animal <|-- Cat
class Animal {
String name
eat()
sleep()
}
class Dog {
String breed
bark()
}
class Cat {
meow()
}
Inheritance in Python
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} is eating.")
def sleep(self):
print(f"{self.name} is sleeping.")
class Dog(Animal): # Inheritance
def __init__(self, name, breed):
super().__init__(name) # Call parent constructor
self.breed = breed
def bark(self):
print(f"{self.name} says Woof!")
class Cat(Animal): # Inheritance
def meow(self):
print(f"{self.name} says Meow!")
my_dog = Dog("Max", "Golden Retriever")
my_cat = Cat("Whiskers")
my_dog.eat() # Inherited method
my_dog.bark() # Dog's own method
—
Polymorphism
Polymorphism means “many forms”. It allows objects of different classes to be used interchangeably if they share a common interface.
The same method name can behave differently depending on the object.
classDiagram
Shape <|-- Rectangle
Shape <|-- Circle
class Shape {
area()
}
class Rectangle {
area()
}
class Circle {
area()
}
Polymorphism in Python
class Shape:
def area(self):
return 0
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self): # Overriding
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self): # Overriding
return 3.14159 * self.radius ** 2
# Polymorphism in action
shapes = [
Rectangle(5, 3),
Circle(4),
Rectangle(6, 2)
]
for shape in shapes:
print(f"Area = {shape.area():.2f}")
Key Point: Even though all are different shapes, we can call .area() on any of them uniformly.
Summary
OOP → Organize code using classes and objects
Encapsulation → Prevents certain attributes and methods to be used externally (outside the class’s own methods)
Containment → “Has-a” relationship (one object owns another)
Inheritance → “Is-a” relationship (code reuse through hierarchy)
Polymorphism → Same method, different behavior
These four concepts together make your programs more powerful, flexible, and easier to maintain.