Python Abstract classes
Abstract classes were added to Python with PEP-31191. One of the goals was to add interfaces to reinforce type checking. For instance:
from abc import ABC, abstractmethod
class AbstractFoo(ABC):
@abstractmethod
def calculate_foo(self):
pass
def foo(self):
return self.calculate_foo()
Sub-class hooks
The Abstract class implementation in Python has have a way of determinining what is a child of it even if it’s not a direct subclass of it. This is done by using sub-class hooks. For instance:
class AbstractFoo(ABC):
@abstractmethod
def calculate_foo(self):
pass
def foo(self):
return self.calculate_foo()
@classmethod
def __subclasshook__(cls, C):
return hasattr(C, "calculate_foo")
will return True
if a class implements calculate_foo
, even if it’s not a sub-class of AbstractFoo
.
For instance:
class NotAFoo:
def calculate_foo(self):
print("I can calculate Foo!")
isinstance(NotAFoo(), AbstractFoo)
True
Since __subclasshook__
can contain any code that can be evaluated at runtime, we can get creative.
For instance, we can create an abstract class for classes that have names like 1960s Frank Zappa albums.
class SixtiesZappa(ABC):
@classmethod
def __subclasshook__(cls, C):
albums = [album.title().replace(" ", "") for album in
["Freak Out", "Absolutely Free", "Lumpy Gravy", "We're Only in It for the Money", "Cruising with Ruben & the Jets", "Mothermania", "Uncle Meat", "Hot Rats"]]
name = C.__name__
return name in albums
class HotRats:
pass
isinstance(HotRats(), SixtiesZappa)
True
class WeaselsRippedMyFlesh:
pass
isinstance(WeaselsRippedMyFlesh(), SixtiesZappa)
False
PEP-3119, from 2007: https://peps.python.org/pep-3119/ ↩︎