Edit: My old post actually didn't work. Subclassing from classmethod
doesn't work as expected.
First, we would like to have some way to tell the metaclass that this particular method is supposed to have the special called on subclass behavior, we'll just set an attribute on the function we'd like to call. As a convenience, we'll even turn the function into a classmethod
so that the real baseclass it was found in can be discovered, too. We'll return the classmethod so that it can be used as a decorator, which is most convenient.
import types
import inspect
def subclass_hook(func):
func.is_subclass_hook = True
return classmethod(func)
We're also going to want a convenient way to see that the subclass_hook
decorator was used. We know that classmethod
has been used, so we'll check for that, and only then look for the is_subclass_hook
attribute.
def test_subclass_hook(thing):
x = (isinstance(thing, types.MethodType) and
getattr(thing.im_func, 'is_subclass_hook', False))
return x
Finally, we need a metaclass that acts on the information: For most cases, the most interesting thing to do here is just check each of the supplied bases for hooks. In that way, super works in the least surprising way.
class MyMetaclass(type):
def __init__(cls, name, bases, attrs):
super(MyMetaclass, cls).__init__(name, bases, attrs)
for base in bases:
if base is object:
continue
for name, hook in inspect.getmembers(base, test_subclass_hook):
hook(cls)
and that should do it.
>>> class SuperClass:
... __metaclass__ = MyMetaclass
... @subclass_hook
... def triggered_routine(cls, subclass):
... print(cls.__name__ + " was subclassed by " + subclass.__name__)
>>> class SubClass0(SuperClass):
... pass
SuperClass was subclassed by SubClass0
>>> class SubClass1(SuperClass):
... print("test")
test
SuperClass was subclassed by SubClass1