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