Object-Oriented Programming (OOP) is a programming paradigm based on the concept of “objects” — containers that bundle data and behavior together. Python, while supporting multiple paradigms, is an object-oriented language at its core. Understanding OOP is essential for writing structured, maintainable, and scalable code.
In this chapter, we will explore OOP fundamentals in Python: classes, objects, attributes, methods, constructors, inheritance, polymorphism, encapsulation, abstraction, and real-world design patterns. You’ll also learn best practices, common pitfalls, and how to structure large applications using OOP.
Table of Contents
1. What is Object-Oriented Programming?
OOP organizes code around objects and classes instead of functions and logic.
Key Concepts:
- Class: Blueprint for creating objects
- Object: Instance of a class
- Attribute: Data associated with an object
- Method: Function associated with an object
Real-world Analogy:
A class is like a car blueprint, and an object is the actual car built from that blueprint.
2. Creating a Class
Syntax:
class ClassName:
# class body
Example:
class Dog:
def bark(self):
print("Woof!")
3. Creating Objects (Instances)
my_dog = Dog()
my_dog.bark() # Output: Woof!
Each object has its own identity and can access class methods.
4. The __init__()
Method (Constructor)
Special method that runs automatically when an object is created.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
Creating Objects:
d = Dog("Buddy", 3)
print(d.name, d.age)
5. Instance and Class Attributes
Instance Attributes:
Defined using self
inside the __init__()
method.
Class Attributes:
Defined outside all methods, shared among all instances.
class Dog:
species = "Canine"
6. Instance Methods
Most common type, defined with self
as the first parameter.
class Dog:
def speak(self):
print(f"My name is {self.name}")
7. Class Methods and Static Methods
Class Methods:
Defined with @classmethod
, receive class (cls
) instead of instance.
@classmethod
def from_string(cls, data):
name, age = data.split("-")
return cls(name, int(age))
Static Methods:
Defined with @staticmethod
, behave like regular functions but belong to the class namespace.
@staticmethod
def greet():
print("Welcome!")
8. Encapsulation
Protecting internal object data by making attributes private or protected.
class Account:
def __init__(self, balance):
self.__balance = balance # private
def get_balance(self):
return self.__balance
9. Inheritance
Allows a class to inherit attributes and methods from another class.
Syntax:
class ChildClass(ParentClass):
# additional members
Example:
class Animal:
def speak(self):
print("Animal sound")
class Cat(Animal):
def speak(self):
print("Meow")
super()
Function:
Calls parent class method.
class Dog(Animal):
def __init__(self):
super().__init__()
10. Polymorphism
Ability to use a common interface for different data types.
class Bird:
def speak(self):
print("Chirp")
class Human:
def speak(self):
print("Hello")
for entity in [Bird(), Human()]:
entity.speak() # Different outputs
11. Abstraction
Hiding implementation details and showing only the interface.
Abstract Base Classes:
Use abc
module.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
12. Special Methods (Dunder Methods)
Double underscore (dunder) methods customize class behavior.
Method | Purpose |
---|---|
__str__ | String representation |
__repr__ | Official representation |
__len__ | Length of object |
__eq__ | Equality comparison |
__add__ | Overload + operator |
Example:
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return self.title
13. Composition vs Inheritance
Composition:
Class contains other class objects.
class Engine:
pass
class Car:
def __init__(self):
self.engine = Engine()
Use composition when a “has-a” relationship makes more sense than “is-a”.
14. Real-World Example: Employee Management System
class Employee:
def __init__(self, name, id):
self.name = name
self.id = id
def show(self):
print(f"Name: {self.name}, ID: {self.id}")
class Manager(Employee):
def __init__(self, name, id, department):
super().__init__(name, id)
self.department = department
def show(self):
super().show()
print(f"Department: {self.department}")
15. Best Practices
- Use
self
consistently - Favor composition over deep inheritance chains
- Encapsulate data properly
- Use class methods for alternative constructors
- Use
__str__()
for meaningful object representation - Write unit tests for class methods
16. Common Mistakes
- Forgetting
self
in instance methods - Overriding methods without calling
super()
- Using mutable default arguments in
__init__
- Confusing class and instance variables
Debugging Tips:
- Use
type()
anddir()
to inspect objects - Add
print()
statements to trace method calls
17. Exercises
- Create a class
Rectangle
with width, height and area methods. - Implement inheritance with
Vehicle
andCar
classes. - Use
@classmethod
to instantiate an object from a string. - Implement a
BankAccount
class with encapsulated balance. - Create a polymorphic function
speak()
used byDog
,Cat
, andBird
classes. - Use the
abc
module to define and implement an abstract base class.
18. Summary
Object-Oriented Programming is a cornerstone of software development. With classes, objects, encapsulation, inheritance, polymorphism, and abstraction, Python allows developers to model real-world entities in code. Mastering OOP leads to cleaner, more maintainable, and reusable code — a vital skill for any serious Python developer.
✅ Next Chapter: Modules and Packages – Learn how to organize your Python codebase using modular and scalable structures.