Generic metaclass to keep track of subclasses?

前端 未结 3 1205
生来不讨喜
生来不讨喜 2021-02-20 05:26

I\'m trying to write a generic metaclass to track subclasses

Since I want this to be generic, I didn\'t want to hardcode any class name within this metaclass, therefore

相关标签:
3条回答
  • 2021-02-20 05:56

    Here's something I've been playing around with (that works):

    def sublass_registry():
        ''' Create a metaclass to register subclasses '''
    
        class SublassRegistryMeta(type):
            def __init__(cls, name, bases, classdict):
                if classdict.get('__metaclass__') is SublassRegistryMeta:
                    SublassRegistryMeta.lineage = [cls] # put root class at head of a list
                else:
                    # sublclasses won't have __metaclass__ explicitly set to this class
                    # we know they're subclassees because this ctor is being called for them
                    SublassRegistryMeta.lineage.append(cls) # add subclass to list
                type.__init__(cls, name, bases, classdict)
    
        return SublassRegistryMeta
    
    def subclasses(cls):
        ''' Return a list containing base and subclasses '''
    
        try:
            if cls.__metaclass__.lineage[0] is cls: # only valid for a root class
                return cls.__metaclass__.lineage
        except AttributeError:
            pass
        return None
    
    class Car(object): # root class
        __metaclass__ = sublass_registry()
    
    class Audi(Car): # inherits __metaclass__
        pass
    
    class Ford(Car): # inherits __metaclass__
        pass
    
    class Audi2(Audi): # sub-subclass also inherits __metaclass__
        pass
    
    print subclasses(Car)
    # [<class '__main__.Car'>, <class '__main__.Audi'>, <class '__main__.Ford'>, <class '__main__.Audi2'>]
    print subclasses(Audi)
    # None
    
    0 讨论(0)
  • 2021-02-20 06:06

    Python does this automatically for new-style classes, as mentioned in this answer to the similar queston How to find all the subclasses of a class given its name? here.

    0 讨论(0)
  • 2021-02-20 06:14

    I think you want something like this (untested):

    class SubclassTracker(type):
        def __init__(cls, name, bases, dct):
            if not hasattr(cls, '_registry'):
                cls._registry = []
            print('registering %s' % (name,))
            cls._registry.append(cls)
            super(SubclassTracker, cls).__init__(name, bases, dct)
    

    Then, for Python 2, you can invoke it like:

    class Root(object):
        __metaclass__ = SubclassTracker
    

    for Python 3

    class Root(object, metaclass=SubclassTracker):
    

    Note that you don't need to stick the _registry attribute on there because stuff like that is what metaclasses are for. Since you already happen to have one laying around... ;)

    Note also that you might want to move the registration code into an else clause so that the class doesn't register itself as a subclass.

    0 讨论(0)
提交回复
热议问题