Class hooks and metaclasses
A class can react whenever it is subclassed. Python calls __init_subclass__ on the parent with the new class as cls, and passes it any keyword arguments from the class line.
class Plugin:
registry = {}
def __init_subclass__(cls, key, **kw):
super().__init_subclass__(**kw)
Plugin.registry[key] = cls
class Csv(Plugin, key="csv"):
pass
class Json(Plugin, key="json"):
pass
print(Plugin.registry)This covers most needs: registering plugins, checking that a subclass defines what it must, or filling in defaults.
Metaclasses
class Tracked(type):
def __init__(cls, name, bases, ns):
super().__init__(name, bases, ns)
print("created", name)
class Model(metaclass=Tracked):
pass
print(type(Model).__name__, type(int))Classes are objects too, and the type of a class is its metaclass, normally type. A metaclass subclasses type and controls how classes are built; ABC and Enum rely on one. Reach for a metaclass only when __init_subclass__ or a class decorator cannot do the job, because metaclasses are hard to combine.
Your task
Give Command an __init_subclass__ that takes a keyword name and registers each subclass in Command.registry, raising TypeError if the subclass has no run method or the name is already taken. dispatch(name) creates the registered command and returns its run() result, raising KeyError for unknown names.
The explanation opens here once every check passes.