Object-Oriented Programming in Python
Master classes, objects, the __init__ constructor, methods, and attributes to build well-structured Python programs.
What is Object-Oriented Programming?
Object-Oriented Programming (OOP) is a programming paradigm that organises code around objects rather than functions and logic alone. An object bundles together data (attributes) and behaviour (methods) into a single unit.
In Python, you define a class as a blueprint and then create instances (objects) from that class. This promotes code reuse, modularity, and easier maintenance of large programs.
class Dog:
"""A simple Dog class."""
def __init__(self, name, breed):
self.name = name # instance attribute
self.breed = breed # instance attribute
def bark(self):
return f"{self.name} says Woof!"
# Creating objects (instances)
my_dog = Dog("Buddy", "Labrador")
print(my_dog.bark()) # Buddy says Woof!
The __init__ method is called automatically when a new object is created. self refers to the current instance.
Attributes and Methods
Attributes store data about an object. They can be instance attributes (unique to each object) or class attributes (shared across all instances). Methods are functions defined inside a class that operate on the object's data.
class BankAccount:
interest_rate = 0.05 # class attribute (shared)
def __init__(self, owner, balance=0):
self.owner = owner # instance attribute
self.balance = balance # instance attribute
def deposit(self, amount):
self.balance += amount
return f"Deposited ${amount}. New balance: ${self.balance}"
def withdraw(self, amount):
if amount > self.balance:
return "Insufficient funds"
self.balance -= amount
return f"Withdrew ${amount}. Remaining: ${self.balance}"
def apply_interest(self):
self.balance += self.balance * BankAccount.interest_rate
return f"Interest applied. New balance: ${self.balance:.2f}"
acc = BankAccount("Alice", 1000)
print(acc.deposit(500)) # Deposited $500. New balance: $1500
print(acc.apply_interest()) # Interest applied. New balance: $1575.00
Encapsulation and Privacy Conventions
Encapsulation means restricting direct access to some of an object's internal data. In Python, a single underscore prefix (_name) signals that an attribute is intended for internal use, while a double underscore (__name) triggers name mangling for stronger privacy.
class Student:
def __init__(self, name, grade):
self.name = name # public
self._grade = grade # protected (convention)
self.__student_id = id(self) # private (name mangled)
def get_id(self):
"""Getter method provides controlled access."""
return self.__student_id
@property
def grade(self):
"""Property decorator for clean access."""
return self._grade
@grade.setter
def grade(self, value):
if 0 <= value <= 100:
self._grade = value
else:
raise ValueError("Grade must be between 0 and 100")
s = Student("Maya", 95)
print(s.grade) # 95 (uses property getter)
s.grade = 88 # uses property setter
print(s.get_id()) # controlled access to private attr
Key Vocabulary
Class
A blueprint or template that defines the structure and behaviour of objects. Defined with the class keyword.
Object / Instance
A concrete entity created from a class. Each instance has its own set of attribute values.
__init__
The constructor method that runs automatically when a new object is instantiated. Used to initialise attributes.
self
A reference to the current instance of the class. It must be the first parameter of every instance method.
Worked Examples
Create a Rectangle class with area and perimeter methods
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def __str__(self):
return f"Rectangle({self.width} x {self.height})"
r = Rectangle(5, 3)
print(r) # Rectangle(5 x 3)
print(r.area()) # 15
print(r.perimeter()) # 16
Step 1: Define the class and __init__ to accept width and height.
Step 2: Create area() and perimeter() methods that compute using stored attributes.
Step 3: The __str__ dunder method gives a readable string when printing the object.
Build a Playlist class using a list attribute
class Playlist:
def __init__(self, name):
self.name = name
self.songs = [] # mutable default via __init__
def add_song(self, song):
self.songs.append(song)
def remove_song(self, song):
if song in self.songs:
self.songs.remove(song)
def show(self):
print(f"Playlist: {self.name}")
for i, song in enumerate(self.songs, 1):
print(f" {i}. {song}")
my_list = Playlist("Study Vibes")
my_list.add_song("Lo-fi Beats")
my_list.add_song("Chill Waves")
my_list.show()
# Playlist: Study Vibes
# 1. Lo-fi Beats
# 2. Chill Waves
Key point: Initialise mutable attributes (like lists) inside __init__, never as default parameter values, to avoid sharing across instances.
Implement a Counter with class methods and static methods
class Counter:
total_counters = 0 # class attribute
def __init__(self, start=0):
self.value = start
Counter.total_counters += 1
def increment(self):
self.value += 1
@classmethod
def get_total(cls):
return f"Total counters created: {cls.total_counters}"
@staticmethod
def is_positive(n):
return n > 0
c1 = Counter()
c2 = Counter(10)
c1.increment()
print(c1.value) # 1
print(Counter.get_total()) # Total counters created: 2
print(Counter.is_positive(5)) # True
@classmethod receives the class (cls) instead of the instance. Useful for accessing class-level data.
@staticmethod does not receive the instance or class. It is a utility function that logically belongs to the class.
Knowledge Check
Select the correct answer for each question. Click "Check Answer" to see if you are right.
Question 1
What is the purpose of the __init__ method in a Python class?
Question 2
What does self refer to inside a class method?
Question 3
Which of the following correctly creates an instance of class Car with a make parameter?
Question 4
What is a class attribute in Python?
Question 5
What does the @property decorator allow you to do?
Key Concepts Summary
- ● A class is a blueprint; an object is an instance created from that blueprint.
- ● __init__ is the constructor that initialises attributes when an object is created.
- ● self always refers to the current instance and must be the first parameter of instance methods.
- ● Class attributes are shared; instance attributes belong to individual objects.
- ● Encapsulation uses naming conventions and properties to control access to internal data.