Abstraction¶
Abstraction hides complex implementation details and shows only the necessary features, reducing complexity for users.
What is Abstraction¶
1. Hide Complexity¶
Show only essential features while hiding implementation details.
2. High-Level Interface¶
Users interact with objects at a high level without understanding internals.
3. Essential Features¶
Focus on what an object does, not how it does it.
Abstract Base Classes¶
1. Using ABC Module¶
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
Defines a contract that subclasses must follow.
2. Cannot Instantiate¶
shape = Shape() # TypeError!
Abstract classes cannot be instantiated directly.
3. Must Implement¶
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self): # Must implement
return self.width * self.height
@abstractmethod¶
1. Required Methods¶
class MyBase(ABC):
@abstractmethod
def must_override(self):
pass
Subclasses must implement this method.
2. Enforcement¶
class Incomplete(MyBase):
pass
obj = Incomplete() # TypeError!
Python prevents instantiation if abstract methods aren't implemented.
3. Complete Implementation¶
class Complete(MyBase):
def must_override(self):
return "Implemented!"
obj = Complete() # Works
Abstract vs Optional¶
1. Required Methods¶
class Dataset(ABC):
@abstractmethod
def __getitem__(self, index):
pass # Must override
@abstractmethod
def __len__(self):
pass # Must override
2. Optional Methods¶
def __add__(self, other):
return ConcatDataset([self, other])
No @abstractmethod = optional to override.
3. Mixed Approach¶
class MyDataset(Dataset):
def __getitem__(self, index): # Required
return self.data[index]
def __len__(self): # Required
return len(self.data)
# __add__ inherited - optional
Abstract Decorators¶
1. Abstract Class Method¶
class MyBase(ABC):
@classmethod
@abstractmethod
def create(cls):
pass
2. Abstract Static Method¶
class MyBase(ABC):
@staticmethod
@abstractmethod
def validate(data):
pass
3. Abstract Property¶
class MyBase(ABC):
@property
@abstractmethod
def name(self):
pass
Real-World Example¶
1. Shape Hierarchy¶
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
2. Concrete Shapes¶
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
3. Polymorphic Use¶
shapes = [Rectangle(3, 4), Circle(5)]
for shape in shapes:
print(f"Area: {shape.area()}")
Benefits¶
1. Contract Enforcement¶
Guarantees subclasses implement required methods.
2. Clear Interface¶
Defines what operations are available.
3. Design Consistency¶
All implementations follow the same structure.
Common Patterns¶
1. Template Method¶
class Algorithm(ABC):
@abstractmethod
def step_one(self):
pass
@abstractmethod
def step_two(self):
pass
def execute(self): # Template
self.step_one()
self.step_two()
2. Strategy Pattern¶
class Strategy(ABC):
@abstractmethod
def execute(self, data):
pass
3. Factory Pattern¶
class Creator(ABC):
@abstractmethod
def create_product(self):
pass
Key Takeaways¶
- Abstraction hides implementation details.
- ABC module enforces contracts.
@abstractmethodmarks required methods.- Cannot instantiate abstract classes.
- Enables polymorphic designs.
Runnable Example: abstract_base_classes_examples.py¶
"""Abstract Base Classes (ABC) - Defining contracts"""
from abc import ABC, abstractmethod
# =============================================================================
# Definitions
# =============================================================================
class PaymentProcessor(ABC):
"""Abstract interface for payment processing"""
@abstractmethod
def process_payment(self, amount):
"""All processors must implement this"""
pass
@abstractmethod
def refund(self, transaction_id):
"""All processors must implement this"""
pass
def log_transaction(self, details):
"""Common implementation for all"""
print(f"Logged: {details}")
class StripeProcessor(PaymentProcessor):
def process_payment(self, amount):
print(f"Processing ${amount} via Stripe")
self.log_transaction(f"Stripe: ${amount}")
def refund(self, transaction_id):
print(f"Refunding Stripe transaction {transaction_id}")
class PayPalProcessor(PaymentProcessor):
def process_payment(self, amount):
print(f"Processing ${amount} via PayPal")
self.log_transaction(f"PayPal: ${amount}")
def refund(self, transaction_id):
print(f"Refunding PayPal transaction {transaction_id}")
# Polymorphism with abstraction
def process_order(processor: PaymentProcessor, amount):
processor.process_payment(amount)
# =============================================================================
# Main
# =============================================================================
if __name__ == "__main__":
stripe = StripeProcessor()
paypal = PayPalProcessor()
process_order(stripe, 100)
process_order(paypal, 50)