Advanced OOP: Inheritance, Abstract Classes and Polymorphism
Learning objectives
In this second in-depth part you will learn to:
- Define class hierarchies with Inheritance.
- Use Abstract Classes to enforce rules on subclasses.
- Use Static Methods for utilities decoupled from the instance.
- Implement Operator Overloading to compare objects (e.g.
<,==).
Abstract Classes and Inheritance
We often want to define a “base model” that cannot be used directly, but only serves as a foundation for more specific classes.
The abc module (Abstract Base Classes)
Imagine we want to manage both IPv4 and IPv6 addresses. We create a parent class IPAddress that forces child classes to implement certain methods.
Class hierarchy:
from abc import ABC, abstractmethod
# Abstract Class (inherits from ABC)
class IPAddress(ABC):
def __init__(self, address):
self.address = address
# Abstract method: children MUST implement it
@abstractmethod
def to_binary(self):
pass
# Concrete method: children inherit it ready to use
def __str__(self):
return f"IP: {self.address}"
# Concrete Subclass
class IPv4(IPAddress):
def to_binary(self):
# IPv4-specific logic
octets = self.address.split('.')
binary = [format(int(x), '08b') for x in octets]
return "".join(binary)
If you tried to instantiate IPAddress directly or create a subclass without to_binary, Python would raise an error.
Static Methods (@staticmethod)
Sometimes you need a function that logically belongs to the class but does not need to access self (i.e. the instance’s data). They are often used for validation or utility functions.
class IPv4(IPAddress):
# ... previous methods ...
@staticmethod
def is_valid_format(ip_str):
"""Checks whether a string looks like a valid IPv4 address."""
parts = ip_str.split('.')
if len(parts) != 4:
return False
for p in parts:
if not p.isdigit() or not (0 <= int(p) <= 255):
return False
return True
# We can call it WITHOUT creating an object!
print(IPv4.is_valid_format("192.168.1.1")) # True
print(IPv4.is_valid_format("300.0.0.1")) # False
Operator Overloading and Comparators
In Python there is no method overloading (same name, different parameters) as in Java, but Operator Overloading is very powerful. We can redefine how objects respond to standard operators (+, -, ==, <) using Magic Methods.
Comparison (__eq__, __lt__)
We want to be able to sort a list of IPs or ask if ip1 > ip2.
class IPv4(IPAddress):
# ...
def __eq__(self, other):
"""Defines the behaviour of the == operator"""
if not isinstance(other, IPv4):
return False
return self.address == other.address
def __lt__(self, other):
"""Defines the behaviour of the < operator (Less Than)"""
# Trick: compare integer values or binary strings
return self.to_binary() < other.to_binary()
# Usage
ip_a = IPv4("10.0.0.1")
ip_b = IPv4("192.168.1.1")
if ip_a < ip_b:
print(f"{ip_a} comes before {ip_b}")
# Automatic sorting in lists!
ip_list = [ip_b, ip_a]
ip_list.sort() # Uses __lt__ automatically
print([str(ip) for ip in ip_list])
Overloading Mathematical Operators
We can also define addition. For example, adding an integer to an IP to get the next IP.
def __add__(self, n):
"""Handles: new_ip = ip_object + integer"""
# 1. Convert IP to integer
# 2. Add n
# 3. Convert back to string and return new object
# (Implementation left as exercise)
pass
Conceptual Summary
- Inheritance:
class B(A). “B is a type of A”. Avoids rewriting common code. - Abstract Class: An “incomplete” class that defines the rules for its children.
- Static Method: A function inside a class that does not use
self. - Overloading: Giving new meanings to operators
+,==,<for our objects.
Advanced Exercise
Extend the IPv4 class created above:
- Implement
__add__: makeIPv4("192.168.1.1") + 2return a newIPv4("192.168.1.3")object. - Implement
__sub__: the difference between two IPs should return the number of hosts between them (an integer). - Create a
Networkclass that takes an IP and a mask, and has a method to generate all usable IPs (using your+operator).
EC