Metaclasses and ABCs
Controlling how classes themselves are built and behave
__init_subclass__ is the right tool; a hand-written metaclass is the heavy artillery you reach for rarely and deliberately.
- explain the chain instance -> class -> metaclass and why
typesits at the top, - create a class dynamically with
type(name, bases, namespace), - use
__init_subclass__to hook subclass creation without a metaclass, - define and enforce an interface with
abc.ABCand@abstractmethod, - write a custom metaclass and, crucially, judge when it actually earns its keep.
1. A class is an object too
You already know every value in Python is an object with a type: type(3) is int, type("hi") is str. The step most developers miss is that this applies to classes themselves. A class is a runtime object, and its type is called its metaclass. By default that metaclass is the built-in type.
class Dog:
pass
fido = Dog()
print(type(fido)) # <class '__main__.Dog'>
print(type(Dog)) # <class 'type'>
print(type(type)) # <class 'type'> -- type is its own type
So there is a chain: an instance is an instance of a class, and a class is an instance of a metaclass. Because Dog is itself an object, you can assign it to a variable, store it in a list, add attributes to it, and — the key insight — you can build it programmatically instead of with the class keyword.