Object-Oriented Programming (OOP) Concepts¶
A beginner-friendly guide to the main Object-Oriented Programming concepts used in Python.
This document explains: - Classes - Objects - Inheritance - Encapsulation - Polymorphism - Abstraction - Composition vs Inheritance
These concepts are heavily used in: - game development - APIs - large Python applications - design patterns - 42 Python modules
What is OOP?¶
Object-Oriented Programming (OOP) is a programming style based on objects.
Objects: - store data - contain behavior - interact with each other
OOP helps organize large projects into reusable and maintainable structures.
Classes¶
A class is a blueprint for creating objects.
Example¶
class Duck:
def quack(self) -> None:
print("Quack!")
Creating an Object¶
duck = Duck()
duck.quack()
Output:
Quack!
Attributes¶
Attributes store data inside objects.
Example¶
class Player:
def __init__(self, name: str) -> None:
self.name = name
init()¶
__init__() is called automatically when an object is created.
It initializes the object attributes.
Creating the Object¶
player = Player("Sara")
Accessing Attributes¶
print(player.name)
Output:
Sara
Methods¶
Methods are functions inside classes.
Example¶
class Enemy:
def attack(self) -> None:
print("Enemy attacks!")
Inheritance¶
Inheritance allows one class to reuse another class.
A child class inherits: - attributes - methods - behavior
from a parent class.
Example¶
class Animal:
def speak(self) -> None:
print("Some sound")
class Dog(Animal):
def bark(self) -> None:
print("Woof!")
Using Inheritance¶
dog = Dog()
dog.speak()
dog.bark()
Output:
Some sound
Woof!
Why Use Inheritance?¶
Inheritance: - reduces duplicated code - improves organization - allows shared behavior
Overriding Methods¶
Child classes can replace parent behavior.
Example¶
class Animal:
def speak(self) -> None:
print("Animal sound")
class Cat(Animal):
def speak(self) -> None:
print("Meow")
Result¶
cat = Cat()
cat.speak()
Output:
Meow
This is called method overriding.
Polymorphism¶
Polymorphism means: - different objects - same method name - different behavior
Example¶
class Dog:
def speak(self) -> None:
print("Woof")
class Cat:
def speak(self) -> None:
print("Meow")
Using Polymorphism¶
animals = [Dog(), Cat()]
for animal in animals:
animal.speak()
Output:
Woof
Meow
Same method: - different implementations
Encapsulation¶
Encapsulation means protecting internal object data.
Objects should control access to their own state.
Protected Attributes¶
Convention:
- _name
Means: - "internal use" - should not be modified directly
Private Attributes¶
Convention:
- __name
Makes direct access harder.
Example¶
class BankAccount:
def __init__(self) -> None:
self.__balance = 0
Access Problem¶
account = BankAccount()
print(account.__balance)
This raises an error.
Controlled Access¶
Use methods instead.
Example¶
class BankAccount:
def __init__(self) -> None:
self.__balance = 0
def deposit(self, amount: int) -> None:
self.__balance += amount
def get_balance(self) -> int:
return self.__balance
Abstraction¶
Abstraction hides implementation details.
The user interacts with: - simple interfaces - not internal complexity
Example¶
class CoffeeMachine:
def make_coffee(self) -> None:
print("Making coffee...")
The user: - presses a button - does not care about internal machine logic
Abstract Classes¶
Python supports abstract classes using ABC.
Example¶
from abc import ABC, abstractmethod
class Creature(ABC):
@abstractmethod
def attack(self) -> None:
pass
Child Implementation¶
class Dragon(Creature):
def attack(self) -> None:
print("Fire breath!")
Why Use Abstraction?¶
Abstraction: - forces structure - improves consistency - defines required behavior
Very useful in: - game systems - factories - APIs - strategy patterns
Composition¶
Composition means: - objects contain other objects
Instead of inheriting behavior.
Example¶
class Engine:
def start(self) -> None:
print("Engine started")
class Car:
def __init__(self) -> None:
self.engine = Engine()
Using Composition¶
car = Car()
car.engine.start()
Composition vs Inheritance¶
Inheritance¶
Represents: - "is-a" relationship
Example:
Dog is an Animal
Composition¶
Represents: - "has-a" relationship
Example:
Car has an Engine
Which is Better?¶
Composition is often safer and more flexible.
Inheritance is useful when: - behavior is truly shared
Composition is useful when: - objects collaborate together
Real 42 Examples¶
Inheritance¶
class Enemy(GameObject):
Composition¶
class Game:
self.player = Player()
Polymorphism¶
enemy.attack()
boss.attack()
Different enemies: - same method - different behavior
Common OOP Benefits¶
- Better code organization
- Reusability
- Easier maintenance
- Cleaner architecture
- Scalability for large projects
Common OOP Problems¶
Bad OOP design may cause: - overly deep inheritance trees - tightly coupled systems - hard debugging - unnecessary complexity
Best Practices¶
- Prefer composition over excessive inheritance
- Keep classes focused on one responsibility
- Use meaningful class names
- Avoid giant classes
- Encapsulate sensitive data
- Use abstraction for shared interfaces
Summary Table¶
| Concept | Meaning |
|---|---|
| Class | Blueprint for objects |
| Object | Instance of a class |
| Inheritance | Reuse behavior from another class |
| Encapsulation | Protect internal data |
| Polymorphism | Same method, different behavior |
| Abstraction | Hide implementation details |
| Composition | Objects containing objects |
Final Notes¶
OOP is one of the most important programming paradigms in Python.
Understanding these concepts helps when building: - games - MLX projects - APIs - parsers - large applications - design pattern systems
Strong OOP foundations make code: - cleaner - safer - easier to scale