Classes & Objects
The foundation of OOP: blueprints and instances. Learn how to define classes, create objects, and work with attributes and methods.
Navigation
Master Object-Oriented Programming in Python with this comprehensive guide. Covers classes, inheritance, encapsulation, magic methods, ABCs, composition, dataclasses, MRO, and best practices with clean, Pythonic examples.
Advertisement
01 — OOP FUNDAMENTALS
Python's object-oriented programming model is elegant, powerful, and Pythonic. Unlike languages that force OOP, Python lets you choose your paradigm, but its core — everything from integers to modules — is built on objects. This guide walks you through OOP in Python with clarity and practical examples, covering classes, objects, inheritance, polymorphism, encapsulation, magic methods (dunders), class methods, static methods, abstract base classes, composition, and best practices used by professional Python developers.
Understanding OOP is essential for writing maintainable, scalable Python code. Whether you're building web applications, data processing pipelines, or AI systems, object-oriented design helps you model real-world entities and relationships naturally.
The foundation of OOP: blueprints and instances. Learn how to define classes, create objects, and work with attributes and methods.
Create class hierarchies, override behavior, and write flexible code that works with objects of different types seamlessly.
Protect internal state, control attribute access, and customize behavior with dunder methods to make objects Pythonic.
Advertisement
02 — CLASSES & OBJECTS
A class is a blueprint for creating objects. An object is an instance of a class, with its own data and behavior. Python's class statement defines both attributes (data) and methods (functions belonging to the class).
03 — ENCAPSULATION
Python uses conventions rather than strict access control. A single underscore _protected means "internal use". Double underscore __private triggers name mangling to avoid accidental overriding. Properties with @property provide controlled access, a Pythonic alternative to getters/setters.
04 — INHERITANCE & POLYMORPHISM
Inheritance creates hierarchies; polymorphism allows interchangeable objects. Python supports multiple inheritance with clean MRO (Method Resolution Order). Subclasses can override or extend parent behavior.
05 — MAGIC METHODS
Magic methods define behavior for built-in operations: __str__, __len__, __add__, __eq__ and many more. They let your objects integrate seamlessly with Python syntax.
06 — CLASS VS STATIC METHODS
@staticmethod doesn't receive self or cls — acts like a regular function but lives in the class namespace. @classmethod receives the class as first argument, useful for factory methods.
07 — ABSTRACT BASE CLASSES
The abc module lets you define abstract methods that must be implemented by subclasses. This enforces a contract and is ideal for frameworks.
08 — BEST PRACTICES
Writing Pythonic OOP code means embracing Python's idioms rather than forcing patterns from other languages. Here are the key principles professional Python developers follow:
@property instead of explicit getter/setter methods__slots__ to save memory and speed up attribute accessisinstance() checksCamelCase for classes, snake_case for methods@classmethod for alternative constructors (factory methods)09 — COMPOSITION
Composition means building complex objects from simpler, focused components. Instead of inheriting from a class to gain its behavior, you hold a reference to it. This creates has-a relationships and results in more flexible, loosely coupled code that is easier to test and maintain.
10 — DATACLASSES
Python 3.7+ introduced @dataclass — a decorator that auto-generates __init__, __repr__, and __eq__ from annotated fields. Dataclasses dramatically reduce boilerplate for classes that primarily hold data, while remaining fully compatible with regular class methods and inheritance.
11 — __repr__ vs __str__
__str__ is the human-friendly string, used by print(). __repr__ is the developer-friendly representation, ideally one you could paste back into Python to recreate the object. Always define __repr__ at minimum — if __str__ is absent, Python falls back to __repr__.
12 — METHOD RESOLUTION ORDER
Python resolves method calls in multiple-inheritance hierarchies using the C3 linearisation algorithm, also known as Method Resolution Order (MRO). Understanding MRO helps you predict which parent method gets called and design mixins safely. Use ClassName.__mro__ or help(ClassName) to inspect it.
Advertisement
FAQ
A class method receives the class as the first argument (cls) and can access or modify class state. A static method does not receive any special first argument and behaves like a regular function but belongs to the class namespace. Use @classmethod for factory methods, @staticmethod for utility functions.
Use composition when you need a "has-a" relationship rather than "is-a". Composition offers more flexibility, looser coupling, and often leads to more maintainable code. Favor composition over deep inheritance hierarchies to avoid complexity and promote code reuse.
Magic methods (dunder methods) are special methods with double underscores, like __init__, __str__, __add__. They define behavior for built-in operations and allow your objects to integrate seamlessly with Python syntax, enabling operator overloading and customizing object behavior.
__str__ returns a human-readable string, used by print(). __repr__ returns an unambiguous developer-facing string, ideally one that could recreate the object. Always implement __repr__ at minimum — if __str__ is missing, Python falls back to __repr__. In interactive sessions, __repr__ is shown by default.
Use @dataclass when your class primarily holds data with little or no complex logic. Dataclasses auto-generate __init__, __repr__, and __eq__, eliminating boilerplate. They also support immutability via frozen=True, ordering with order=True, and default factories. For classes with significant behavior or complex initialization, a regular class is often clearer.
MRO defines the order in which Python searches base classes when resolving a method call in a multiple-inheritance scenario. Python uses the C3 linearisation algorithm to compute a consistent, predictable lookup order. You can inspect it with ClassName.__mro__ or the mro() method. Understanding MRO is essential for safely composing mixins and avoiding the "diamond problem".
@property turns a method into a read-only attribute. Combine it with @attribute.setter and @attribute.deleter to add write and delete access. This lets you expose a clean attribute interface while running validation or computation under the hood, without breaking existing code that already uses the attribute. It is the Pythonic replacement for explicit getters and setters.
Get new guides, code deep-dives, and Python insights delivered to your inbox.
Join readers learning Python & AI.