Python OOP Masterclass: Classes, Objects, and Inheritance Explained
As applications grow in scope, structuring code procedurally can lead to unmaintainable codebases. Object-Oriented Programming (OOP) provides a structured paradigm that bundles related data (attributes) and behaviors (methods) into reusable blueprints called classes.
This guide covers class definitions, object instantiation, the __init__ constructor, instance vs. class attributes, and extending functionality using inheritance.
Key Takeaways
- A Class serves as a blueprint; an Object is a concrete instance of that blueprint.
- The
__init__method initializes an object’s state upon instantiation. selfexplicitly references the current instance accessing class attributes or methods.- Inheritance allows child classes to inherit and override methods from parent classes.
1. Defining Classes and Instantiating Objects
Define a class using the class keyword. The __init__ method acts as the constructor to assign initial state.
Python
class SystemNode:
# Class attribute (shared across all instances)
environment = "Production"
def __init__(self, node_id, ip_address):
# Instance attributes (unique to each object)
self.node_id = node_id
self.ip_address = ip_address
self.is_active = False
def activate(self):
self.is_active = True
print(f"Node {self.node_id} [{self.ip_address}] is online.")
# Instantiating objects
primary_node = SystemNode("Alpha-1", "192.168.1.10")
primary_node.activate() # Output: Node Alpha-1 [192.168.1.10] is online.
2. Object Inheritance and super()
Inheritance enables a sub-class (child) to inherit all attributes and methods from a base-class (parent), promoting code reuse without duplication.
Python
# Base Parent Class
class DatabaseConnection:
def __init__(self, host, port):
self.host = host
self.port = port
def connect(self):
print(f"Connecting to {self.host}:{self.port}...")
# Derived Child Class
class PostgresConnection(DatabaseConnection):
def __init__(self, host, port, db_name):
# Initialize parent attributes using super()
super().__init__(host, port)
self.db_name = db_name
# Overriding parent method
def connect(self):
super().connect()
print(f"Established connection to Postgres DB: '{self.db_name}'")
pg_client = PostgresConnection("localhost", 5432, "eduzik_analytics")
pg_client.connect()
3. Encapsulation: Public vs. Private Attributes
Python handles private attributes via naming conventions rather than strict access modifiers. Prefixing an attribute with double underscores __ triggers name mangling to prevent accidental direct access.
Python
class SecurityVault:
def __init__(self, secret_key):
self.public_id = "Vault_01"
self.__secret_key = secret_key # Private attribute
def verify_key(self, input_key):
return self.__secret_key == input_key
vault = SecurityVault("CYBER_SECURE_99")
print(vault.public_id) # Output: Vault_01
# Direct access to __secret_key raises an AttributeError
# print(vault.__secret_key)
4. Class Methods vs. Static Methods
| Method Type | Decorator | First Argument | Primary Purpose |
| Instance Method | None | self | Accesses/modifies instance state |
| Class Method | @classmethod | cls | Accesses/modifies class-level state or provides alternative constructors |
| Static Method | @staticmethod | None | Operates as a utility function without accessing instance/class state |
Conclusion
Mastering Python Object-Oriented Programming allows you to build modular, scalable, and maintainable software systems. Using encapsulation, class inheritance, and polymorphic methods ensures your codebase stays clean as architecture complexity expands.
Frequently Asked Questions
What is the purpose of self in Python classes?
self explicitly passes the instance object into the method. It allows methods to access and modify the specific instance’s attributes and other methods.
Can a Python child class inherit from multiple parent classes?
Yes. Python supports Multiple Inheritance. When multiple parent classes share method names, Python resolves access order using the Method Resolution Order (MRO) algorithm.
What is the difference between __str__ and __repr__?
__str__ provides a readable, user-friendly string representation of an object, while __repr__ provides an unambiguous developer-focused representation primarily used for debugging.

