Object-Oriented Programming (OOP) is a paradigm that groups related data (attributes) and behaviour (methods) into reusable blueprints called classes. An individual object built from a class is an instance. Python’s OOP model is highly flexible — classes are first-class objects, type checking is dynamic, and you have precise control over encapsulation and inheritance. This page walks you through everything from basic class definitions to advanced features like abstract base classes and class variables.
Classes and Instances
A class is a blueprint. An instance is a concrete object built from that blueprint.
Key concepts demonstrated above:
- Class:
Student
- Object (instance):
student1
- Attributes:
name, age
- Methods:
introduce(), study()
- Constructor:
__init__() initialises instance attributes
Attributes and Methods
Instance Methods
Functions inside a class that operate on a specific instance. They always accept self as the first parameter:
Class Attributes and @classmethod
Class attributes are shared by all instances. Class methods accept cls instead of self and are used for factory methods or modifying class-level state:
Static Methods (@staticmethod)
Static methods do not receive self or cls. They are plain utility functions grouped inside the class namespace:
Type Checking: type() vs isinstance()
Prefer isinstance() in most code. It respects the inheritance hierarchy and is the Pythonic way to check types.
Inheritance
Inheritance lets a child class inherit attributes and methods from a parent class, extending or overriding behaviour as needed:
Passing Arguments via __init_subclass__
You can pass configuration to a parent class at class-definition time using __init_subclass__:
This runs at import time — before any instances are created — and is the pattern used by ORMs like SQLAlchemy.
Encapsulation
Encapsulation restricts direct access to internal state to prevent accidental modification:
- Protected (
_name): Convention only — Python does not enforce it.
- Private (
__pin): Triggers name mangling (_ClassName__pin), making direct external access raise an AttributeError.
Properties
Properties let you attach validation logic to attribute access using @property getters and setters:
Abstract Classes
Abstract Base Classes (ABCs) define an interface contract — subclasses must implement every abstract method:
Introspection
Python provides built-in tools to inspect objects at runtime:
Type Annotations, Class Variables, and Instance Variables
Understanding what each declaration means is critical when working with Python, Pydantic, and dataclasses.
Type Annotation Only
A bare annotation describes the expected type but does not create an attribute:
Accessing Product.name raises AttributeError.
Instance Variables
Created when a value is assigned to self inside __init__:
Each object gets its own independent copy.
Class Variables
Shared by every instance. Annotate with ClassVar to make intent explicit:
Behaviour Across Frameworks
Always use ClassVar when declaring class-level constants. It prevents Pydantic and dataclasses from accidentally treating the attribute as a model field.