Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • ๐Ÿ“ Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • ๐Ÿ“ Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • ๐Ÿ“ Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • ๐Ÿ“ Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • ๐Ÿ“ While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • ๐Ÿ“ For Each Assignments
    • ๐Ÿ“ All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • ๐Ÿ“ Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • ๐Ÿ“ Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • ๐Ÿ“ Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • ๐Ÿ“ Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • ๐Ÿ“ Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • ๐Ÿ“ Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • ๐Ÿ“ Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • ๐Ÿ“ Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • ๐Ÿ“ Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • ๐Ÿ“ Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • ๐Ÿ“ Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • ๐Ÿ“ Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • ๐Ÿ“ Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • ๐Ÿ“ Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • ๐Ÿ“ Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • ๐Ÿ“ Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • ๐Ÿ“ Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • ๐Ÿ“ Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • ๐Ÿ“ Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Abstract Class vs Interface

Python Abstract Class vs Interface - Complete Guide

Learn the key differences between abstract classes and interfaces in Python.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
On this page:
  • What's the Difference?
  • Abstract Classes in Python
  • Interfaces in Python
  • Key Differences
  • When to Use What
  • Real-World Examples
  • Best Practices
  • Try It Yourself
  • Quick Quiz
  • Frequently Asked Questions
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • What is an abstract class โ€” a class that can't be instantiated and can have both abstract and concrete methods
  • What is an interface โ€” a contract that defines what methods a class must have
  • Key differences โ€” state, implementation, inheritance, and enforcement
  • When to use each โ€” practical guidance for your code
  • Real-world examples โ€” see both concepts in action
  • Python-specific โ€” how ABCs and Protocols implement these concepts

What's the Difference?

In many programming languages like Java and C#, abstract classes and interfaces are clearly distinct concepts with specific syntax. In Python, the lines are blurrier because Python doesn't have a dedicated interface keyword.

However, Python does provide tools to implement both concepts:

  • Abstract Classes โ€” created using abc.ABC and @abstractmethod
  • Interfaces โ€” implemented using Abstract Base Classes (with only abstract methods) or Protocols (structural typing)

๐Ÿ’ก Key concept: The main difference is what they can contain and how they're used. Abstract classes can have state and concrete methods; interfaces are pure contracts with no implementation.

Abstract Classes in Python

1

What is an Abstract Class?

An abstract class is a class that cannot be instantiated. It serves as a blueprint for other classes. Abstract classes can contain:

  • Abstract methods โ€” methods without implementation that must be implemented by subclasses
  • Concrete methods โ€” methods with implementation that subclasses can use or override
  • Instance variables โ€” state that subclasses inherit
  • Constructors โ€” __init__ methods that initialize state

Abstract classes are great when you have shared behavior and common state that multiple related classes should inherit.

# Abstract Class in Python

from abc import ABC, abstractmethod

class Animal(ABC):
    """Abstract base class for all animals"""
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @abstractmethod
    def make_sound(self):
        """Abstract method โ€” must be implemented by subclasses"""
        pass
    
    @abstractmethod
    def move(self):
        """Abstract method โ€” must be implemented by subclasses"""
        pass
    
    # Concrete method โ€” shared by all subclasses
    def get_info(self):
        return f"{self.name} is {self.age} years old"
    
    # Another concrete method
    def sleep(self):
        return f"{self.name} is sleeping"

class Dog(Animal):
    def make_sound(self):
        return "Woof!"
    
    def move(self):
        return f"{self.name} runs on four legs"
    
    # Optional: override the concrete method
    def sleep(self):
        return f"{self.name} is sleeping (curled up)"

class Cat(Animal):
    def make_sound(self):
        return "Meow!"
    
    def move(self):
        return f"{self.name} walks silently"
    
    def sleep(self):
        return f"{self.name} is sleeping (on the couch)"

# Cannot instantiate abstract class
# animal = Animal("Bob", 5)  # TypeError

# But can instantiate subclasses
dog = Dog("Rex", 3)
cat = Cat("Whiskers", 2)

print(dog.get_info())       # Rex is 3 years old
print(dog.make_sound())     # Woof!
print(dog.move())           # Rex runs on four legs
print(dog.sleep())          # Rex is sleeping (curled up)

print(cat.get_info())       # Whiskers is 2 years old
print(cat.make_sound())     # Meow!
print(cat.move())           # Whiskers walks silently

print("\nโœ… Abstract classes: can have state, abstract methods, and concrete methods")
print("โš ๏ธ Cannot instantiate the abstract class itself")

Abstract class key points:

  • Can have state โ€” instance variables are inherited
  • Can have concrete methods โ€” shared behavior
  • Must have at least one abstract method โ€” to be abstract
  • Cannot be instantiated โ€” used as a template
  • Single inheritance โ€” a class can inherit from only one abstract class (in most cases)

Quick Check: What two types of methods can an abstract class have? (Answer: Abstract methods and concrete methods)

Interfaces in Python

2

What is an Interface?

An interface is a contract that defines what methods a class must have, but not how they work. In Python, interfaces are implemented in two ways:

  1. Using Abstract Base Classes with only abstract methods (no concrete methods, no state)
  2. Using Protocols (structural typing, no inheritance required)

Interfaces are great when you want to define a contract that multiple unrelated classes can implement. They focus on what the class can do, not what it is.

# Interfaces in Python โ€” Two Approaches

from abc import ABC, abstractmethod
from typing import Protocol

# -----------------------------
# Approach 1: ABC with only abstract methods
# -----------------------------

class Drawable(ABC):
    """Interface: anything that can be drawn"""
    
    @abstractmethod
    def draw(self):
        """Draw the object โ€” must be implemented"""
        pass
    
    @abstractmethod
    def get_area(self):
        """Return the area โ€” must be implemented"""
        pass

class Circle(Drawable):
    def __init__(self, radius):
        self.radius = radius
    
    def draw(self):
        return f"Drawing a circle with radius {self.radius}"
    
    def get_area(self):
        return 3.14 * self.radius ** 2

class Square(Drawable):
    def __init__(self, side):
        self.side = side
    
    def draw(self):
        return f"Drawing a square with side {self.side}"
    
    def get_area(self):
        return self.side ** 2

# -----------------------------
# Approach 2: Protocol (structural typing)
# -----------------------------

class Renderable(Protocol):
    """Interface: anything that can be rendered"""
    def render(self) -> str: ...
    def get_name(self) -> str: ...

# These classes don't need to inherit from anything
class PDFRenderer:
    def render(self) -> str:
        return "Rendering PDF file"
    
    def get_name(self) -> str:
        return "PDF Renderer"

class HTMLRenderer:
    def render(self) -> str:
        return "Rendering HTML content"
    
    def get_name(self) -> str:
        return "HTML Renderer"

# -----------------------------
# Using both approaches
# -----------------------------

print("=== ABC Interface (Drawable) ===")
circle = Circle(5)
square = Square(4)

print(circle.draw())        # Drawing a circle with radius 5
print(f"Area: {circle.get_area()}")  # Area: 78.5

print(square.draw())        # Drawing a square with side 4
print(f"Area: {square.get_area()}")  # Area: 16

print("\n=== Protocol Interface (Renderable) ===")
def display(renderer: Renderable):
    print(f"{renderer.get_name()}: {renderer.render()}")

display(PDFRenderer())      # PDF Renderer: Rendering PDF file
display(HTMLRenderer())     # HTML Renderer: Rendering HTML content

print("\nโœ… Interfaces: pure contract, no state, no implementation")
print("โœ… Protocol: classes don't need to inherit from anything")

Interface key points:

  • No state โ€” no instance variables
  • No concrete methods โ€” only abstract method signatures
  • Pure contract โ€” defines what methods must exist
  • Multiple inheritance โ€” a class can implement multiple interfaces
  • Protocols โ€” structural typing, no inheritance required

Quick Check: Can an interface have instance variables? (Answer: No โ€” interfaces only define methods, not state)

Key Differences

3

Abstract Class vs Interface โ€” Side by Side

# Abstract Class vs Interface โ€” Side by Side Comparison

from abc import ABC, abstractmethod
from typing import Protocol

# ----- ABSTRACT CLASS -----
class Vehicle(ABC):
    """Abstract class โ€” can have state and concrete methods"""
    
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.speed = 0
    
    @abstractmethod
    def start_engine(self):
        """Abstract method โ€” must be implemented"""
        pass
    
    @abstractmethod
    def stop_engine(self):
        """Abstract method โ€” must be implemented"""
        pass
    
    # Concrete method โ€” shared behavior
    def accelerate(self, amount):
        self.speed += amount
        return f"Accelerating to {self.speed} km/h"
    
    # Concrete method โ€” shared behavior
    def get_info(self):
        return f"{self.make} {self.model} ({self.year})"

class Car(Vehicle):
    def start_engine(self):
        return "Car engine started with key"
    
    def stop_engine(self):
        return "Car engine stopped"

class Motorcycle(Vehicle):
    def start_engine(self):
        return "Motorcycle engine started with kick"
    
    def stop_engine(self):
        return "Motorcycle engine stopped"


# ----- INTERFACE (using ABC with only abstract methods) -----
class Drivable(ABC):
    """Interface โ€” pure contract, no state, no concrete methods"""
    
    @abstractmethod
    def drive(self, distance):
        pass
    
    @abstractmethod
    def brake(self):
        pass

class Sedan(Drivable):
    def __init__(self, make, model):
        self.make = make
        self.model = model
    
    def drive(self, distance):
        return f"Driving {distance} km in sedan"
    
    def brake(self):
        return "Applying brakes in sedan"

class SUV(Drivable):
    def __init__(self, make, model):
        self.make = make
        self.model = model
    
    def drive(self, distance):
        return f"Driving {distance} km in SUV"
    
    def brake(self):
        return "Applying brakes in SUV"


# ----- INTERFACE (using Protocol) -----
class Brakeable(Protocol):
    """Interface โ€” structural typing"""
    def brake(self) -> str: ...

def apply_emergency_brake(vehicle: Brakeable):
    print(f"โš ๏ธ EMERGENCY: {vehicle.brake()}")

# Both Sedan and SUV work with the Protocol
car1 = Sedan("Toyota", "Camry")
car2 = SUV("Honda", "CR-V")
apply_emergency_brake(car1)  # Applying brakes in sedan
apply_emergency_brake(car2)  # Applying brakes in SUV


# ----- COMPARISON SUMMARY -----
print("\n" + "=" * 60)
print("ABSTRACT CLASS vs INTERFACE")
print("=" * 60)

print("\n| Feature              | Abstract Class        | Interface             |")
print("|----------------------|-----------------------|-----------------------|")
print("| State (variables)    | โœ… Yes                | โŒ No                 |")
print("| Concrete methods     | โœ… Yes                | โŒ No                 |")
print("| Abstract methods     | โœ… Yes                | โœ… Yes                |")
print("| Instantiable?        | โŒ No                 | โŒ No                 |")
print("| Multiple inheritance | โŒ Usually one        | โœ… Yes                |")
print("| Use case             | Share code & state    | Define contract       |")
print("| Python tools         | abc.ABC, @abstractmethod | abc.ABC or Protocol |")

print("\nโœ… Abstract class: 'IS-A' relationship (what something IS)")
print("โœ… Interface: 'CAN-DO' relationship (what something CAN DO)")

Key differences summary:

  • State: Abstract classes can have instance variables; interfaces cannot
  • Concrete methods: Abstract classes can have implemented methods; interfaces cannot
  • Inheritance: Abstract classes support single inheritance; interfaces support multiple
  • Relationship: Abstract class = "IS-A"; Interface = "CAN-DO"
  • Use case: Abstract class = share code; Interface = define a contract

Quick Check: What's the main difference in terms of state? (Answer: Abstract classes can have instance variables; interfaces cannot)

When to Use What

4

Making the Right Choice

Here's a simple rule of thumb for choosing between an abstract class and an interface:

# When to Use Abstract Class vs Interface

# ----- USE ABSTRACT CLASS WHEN -----

# 1. You need to share common code among related classes
class Shape(ABC):
    def __init__(self, color):
        self.color = color
    
    @abstractmethod
    def area(self):
        pass
    
    # Concrete method โ€” shared by all shapes
    def get_color(self):
        return self.color

class Rectangle(Shape):
    def __init__(self, color, width, height):
        super().__init__(color)
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

# โœ… Abstract class: Rectangle IS-A Shape, shares color state and get_color()


# 2. You want to provide a default implementation
class Writer(ABC):
    @abstractmethod
    def write(self, content):
        pass
    
    # Default implementation that can be overridden
    def write_line(self, content):
        return self.write(content + "\n")

class FileWriter(Writer):
    def write(self, content):
        return f"Writing: {content}"

class ConsoleWriter(Writer):
    def write(self, content):
        return f"Console: {content}"

# โœ… Abstract class: provides default write_line() that uses write()


# ----- USE INTERFACE WHEN -----

# 1. You want to define a contract for unrelated classes
class Flyable(ABC):
    @abstractmethod
    def fly(self):
        pass

class Bird:
    def fly(self):
        return "Bird flies"

class Airplane:
    def fly(self):
        return "Airplane flies"

# โœ… Interface: Bird and Airplane are unrelated but both CAN-DO fly()


# 2. You want multiple inheritance (implement multiple interfaces)
class Swimmable(ABC):
    @abstractmethod
    def swim(self):
        pass

class Amphibian(Flyable, Swimmable):
    def fly(self):
        return "Amphibian flies"
    
    def swim(self):
        return "Amphibian swims"

# โœ… Interface: multiple inheritance is clean and safe


# ----- USE PROTOCOL (modern interface) WHEN -----

# 3. You want loose coupling (class doesn't depend on your interface)
class Serializer(Protocol):
    def serialize(self) -> str: ...

class JSONData:
    def serialize(self) -> str:
        return '{"data": "json"}'

class XMLData:
    def serialize(self) -> str:
        return "xml"

def save(data: Serializer):
    print(f"Saving: {data.serialize()}")

# โœ… Protocol: classes don't need to import or inherit from Serializer

print("""
| Use Case                          | Abstract Class | Interface (ABC) | Protocol |
|-----------------------------------|----------------|-----------------|----------|
| Share code & state               | โœ… Yes         | โŒ No           | โŒ No     |
| Define contract for related classes | โœ… Yes     | โœ… Yes          | โœ… Yes    |
| Define contract for unrelated classes | โŒ No  | โœ… Yes          | โœ… Yes    |
| Multiple inheritance             | โŒ No          | โœ… Yes          | โœ… Yes    |
| Loose coupling (no dependency)   | โŒ No          | โŒ No           | โœ… Yes    |
| Runtime enforcement              | โœ… Yes         | โœ… Yes          | โŒ No*    |
| Static type checking             | โœ… Yes         | โœ… Yes          | โœ… Yes    |
| Default implementations          | โœ… Yes         | โŒ No           | โŒ No     |
""")

print("* Protocols can use @runtime_checkable for runtime checks")

Decision guide:

  • Abstract class โ€” use when classes are closely related and share code/state
  • Interface (ABC) โ€” use when defining a contract for unrelated classes
  • Interface (Protocol) โ€” use when you want loose coupling and structural typing
  • Multiple inheritance โ€” interfaces are safer than abstract classes
  • Default implementations โ€” only abstract classes can provide these

Quick Check: When would you choose an abstract class over an interface? (Answer: When you need to share code and state among closely related classes)

Real-World Examples

5

Abstract Class and Interface in Action

# Real-World Example: Payment Processing System

from abc import ABC, abstractmethod
from typing import Protocol
import json

# ============================================================
# Part 1: ABSTRACT CLASS โ€” Base Payment Processor
# ============================================================

class PaymentProcessor(ABC):
    """Abstract class for all payment processors"""
    
    def __init__(self, merchant_id: str):
        self.merchant_id = merchant_id
        self.transactions = []
    
    @abstractmethod
    def process_payment(self, amount: float, currency: str) -> dict:
        """Process a payment โ€” must be implemented"""
        pass
    
    @abstractmethod
    def refund_payment(self, transaction_id: str) -> dict:
        """Refund a payment โ€” must be implemented"""
        pass
    
    # Concrete method โ€” shared by all processors
    def get_transaction_history(self):
        return self.transactions
    
    # Concrete method โ€” shared by all processors
    def add_transaction(self, transaction):
        self.transactions.append(transaction)
        return transaction

class StripeProcessor(PaymentProcessor):
    def process_payment(self, amount: float, currency: str) -> dict:
        transaction = {
            "id": f"stripe_{len(self.transactions) + 1}",
            "amount": amount,
            "currency": currency,
            "status": "succeeded",
            "processor": "Stripe"
        }
        self.add_transaction(transaction)
        return transaction
    
    def refund_payment(self, transaction_id: str) -> dict:
        return {
            "id": f"refund_{transaction_id}",
            "status": "refunded",
            "processor": "Stripe"
        }

class PayPalProcessor(PaymentProcessor):
    def process_payment(self, amount: float, currency: str) -> dict:
        transaction = {
            "id": f"paypal_{len(self.transactions) + 1}",
            "amount": amount,
            "currency": currency,
            "status": "completed",
            "processor": "PayPal"
        }
        self.add_transaction(transaction)
        return transaction
    
    def refund_payment(self, transaction_id: str) -> dict:
        return {
            "id": f"refund_{transaction_id}",
            "status": "refunded",
            "processor": "PayPal"
        }

# ============================================================
# Part 2: INTERFACE โ€” Notification System (Protocol)
# ============================================================

class Notifier(Protocol):
    """Protocol for notification senders"""
    def send(self, message: str, recipient: str) -> dict: ...
    def get_status(self) -> str: ...

# These classes don't know about the Notifier protocol

class EmailNotifier:
    def send(self, message: str, recipient: str) -> dict:
        return {
            "status": "sent",
            "recipient": recipient,
            "message": message[:20] + "...",
            "method": "email"
        }
    
    def get_status(self) -> str:
        return "Email service: healthy"

class SMSNotifier:
    def send(self, message: str, recipient: str) -> dict:
        return {
            "status": "sent",
            "recipient": recipient,
            "message": message[:15] + "...",
            "method": "sms"
        }
    
    def get_status(self) -> str:
        return "SMS service: healthy"

class PushNotifier:
    def send(self, message: str, recipient: str) -> dict:
        return {
            "status": "delivered",
            "recipient": recipient,
            "message": message[:10] + "...",
            "method": "push"
        }
    
    def get_status(self) -> str:
        return "Push service: healthy"

# ============================================================
# Part 3: USING THE SYSTEM
# ============================================================

def process_order(amount: float, currency: str, 
                  processor: PaymentProcessor, 
                  notifiers: list):
    """Process an order with payment and notifications"""
    
    # Process payment (using abstract class)
    result = processor.process_payment(amount, currency)
    print(f"๐Ÿ’ฐ Payment: {result}")
    
    # Send notifications (using protocol)
    for notifier in notifiers:
        status = notifier.get_status()
        if "healthy" in status or "delivered" in status:
            notification = notifier.send(
                f"Your payment of {amount} {currency} was processed",
                "customer@example.com"
            )
            print(f"๐Ÿ“จ {notification}")
    
    return result

# Demo
print("=" * 60)
print("PAYMENT PROCESSING SYSTEM")
print("=" * 60)

stripe = StripeProcessor("merchant_001")
paypal = PayPalProcessor("merchant_002")

notifiers = [EmailNotifier(), SMSNotifier(), PushNotifier()]

print("\n1. Processing with Stripe")
process_order(99.99, "USD", stripe, notifiers)

print("\n2. Processing with PayPal")
process_order(49.95, "EUR", paypal, notifiers)

print("\n3. Transaction History")
print(f"Stripe transactions: {stripe.get_transaction_history()}")
print(f"PayPal transactions: {paypal.get_transaction_history()}")

print("\nโœ… Abstract class: PaymentProcessor provides shared state and methods")
print("โœ… Protocol: Notifier allows any class with send() and get_status()")

Real-world example key points:

  • Abstract class (PaymentProcessor) โ€” shares state (merchant_id, transactions) and concrete methods (get_transaction_history, add_transaction)
  • Concrete processors (StripeProcessor, PayPalProcessor) โ€” implement the abstract methods
  • Interface (Notifier Protocol) โ€” defines what a notifier can do without requiring inheritance
  • Concrete notifiers (Email, SMS, Push) โ€” don't depend on the protocol, just have the right methods
  • Both approaches โ€” work together in the same system

Quick Check: In the example, what does the abstract class provide that the interface doesn't? (Answer: State (merchant_id, transactions) and concrete methods (get_transaction_history, add_transaction))

Best Practices

6

Using Abstract Classes and Interfaces Effectively

# Best Practices for Abstract Classes and Interfaces

from abc import ABC, abstractmethod
from typing import Protocol

# ============================================================
# 1. ABSTRACT CLASS BEST PRACTICES
# ============================================================

# โœ… DO: Use abstract classes for related classes with shared code
class Animal(ABC):
    def __init__(self, name):
        self.name = name
    
    @abstractmethod
    def speak(self):
        pass
    
    # Shared concrete method
    def get_name(self):
        return self.name

# โŒ DON'T: Use abstract class for unrelated classes
# class Printable(ABC):  # This should be an interface
#     @abstractmethod
#     def print(self):
#         pass


# ============================================================
# 2. INTERFACE BEST PRACTICES
# ============================================================

# โœ… DO: Keep interfaces focused (single responsibility)
class Printable(ABC):
    @abstractmethod
    def print(self):
        pass

class Serializable(ABC):
    @abstractmethod
    def serialize(self):
        pass

# โŒ DON'T: Put unrelated methods in one interface
# class Everything(ABC):
#     @abstractmethod
#     def print(self):
#         pass
#     @abstractmethod
#     def serialize(self):
#         pass
#     @abstractmethod
#     def save(self):
#         pass


# ============================================================
# 3. MIXING ABSTRACT CLASSES AND INTERFACES
# ============================================================

# โœ… DO: Use abstract class for core behavior and interfaces for additional capabilities
class Document(ABC):
    """Abstract class: core document behavior"""
    def __init__(self, title):
        self.title = title
    
    @abstractmethod
    def open(self):
        pass
    
    @abstractmethod
    def close(self):
        pass

class Printable:
    """Interface: can be printed"""
    def print(self):
        return f"Printing {self.title}"

class Exportable:
    """Interface: can be exported"""
    def export(self):
        return f"Exporting {self.title}"

class PDFDocument(Document, Printable):
    def open(self):
        return f"Opening PDF: {self.title}"
    
    def close(self):
        return f"Closing PDF: {self.title}"

class WordDocument(Document, Printable, Exportable):
    def open(self):
        return f"Opening Word: {self.title}"
    
    def close(self):
        return f"Closing Word: {self.title}"


# ============================================================
# 4. INTERFACE NAMING CONVENTIONS
# ============================================================

# โœ… DO: Use clear names โ€” suffix with "-able" for capabilities
class Playable(ABC):
    """Interface: something that can be played"""
    @abstractmethod
    def play(self):
        pass

class Stoppable(ABC):
    """Interface: something that can be stopped"""
    @abstractmethod
    def stop(self):
        pass

# โŒ DON'T: Use vague names
# class Doer(ABC):
#     pass


# ============================================================
# 5. PROTOCOL BEST PRACTICES
# ============================================================

# โœ… DO: Use Protocols for loose coupling
class Logger(Protocol):
    def log(self, message: str) -> None: ...

class FileLogger:
    def log(self, message: str) -> None:
        print(f"File: {message}")

class ConsoleLogger:
    def log(self, message: str) -> None:
        print(f"Console: {message}")

# โœ… DO: Use Protocols for public APIs
class Cache(Protocol):
    def get(self, key: str) -> object: ...
    def set(self, key: str, value: object) -> None: ...


# ============================================================
# 6. SUMMARY
# ============================================================

print("""
๐Ÿ“‹ BEST PRACTICES SUMMARY
========================

ABSTRACT CLASS:
โ€ข Use for closely related classes
โ€ข Share code and state
โ€ข Use single inheritance
โ€ข Name clearly (Animal, PaymentProcessor, etc.)

INTERFACE (ABC):
โ€ข Use for unrelated classes
โ€ข Define contract only (no state, no implementation)
โ€ข Support multiple inheritance
โ€ข Name with "-able" suffix (Printable, Serializable)

INTERFACE (PROTOCOL):
โ€ข Use for loose coupling
โ€ข Class doesn't depend on the interface
โ€ข Use for structural typing
โ€ข Great for public APIs and third-party code

MIXING:
โ€ข Abstract class for core behavior
โ€ข Interfaces for additional capabilities
โ€ข Both can be used in the same class
""")

Best practices summary:

  • Abstract classes โ€” for related classes that share code and state
  • Interfaces (ABC) โ€” for unrelated classes that need a contract
  • Interfaces (Protocol) โ€” for loose coupling and structural typing
  • Keep interfaces focused โ€” single responsibility
  • Use clear naming โ€” "-able" suffix for interfaces
  • Mix both โ€” abstract class for core behavior, interfaces for additional capabilities

Quick Check: What's a good naming convention for interfaces? (Answer: Use the "-able" suffix, like Printable, Serializable, Playable)

Try It Yourself

Experiment with abstract classes and interfaces in the editor below.

Loading Pyodide... 0%
Python Code Editor
==================================================
ABSTRACT CLASS vs INTERFACE - PRACTICE
==================================================

1. DEFINING AN ABSTRACT CLASS

2. IMPLEMENTING THE ABSTRACT CLASS

3. DEFINING AN INTERFACE (PROTOCOL)

4. CLASSES SATISFYING THE PROTOCOL

5. USING ABSTRACT CLASS AND INTERFACE
=== Appliances (Abstract Class) ===
LG WM-100
๐Ÿงบ Washing machine is ON
๐Ÿงบ Washing machine is OFF
Samsung RF-200
โ„๏ธ Refrigerator is ON (cooling)
โ„๏ธ Refrigerator is OFF

=== Smart Devices (Protocol) ===
Controlling Smart Washer:
Status: OFF
๐Ÿงบ Washing machine is ON
Status: ON
๐Ÿงบ Washing machine is OFF
Status: OFF

Controlling Smart Fridge:
Status: Idle
โ„๏ธ Refrigerator is ON (cooling)
Status: Cooling
โ„๏ธ Refrigerator is OFF
Status: Idle

โœ… Abstract class: shares state and concrete methods
โœ… Protocol: no inheritance needed, just have the right methods
๐Ÿ†

You've Got It!

You now understand the differences between abstract classes and interfaces in Python. You know when to use each and how to implement them using ABCs and Protocols.

Quick Quiz

Test what you've learned:

1. Can an abstract class have instance variables?
2. Can an interface have concrete methods?
3. Which relationship does an abstract class represent?
4. What's the advantage of using a Protocol over an ABC interface?
5. When should you use an interface over an abstract class?

Frequently Asked Questions

Is there a difference between an abstract class and an interface in Python? โ–ผ

Yes, but the distinction is less strict than in Java or C#. In Python, abstract classes can have state and concrete methods, while interfaces (implemented using ABCs with only abstract methods or Protocols) are pure contracts with no implementation or state.

Can a class inherit from both an abstract class and an interface? โ–ผ

Yes! In Python, a class can inherit from one abstract class (since it's a regular class) and multiple interfaces (since they're also classes). For example: class MyClass(AbstractClass, Interface1, Interface2):.

When should I use an abstract class vs an interface? โ–ผ

Use an abstract class when you have closely related classes that share code and state. Use an interface when you want to define a contract for unrelated classes. Use a Protocol when you want loose coupling and classes shouldn't depend on your interface.

Can an abstract class have a constructor? โ–ผ

Yes! Abstract classes can have constructors (__init__ methods) that initialize state. Subclasses typically call the abstract class constructor using super().__init__().

What's a common interview question about abstract classes vs interfaces? โ–ผ

Common questions include: "What's the difference between an abstract class and an interface in Python?" "When would you use an abstract class instead of an interface?" "How do you implement interfaces in Python?" and "What are Protocols and how are they different from ABCs?"

Are Protocols and interfaces the same thing? โ–ผ

Protocols are one way to implement interfaces in Python. They use structural subtyping (duck typing) rather than nominal subtyping (inheritance). Protocols are great for loose coupling and are preferred for public APIs, while ABC interfaces are better when you need runtime enforcement.

Where to Go From Here

Now that you understand the differences between abstract classes and interfaces, check out these related topics:

Abstraction in Python

Learn more about abstraction and how it's implemented in Python.

Learn More โ†’

Interfaces in Python

Deep dive into interfaces using ABCs and Protocols.

Learn More โ†’

Polymorphism

Learn how abstract classes and interfaces enable polymorphic behavior.

Learn More โ†’
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Interfaces in Python Next: Public, Private and Protected โ†’