🐍 Python OOP — Concise Yet Complete
Notes
📌 What is OOP?
Object-Oriented Programming (OOP) is a paradigm where code is organized around objects —
entities that bundle data (attributes) and behaviors (methods) together.
🧱 Core OOP Concepts in Python
1. Class
A blueprint for creating objects.
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} says woof!")
2. Object
An instance of a class.
dog1 = Dog("Bruno")
dog1.bark() # Output: Bruno says woof!
3. __init__ Method
• Special method (constructor)
• Runs automatically when an object is created
• Initializes instance attributes
def __init__(self, name):
self.name = name
4. self Keyword
• Refers to the current object instance
• Passed automatically to instance methods
• Used to access attributes and methods inside the class
class Cat:
def __init__(self, name):
self.name = name # assigns name to the current object
def speak(self):
print(self.name, "says meow")
cat1 = Cat("Milo")
cat1.speak() # Internally: Cat.speak(cat1)
✅ self is not a keyword — just a naming convention (but must be first parameter).
💾 Memory Concepts in OOP
🔹 Object Lifecycle
• When you do obj = ClassName(), Python:
• Allocates memory on the heap for the object
• Stores the reference (obj) on the stack
🔹 Memory Table Example
car1 = Car("Toyota", 2020)
Element Location What it does
car1 Stack Variable name (reference)
Car object Heap Holds attributes (brand, year)
self Stack Refers to the object inside methods
🔹 Class vs Instance Attributes
class A:
x = [] # class attribute (shared)
def __init__(self):
self.y = [] # instance attribute (unique)
a1 = A()
a2 = A()
a1.x.append(1)
a1.y.append(1)
print(a2.x) # [1] — shared across all instances
print(a2.y) # [] — different for each instance
🧠 4 Pillars of OOP
1. Encapsulation
• Bundles data + functions
• Controls access using public (self.x) and private (self.__x) attributes
class Account:
def __init__(self, balance):
self.__balance = balance # private
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
2. Inheritance
• Create new class from existing class
• Reuse and extend behavior
class Animal:
def speak(self):
print("Some sound")
class Dog(Animal):
def speak(self):
print("Bark")
3. Polymorphism
• Same method name behaves differently for different classes
def make_sound(animal):
animal.speak()
make_sound(Dog()) # Bark
make_sound(Animal()) # Some sound
4. Abstraction
• Hide complex logic, expose only essentials
• Use abstract base classes
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
🔁 OOP vs Functional Programming
Feature Functional OOP
Structure Pure functions Classes and Objects
State Avoids shared state Encapsulated in objects
Ideal For Data transformation pipelines Modeling real-world systems
Reuse Functions & higher-order funcs Inheritance & polymorphism
✅ Pros of OOP
• Clear and modular structure
• Easier maintenance and scalability
• Code reuse through inheritance
• Real-world modeling is intuitive
❌ Cons of OOP
• More boilerplate than functional style
• Can lead to overengineering
• Harder to debug in very deep class hierarchies
• Slightly more memory due to object overhead
🧪 Quick Example
class Car:
def __init__(self, brand, year):
self.brand = brand
self.year = year
def start(self):
print(f"{self.brand} from {self.year} is starting...")
car1 = Car("Tesla", 2024)
car1.start()
🧩 Summary
• Use classes to bundle data + behavior
• Use self to refer to the object instance
• Objects live in heap memory, and variables (like obj) refer to them from the stack
• Understand the 4 pillars: encapsulation, inheritance, polymorphism, abstraction
Would you like practice questions, a real-world project, or a diagram to visualize memory and
object behavior?