Object-oriented programming (OOP) organizes software around objects that combine state and behavior. Python supports classes and objects, inheritance, composition and polymorphism. OOP can make a system easier to model when several pieces of data and related operations belong together, but simple functions and data structures are often clearer for small tasks.
Class versus object
A class defines the structure and behavior of a kind of object. An object is a particular instance of that class. For example, Student could be a class, while a student record with a particular name and ID is one object.
The __init__ method initializes instance attributes when an object is created. The first parameter is conventionally named self and refers to the current instance.
class Student:
def __init__(self, name: str, student_id: str):
self.name = name
self.student_id = student_id
def describe(self) -> str:
return f'{self.name} ({self.student_id})'
student = Student('Alex', 'S102')
print(student.describe())Encapsulation: keep data and operations together
Encapsulation groups related data and behavior and defines how an object should be used. Python does not enforce private instance variables in the same way as some languages, but naming conventions and properties can communicate intended access and validate values.
Prefer clear interfaces that preserve valid object state. Avoid adding getters and setters for every attribute automatically; use methods or properties when they provide a real benefit.
Inheritance and composition
Inheritance allows a specialized class to reuse or override behavior from a base class. It is appropriate when the subtype genuinely fits the base type's contract. Overusing inheritance can create tightly coupled classes that are difficult to change.
Composition builds an object by giving it references to other objects that handle specific responsibilities. It is often simpler when one object uses another without being a subtype of it.
class Person:
def __init__(self, name: str):
self.name = name
class TeachingAssistant(Person):
def role(self) -> str:
return 'Teaching assistant'Polymorphism: same interface, different behavior
Polymorphism means code can work with different object types through a shared interface or behavior. In Python, this often relies on duck typing: if an object supports the methods the code needs, it can be used without requiring a particular explicit base class.
For example, several report objects might expose a render() method. A function that calls render() can work with each of them, provided each implementation follows the expected contract.
When OOP helps and when it does not
OOP is helpful when the domain has entities with state, rules and lifecycle behavior, or when multiple implementations need a common interface. Examples include payment methods, game entities, report exporters and data models.
For a small one-time calculation, a function may be simpler. Good design is not the maximum number of classes; it is a structure that makes responsibilities and changes easier to understand.
- Give each class a clear responsibility.
- Keep methods cohesive and readable.
- Prefer composition over complicated inheritance where practical.
- Test public behavior rather than relying on internal details.
- Do not create classes solely to wrap one trivial function without a clear reason.
Understanding the concept is the first step.
Explore related resources or learn about subject-focused tutoring designed to help you understand the material.
