Mastering OOP in Python
Object-Oriented Programming organises code around objects — bundles of data and behaviour. Python is fully object-oriented: every value is an object, every function is callable. This chapter introduces the four pillars — encapsulation, inheritance, polymorphism, abstraction — and the syntax you'll use every day.
Overview
Encapsulation
Group related data and behaviour into a single tidy unit.
Inheritance
Reuse and extend existing classes — DRY principle in action.
Polymorphism
One interface, many behaviours — uniform code over diverse types.
Abstraction
Expose what an object does; hide how it does it.
Dunder Methods
Make your objects work with print, equality, len, and arithmetic operators.
Syntax
- Define a class with
class Name:. - Add an initializer with
def __init__(self, ...):. - Methods take
selfas the first parameter. - Create objects by calling the class:
obj = Name(args).
class Car:
def __init__(self, brand, speed=0):
self.brand = brand
self.speed = speed
def accelerate(self, delta):
self.speed += delta
def __str__(self):
return f"{self.brand} @ {self.speed} km/h"
c = Car("Tesla")
c.accelerate(50)
print(c)
Detailed Explanation
- Classes & Objects: A class describes a category of things; an object is one instance of that class.
Caris a class,my_teslais an object. - Encapsulation: Bundling data and behaviour together. Internal details are hidden behind methods; users interact through a clean API.
- Inheritance: A subclass reuses and extends a parent class.
ElectricCar(Car)automatically has every method ofCarplus its own. - Polymorphism: Many forms of one interface. Different classes can implement
__str__orarea()their own way, but callers use them uniformly. - Abstraction: Hide complexity. Users call
car.start()without caring how ignition works internally.abc.ABC+@abstractmethodenforce abstraction. - Special methods:
__init__,__str__,__repr__,__eq__,__len__let your objects integrate with Python's built-in syntax (print, equality, len).
Code Examples
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
return f"{self.name} says Woof!"
d = Dog("Bruno")
print(d.bark())
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
b1 = Book("Python", 499)
b2 = Book("Django", 699)
print(b1.title, b1.price)
print(b2.title, b2.price)
Django 699
class Animal:
def speak(self):
return "generic sound"
class Cat(Animal):
def speak(self):
return "Meow"
print(Animal().speak())
print(Cat().speak())
Meow
class Square:
def __init__(self, s): self.s = s
def area(self): return self.s * self.s
class Circle:
def __init__(self, r): self.r = r
def area(self): return 3.14 * self.r ** 2
for shape in [Square(4), Circle(5)]:
print(shape.area())
78.5
class Account:
def __init__(self, balance):
self.__balance = balance
def deposit(self, amt):
self.__balance += amt
def get_balance(self):
return self.__balance
a = Account(1000)
a.deposit(500)
print(a.get_balance())
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, o):
return Vector(self.x + o.x, self.y + o.y)
def __str__(self):
return f"({self.x},{self.y})"
print(Vector(1,2) + Vector(3,4))
Real-World Use Cases
User Models
Each user becomes a class with fields and behaviours.
E-commerce Products
Product, Cart, Order — natural class hierarchy.
Game Engines
Sprites, scenes, and game-states modelled as objects.
Hardware Drivers
Each device is an object exposing read/write methods.
Banking Apps
Account, Transaction, Customer — encapsulated business rules.
Notifications
Email, SMS, Push as subclasses of a common Notifier interface.
Notes & Pro Tips
- Use
selffor the current instance;clsfor the class itself in classmethods. - Naming: classes
CamelCase, methods/attrssnake_case. - Single underscore prefix
_x= soft private; double underscore__x= name-mangled. - Prefer composition (has-a) over deep inheritance (is-a) for flexibility.
- Use
@dataclassfor plain data classes — auto-generates__init__,__repr__,__eq__. - Override
__repr__for debugging;__str__for user display.
Common Mistakes
- Forgetting
self: first parameter of every instance method must beself. - Confusing class and instance attributes: setting on the class affects all instances.
- Mutable class-level defaults: share state across instances — surprising bugs.
- Overusing inheritance: deep hierarchies are brittle; favour composition.
- Accessing private:
obj.__namefrom outside is name-mangled — by design. - Missing
__init__: objects can't store instance state without it.
Practice Problems
- Problem 1: Create a class
Rectanglewith attributes length and width and methodsarea()andperimeter(). - Problem 2: Build a class
BankAccountwith deposit, withdraw, and balance methods. - Problem 3: Create classes
Shape,Square, andCircledemonstrating polymorphism. - Problem 4: Build a class hierarchy:
Employee→Manager→Director. - Problem 5: Make a Vector class supporting addition, subtraction, and string representation.
- Problem 6: Create a
Studentclass with private marks and methods to compute grade.
Interview Questions
- Q1. What are the four pillars of OOP?
- Q2. What is the difference between a class and an object?
- Q3. Explain inheritance with an example.
- Q4. What is polymorphism and how does Python implement it?
- Q5. What is the role of
selfin Python classes? - Q6. Difference between
__str__and__repr__?
Frequently Asked Questions
- Q1: What is OOP?
Object-Oriented Programming is a paradigm that organises code as objects — bundles of data and behaviour modelled on real entities. - Q2: What are the four pillars of OOP?
Encapsulation, Inheritance, Polymorphism, Abstraction. - Q3: Do I need OOP for every program?
No. Small scripts may be purely functional. OOP shines when you have multiple related entities or want long-term maintainability. - Q4: What is composition vs inheritance?
Inheritance creates 'is-a' relationships; composition creates 'has-a' relationships by holding other objects as attributes. Composition is more flexible. - Q5: What is a dunder method?
Methods like __init__, __str__, __add__ that Python calls implicitly. They let your objects integrate with operators and built-ins. - Q6: Why use private attributes?
To protect internal state from accidental external modification and signal which API is stable.
Summary
Object-Oriented Programming structures code around objects — modelling real-world entities and their interactions. With encapsulation, inheritance, polymorphism, and abstraction you can build large, reusable, maintainable systems. Python's clean OOP syntax — classes, self, dunder methods, dataclasses — makes the paradigm both accessible and powerful.
Continue Learning
Previous
Go to Previous Chapter