Introduction
Polymorphism and abstraction are frequently confused because they often appear together in the same code, but they solve different problems. Polymorphism is about behavior: the ability for objects of different classes to respond to the same method call, each in its own type-appropriate way, so calling code can treat them uniformly. Abstraction is about interface design: hiding the internal complexity of how something works and exposing only the essential operations a caller needs. Abstraction defines the contract; polymorphism is what makes different classes able to fulfill that contract differently.
Cricket analogy: The BCCI's rulebook defines that every bowler must deliver a legal ball within the crease (abstraction, the contract), while polymorphism is Bumrah's yorker and Ashwin's off-spin fulfilling that same 'bowl a legal delivery' contract in completely different type-appropriate ways.
Explanation
Python's abc module (Abstract Base Classes) is the standard tool for expressing abstraction formally. A class that inherits from ABC and marks one or more methods with the @abstractmethod decorator becomes an abstract class: it cannot be instantiated directly, and any concrete subclass is required to implement every abstract method or it, too, cannot be instantiated. This enforces a contract — abstraction — at the language level rather than by convention alone. Polymorphism, by contrast, doesn't require abc at all; Python supports it naturally through duck typing (if an object has the method being called, it can be used, regardless of its class hierarchy) as well as through inheritance-based polymorphism (subclasses of a common base overriding a shared method). The two concepts complement each other: abc is commonly used to formally declare the abstract interface that multiple polymorphic implementations must satisfy, making the polymorphism explicit and enforced rather than accidental.
Cricket analogy: The ICC's official 'Bowler' certification is like Python's abc module: it formally requires anyone certified as a bowler to demonstrate a legal action, and someone who can't pass the test simply isn't certified to bowl in a match — a hard, enforced contract rather than just a suggestion.
Example
from abc import ABC, abstractmethod
class PaymentProcessor(ABC):
"""Abstraction: defines WHAT every payment processor must do,
without saying HOW any particular one does it."""
@abstractmethod
def process_payment(self, amount):
raise NotImplementedError
@abstractmethod
def refund(self, amount):
raise NotImplementedError
class CreditCardProcessor(PaymentProcessor):
def process_payment(self, amount):
return f"Charged ${amount:.2f} to credit card"
def refund(self, amount):
return f"Refunded ${amount:.2f} to credit card"
class PayPalProcessor(PaymentProcessor):
def process_payment(self, amount):
return f"Sent ${amount:.2f} PayPal payment request"
def refund(self, amount):
return f"Issued ${amount:.2f} PayPal refund"
def checkout(processor: PaymentProcessor, amount: float):
# Polymorphism: caller doesn't care which concrete class this is
print(processor.process_payment(amount))
for processor in (CreditCardProcessor(), PayPalProcessor()):
checkout(processor, 49.99)
# PaymentProcessor() # would raise TypeError: Can't instantiate abstract classAnalysis
PaymentProcessor demonstrates abstraction: it declares that any payment processor must support process_payment and refund, without exposing or dictating how either operation is actually carried out — that complexity is hidden inside each concrete subclass. Because PaymentProcessor inherits from ABC and marks both methods @abstractmethod, Python actively prevents instantiating PaymentProcessor directly; attempting PaymentProcessor() raises TypeError, which enforces the abstraction as a real language-level contract, not just documentation. CreditCardProcessor and PayPalProcessor demonstrate polymorphism: both implement process_payment and refund completely differently, yet the checkout() function calls processor.process_payment(amount) without any knowledge of which concrete class it received — the correct behavior is selected automatically based on the object's actual type. This is the key distinction: abstraction is the PaymentProcessor contract itself (hiding 'how', exposing 'what'), while polymorphism is the loop in checkout() being able to treat every subclass uniformly despite their differing internal implementations.
Cricket analogy: The 'Bowler' certification declares anyone must be able to 'deliver a legal ball' without dictating pace or spin technique (abstraction); attempting to certify someone who's never bowled a legal delivery gets rejected outright, enforcing the contract; and Bumrah's yorker versus Ashwin's carrom ball both satisfying 'deliver a legal ball' completely differently is polymorphism — the umpire signaling a wicket without caring which bowler achieved it.
Key Takeaways
- Abstraction hides implementation details and exposes only an essential, well-defined interface.
- Polymorphism lets different classes respond to the same method call with their own type-specific behavior.
- Python's
abcmodule withABCand@abstractmethodenforces abstraction: abstract classes can't be instantiated, and subclasses must implement all abstract methods. - Abstraction defines the contract; polymorphism is how multiple classes fulfill that contract differently.
Practice what you learned
1. Which statement best distinguishes abstraction from polymorphism?
2. What happens if you try to instantiate `PaymentProcessor` directly in the example?
3. What decorator marks a method as required for all concrete subclasses in Python's abc module?
4. In the `checkout()` function, what OOP concept allows it to call `processor.process_payment(amount)` without knowing the concrete class?
Was this page helpful?
You May Also Like
OOP Core Concepts
An introduction to the four pillars of object-oriented programming: encapsulation, abstraction, inheritance, and polymorphism.
Inheritance
How single inheritance, `super().__init__()`, and method overriding let subclasses reuse and specialize parent class behavior.
Liskov Substitution and Interface Segregation
Understand why subtypes must honor their base type's contract and why fat interfaces should be split into focused ones.
SOLID Principles Overview
A guided tour of the five SOLID design principles that make object-oriented code easier to maintain and extend.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringDesign Patterns Study Notes
Software Design · 30 topics
Software EngineeringGit & Version Control Study Notes
Bash · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics