How to run code when a class is subclassed?

inheritance, python

Solution

Classes (by default) are instances of `type`. Just as an instance of a class `Foo` is created by `foo = Foo(...)`, an instance of `type` (i.e. a class) is created by `myclass = type(name, bases, clsdict)`.

If you want something special to happen at the moment of class-creation, then you have to modify the thing creating the class -- i.e. `type`. The way to do that is to define a subclass of `type` -- i.e. a metaclass.

A metaclass is to its class as a class is to its instance.

In Python2 you would define the metaclass of a class with

class SuperClass:
    __metaclass__ = Watcher

where `Watcher` is a subclass of `type`.

In Python3 the syntax has been changed to

class SuperClass(metaclass=Watcher)

Both are equivalent to

Superclass = Watcher(name, bases, clsdict)

where in this case, `name` equals the string `'Superclass'`, and `bases` is the tuple `(object, )`. The `clsdict` is a dictionary of the class attributes defined in the body of the class definition.

Note the similarity to `myclass = type(name, bases, clsdict)`.

So, just as you would use a class's `__init__` to control events at the moment of a instance's creation, you can control events at the moment of a class's creation with a metaclass's `__init__`:

class Watcher(type):
    def __init__(cls, name, bases, clsdict):
        if len(cls.mro()) > 2:
            print("was subclassed by " + name)
        super(Watcher, cls).__init__(name, bases, clsdict)

class SuperClass:
    __metaclass__ = Watcher


print("foo")

class SubClass0(SuperClass):
  pass

print("bar")

class SubClass1(SuperClass):
  print("test")

prints

foo
was subclassed by SubClass0
bar
test
was subclassed by SubClass1

Problem

Is there a way to trigger code when my class is subclassed? ``` class SuperClass: def triggered_routine(subclass): print("was subclassed by " + subclass.__name__) magically_register_triggered_routine() print("foo") class SubClass0(SuperClass): pass print("bar") class SubClass1(SuperClass): print("test") ``` Should output ``` foo was subclassed by SubClass0 bar test was subclassed by SubClass1 ```

Original source

Related problems