Skip to content

abc Module Complexity

The abc module provides infrastructure for defining abstract base classes (ABCs) in Python, enforcing that derived classes implement specified methods.

Complexity Reference

Operation Time Space Notes
@abstractmethod decorator O(1) O(1) Mark method abstract
Class instantiation attempt O(n) O(n) n = abstract methods
isinstance(obj, ABC) O(n) O(1) n = MRO length; may also check virtual subclasses
issubclass(cls, ABC) O(n) O(1) n = MRO length; may also check virtual subclasses
Method resolution (MRO) O(n) O(n) n = class hierarchy depth
Register virtual subclass O(1) O(n) n = registered classes

Abstract Base Classes

Basic Abstract Class

from abc import ABC, abstractmethod

# Define abstract base class - O(1)
class Animal(ABC):

    # Mark as abstract - O(1)
    @abstractmethod
    def make_sound(self):
        """Subclasses must implement this"""
        pass

    # Concrete method in ABC - O(1)
    def sleep(self):
        return "Zzz..."

# Attempt instantiation - O(n) check for abstract methods
try:
    animal = Animal()  # TypeError
except TypeError as e:
    print("Cannot instantiate ABC")

# Concrete subclass - O(1)
class Dog(Animal):
    def make_sound(self):
        return "Woof!"

# Can instantiate concrete class - O(n)
dog = Dog()
print(dog.make_sound())  # Woof!

Abstract Properties

from abc import ABC, abstractmethod

class Shape(ABC):

    # Abstract property - O(1)
    @property
    @abstractmethod
    def area(self):
        pass

    @property
    @abstractmethod
    def perimeter(self):
        pass

# Concrete implementation - O(1)
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        return self.width * self.height

    @property
    def perimeter(self):
        return 2 * (self.width + self.height)

# Create instance - O(n)
rect = Rectangle(5, 3)

# Access properties - O(1)
print(rect.area)       # 15
print(rect.perimeter)  # 16

Abstract Class Methods

from abc import ABC, abstractmethod

class DatabaseConnection(ABC):

    # Abstract class method - O(1)
    @classmethod
    @abstractmethod
    def from_config(cls, config):
        pass

class PostgresConnection(DatabaseConnection):
    @classmethod
    def from_config(cls, config):
        return cls(config['host'], config['port'])

# Use class method - O(1)
conn = PostgresConnection.from_config({'host': 'localhost', 'port': 5432})

Abstract Static Methods

from abc import ABC, abstractmethod

class Validator(ABC):

    # Abstract static method - O(1)
    @staticmethod
    @abstractmethod
    def validate(value):
        pass

class EmailValidator(Validator):
    @staticmethod
    def validate(value):
        return '@' in value

# Use static method - O(1)
is_valid = EmailValidator.validate("user@example.com")

Multiple Abstract Methods

Enforcing Multiple Implementations

from abc import ABC, abstractmethod

class DataStore(ABC):

    # Multiple abstract methods - O(n) to enforce
    @abstractmethod
    def read(self, key):
        pass

    @abstractmethod
    def write(self, key, value):
        pass

    @abstractmethod
    def delete(self, key):
        pass

# Incomplete implementation - TypeError
class PartialStore(DataStore):
    def read(self, key):
        return None
    # Missing write() and delete()

try:
    store = PartialStore()  # O(n) - checks all 3 methods
except TypeError:
    print("Must implement all abstract methods")

# Complete implementation - O(1)
class MemoryStore(DataStore):
    def __init__(self):
        self.data = {}

    def read(self, key):
        return self.data.get(key)

    def write(self, key, value):
        self.data[key] = value

    def delete(self, key):
        del self.data[key]

# Create instance - O(n)
store = MemoryStore()

Virtual Subclasses

Register Virtual Subclass

from abc import ABC, abstractmethod

class PluginInterface(ABC):
    @abstractmethod
    def execute(self):
        pass

# Existing class not inheriting from ABC
class LegacyPlugin:
    def execute(self):
        return "Legacy execution"

# Register as virtual subclass - O(1) per registration
PluginInterface.register(LegacyPlugin)

# Check relationship - O(n) against inheritance chain
obj = LegacyPlugin()
print(isinstance(obj, PluginInterface))   # True
print(issubclass(LegacyPlugin, PluginInterface))  # True

Virtual Subclass Benefits

from abc import ABC, abstractmethod

class Drawable(ABC):
    @abstractmethod
    def draw(self):
        pass

# Register multiple unrelated classes
class Circle:
    def draw(self):
        return "Drawing circle..."

class Rectangle:
    def draw(self):
        return "Drawing rectangle..."

# Register both - O(1) each
Drawable.register(Circle)
Drawable.register(Rectangle)

# Polymorphic usage - O(n) per check
def render(obj):
    if isinstance(obj, Drawable):
        return obj.draw()
    return "Not drawable"

# Works without inheritance
circle = Circle()
print(render(circle))  # Drawing circle...

Inheritance Hierarchies

Multi-Level Inheritance

from abc import ABC, abstractmethod

# Level 1 - Abstract base
class Vehicle(ABC):
    @abstractmethod
    def start(self):
        pass

# Level 2 - Intermediate abstract
class Car(Vehicle):
    @abstractmethod
    def open_trunk(self):
        pass

# Level 3 - Concrete implementation
class Sedan(Car):
    def start(self):
        return "Engine running"

    def open_trunk(self):
        return "Trunk opened"

# Create instance - O(n) checks hierarchy
sedan = Sedan()
print(sedan.start())        # Engine running
print(sedan.open_trunk())   # Trunk opened

Mixin with Abstract Base

from abc import ABC, abstractmethod

class Logger:
    """Mixin - not abstract"""
    def log(self, message):
        return f"LOG: {message}"

class Serializable(ABC):
    """Abstract mixin"""
    @abstractmethod
    def to_dict(self):
        pass

class User(Logger, Serializable):
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def to_dict(self):
        return {'name': self.name, 'email': self.email}

# Create instance - O(n)
user = User("Alice", "alice@example.com")

# Use mixin methods - O(1)
print(user.log("User created"))     # LOG: User created
print(user.to_dict())               # {'name': 'Alice', ...}

Common Patterns

Factory Pattern with ABC

from abc import ABC, abstractmethod

class Driver(ABC):
    @abstractmethod
    def connect(self):
        pass

class PostgresDriver(Driver):
    def connect(self):
        return "Connected to PostgreSQL"

class MySQLDriver(Driver):
    def connect(self):
        return "Connected to MySQL"

class DriverFactory:
    # O(1) factory lookup
    @staticmethod
    def create_driver(db_type):
        drivers = {
            'postgres': PostgresDriver,
            'mysql': MySQLDriver
        }
        driver_class = drivers.get(db_type)
        if driver_class:
            return driver_class()
        raise ValueError(f"Unknown driver: {db_type}")

# Use factory - O(1)
driver = DriverFactory.create_driver('postgres')
print(driver.connect())  # Connected to PostgreSQL

Template Method Pattern

from abc import ABC, abstractmethod

class DataProcessor(ABC):

    # Template method - defines algorithm structure
    def process(self, data):
        validated = self.validate(data)  # O(?)
        transformed = self.transform(validated)  # O(?)
        return self.save(transformed)  # O(?)

    @abstractmethod
    def validate(self, data):
        pass

    @abstractmethod
    def transform(self, data):
        pass

    @abstractmethod
    def save(self, data):
        pass

class CSVProcessor(DataProcessor):
    def validate(self, data):
        return len(data) > 0

    def transform(self, data):
        return data.upper()

    def save(self, data):
        return f"Saved CSV: {data}"

# Use - O(?) based on implementation
processor = CSVProcessor()
result = processor.process("csv data")

Checking Implementation

Verification Methods

from abc import ABC, abstractmethod

class Interface(ABC):
    @abstractmethod
    def required_method(self):
        pass

class Implementation(Interface):
    def required_method(self):
        return "Implemented"

# Check if subclass - O(n)
print(issubclass(Implementation, Interface))  # True

# Check if instance - O(n)
obj = Implementation()
print(isinstance(obj, Interface))  # True

# Check abstract methods - O(n) to compute set
abstract_methods = Interface.__abstractmethods__
print(abstract_methods)  # frozenset({'required_method'})

Performance Considerations

Instantiation Cost

from abc import ABC, abstractmethod
import time

class AbstractBase(ABC):
    @abstractmethod
    def method(self):
        pass

class Concrete(AbstractBase):
    def method(self):
        return None

# Instantiation checks abstracts - O(n)
start = time.time()
for _ in range(100000):
    obj = Concrete()
elapsed = time.time() - start

# Cost is O(1) per instance, n checks at class definition time

Virtual Subclass Lookups

from abc import ABC

class MyABC(ABC):
    pass

# Register many - O(k) total for k registrations
for i in range(1000):
    class DummyClass:
        pass
    MyABC.register(DummyClass)

# isinstance checks - O(n) in worst case
class TestClass:
    pass
MyABC.register(TestClass)

# Check - O(n) through registered classes
result = isinstance(TestClass(), MyABC)

Best Practices

Do's

  • Use ABC for interface definition
  • Clearly document abstract methods
  • Use @abstractmethod consistently
  • Provide concrete implementations in subclasses
from abc import ABC, abstractmethod

class Cache(ABC):
    """Cache interface for different backends"""

    @abstractmethod
    def get(self, key):
        """Retrieve value by key"""
        pass

    @abstractmethod
    def set(self, key, value):
        """Store key-value pair"""
        pass

Avoid's

  • Don't create ABCs for single-use classes
  • Don't use virtual registration excessively
  • Don't override abstract methods to remove implementation
  • Don't use ABC when simple inheritance suffices