For a Django tab library, I created an architecture that uses tracking of marked subclasses.
To do this, I created a base class and then derive all tab classes from it. I track the descendant classes using a function that recursively uses the cls.__subclasses__() method.
In order to know which subclasses are leaf classes / real tabs, I chose to do this the manual way by adding __tab__ = True to any class I want to show up as a tab. The reason for this is that I’m creating other abstraction classes below TabView that shouldn’t show up as tabs. Maybe this could be re-written as a decorator.
Example:
def get_descendants(cls):
"""Returns all subclasses for cls, and their sublasses, and so on..."""
descendants = []
subclasses = cls.__subclasses__()
for subclass in subclasses:
descendants.append(subclass)
descendants += get_descendants(subclass)
return descendants
def TabView(object):
def _tab_group_members(self):
descendants = get_descendants(TabView)
return [d for d in descendants if '__tab__' in d.__dict__]
(...)
def ConcreteTab(TabView):
__tab__ = True
Now I started reading Marty Alchin’s “Pro Django” book. In there, he proposes the usage of metaclasses to track subclasses:
class SubclassTracker(type):
def __init__(cls, name, bases, attrs):
try:
if TrackedClass not in bases:
return
except NameError:
return
TrackedClass._registry.append(cls)
class TrackedClass(object):
__metaclass__ = SubclassTracker
_registry = []
What are the advantages of the metaclass approach? Is it better than using __subclasses__()?
Using metaclasses means that all the information you stored inside the class itself. It also means the info is stored as the classes are created, rather than scanning all of them afterwards. This means that all you need to worry about is creating classes – they are tracked automatically.