Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
131: Abstract Base Classes with abc
By now, you've probably used inheritance to share code between classes. But there's a big difference between wanting to share behavior and wanting to enforce a specific structure. That's where Abstract Base Classes (ABCs) come in. Think of an ABC as a blueprint or a contract; it tells any subclass, "I don't care how you do it, but you MUST implement these specific methods if you want to be considered one of us."
Why can't I just raise NotImplementedError in a regular base class?
I see this a lot. You create a base class, write a method that just says raise NotImplementedError("Implement this!"), and call it a day. The problem is that you won't find out you forgot to implement that method until the code actually hits that line at runtime. In a large system, that's a disaster waiting to happen.
With the abc module, Python prevents you from even creating an instance of the subclass if it hasn't fulfilled the contract. It moves the failure from "somewhere deep in the logic" to the very moment you try to instantiate the object. It's a much safer way to build plugins or frameworks where other developers (or future you) will be adding new types of functionality.
How do I actually build one of these?
It's pretty straightforward. You inherit from ABC and use the @abstractmethod decorator. Let's imagine we're building a payment system. We have different providers—Stripe, PayPal, Crypto—and we need to guarantee that every single one of them has a process_payment method.
from abc import ABC, abstractmethod
class PaymentProcessor(ABC):
@abstractmethod
def process_payment(self, amount):
pass
@abstractmethod
def refund_payment(self, transaction_id):
pass
class StripeProcessor(PaymentProcessor):
def process_payment(self, amount):
print(f"Processing ${amount} via Stripe...")
def refund_payment(self, transaction_id):
print(f"Refunding {transaction_id} via Stripe...")
# This works fine
stripe = StripeProcessor()
# This would raise a TypeError because PaymentProcessor is abstract
# processor = PaymentProcessor()
Can an abstract class have "normal" methods with actual logic?
Absolutely. This is where ABCs get powerful. You can mix abstract methods (the "must-do" list) with concrete methods (the "here is a shared tool" list). I often use this to handle logging or validation that should be identical across all subclasses, so I don't have to repeat that code everywhere.
class PaymentProcessor(ABC):
@abstractmethod
def process_payment(self, amount):
pass
# This is a concrete method. Every subclass gets this for free.
def log_transaction(self, amount):
print(f"LOG: Transaction of ${amount} initiated.")
class PayPalProcessor(PaymentProcessor):
def process_payment(self, amount):
self.log_transaction(amount) # Using the base class logic
print(f"Processing ${amount} via PayPal...")
def refund_payment(self, transaction_id):
print(f"Refunding {transaction_id} via PayPal...")
What happens if I forget to implement one of the methods?
Python will throw a TypeError the moment you try to create the object. It won't let you get away with it. If PayPalProcessor forgot to implement refund_payment, Python would tell you exactly which method is missing.
I find this incredibly helpful when working in teams. Instead of writing a long README explaining what methods a new plugin needs to have, I just give them the ABC. The code becomes the documentation. If they break the contract, the interpreter tells them immediately.
📋 Practical Task
Build a Multi-Format Document Exporter
You are building a system that can export data to different file formats (PDF, HTML, and Markdown). To ensure every exporter follows the same rules, you need to implement a formal contract.
Your Task:
- Create an abstract base class called
DocumentExporterusing theabcmodule. - Define two abstract methods:
export(self, data)andget_default_extension(self). - Implement a concrete method in the base class called
validate_data(self, data)that simply returnsTrueif the data is a string andFalseotherwise. - Create two subclasses:
PDFExporterandHTMLExporter. - Each subclass must implement the two abstract methods. In
export, you should call thevalidate_datamethod from the base class; if the data is valid, print a message saying the data is being exported to that specific format. - Instantiate both exporters and call their
exportmethods with a valid string.
There are no comments for now.