Again, the fact that abc.ABC
is designed to create mixin classes and is a metaclass itself makes real-world examples easy to imagine, but not necessarily easy to find in the real world precisely because the risk of metaclass conflicts makes developers ditch the good OOD principle of designing mixin classes with abstract methods.
Consider the example I brought up in my other proposal, where I pointed to the helper mixin class I created to simplify the logics of making a class a context manager, leaving __with__
an abstract method for a subclass to implement:
from abc import ABC, abstractmethod
from contextlib import contextmanager
class ContextManagerMixin(ABC):
def __init__(self):
self._contexts = []
def __init_subclass__(cls):
cls.__with__ = contextmanager(cls.__with__)
@abstractmethod
def __with__(self): ...
def __enter__(self):
context = self.__with__()
self._contexts.append(context)
return context.__enter__()
def __exit__(self, exc_type, exc_value, traceback):
return self._contexts.pop().__exit__(exc_type, exc_value, traceback)
Now if a developer wants to create a base class of a general resource manager that uses a metaclass for a dynamic class factory code pattern, and wants to make the base class a context manager, he/she can choose to implement __enter__
and __exit__
himself/herself, or can use the helper mixin class to do so. With Python proclaiming a metaclass conflict when he/she tries to use the helper mixin class, he/she resorts to implementing __enter__
and __exit__
on his/her own, missing out on reusing a good piece of code.
Multiple inheritance isn’t just about composition, making multiple "is a"s true, but more often about using mixins to incorporate multiple distinct features into a class to maximize code reusability. With abc.ABC
being a metaclass often standing in the way, this proposal can make abstract class patterns common again.