This recipe allows a base class to keep track of the child classes that inherit from it using a metaclass.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class ChildTracker(type):
def __new__(cls, name, bases, dict_):
new_class = type.__new__(cls, name, bases, dict_)
# Check if this is the tracking class
if '__metaclass__' in dict_ and dict_['__metaclass__']==ChildTracker:
new_class.child_classes = {}
else:
# Add the new class to the set
new_class.child_classes[name] = new_class
return new_class
class BaseClass(object):
__metaclass__ = ChildTracker
class Child1(BaseClass):
pass
class Child2(BaseClass):
pass
print BaseClass.child_classes.keys()
|
Every new-style Python class keeps track of its immediate child classes through the __subclass__ method. However, in most cases we also want to know about the grandchildren etc. This is an example of how that can be achieved using metaclasses. An alternative approach could be to walk through the items returned by __subclasses__().
The base class is given the meta class, and the meta class extends the baseclass with a variable 'child_classes'. This variable is a dictionary containing name:class pairs.
A nice feature is that this also works if the child class is located in a different module than the base, unlike some other ways I have seen of finding child classes. However, it can not handle two classes with the same names but in different modules.
It works also during runtime: every time a new child is created, it is added.
Why aren't you just using
BaseClass.__subclasses__()
? Am I missing a point here or are you?