Domanda

Is there a way to execute some code in Python when a class is extended by another one? More precisely, I would like to automatically keep a register of all the classes extending a base class. Therefore, I would like to write something like:

class Base:

    subclasses = {}

    @classmethod
    def extending(cls, subclass):
        cls.subclasses[subclass.__name__] = subclass

class Extend1(Base):
    pass

class Extend2(Base):
    pass

After this piece of code, I would expect Base.subclasses to contain links to the two extending classes.

Can you suggest a way to make this work? Or, maybe, provide an equivalent solution?

È stato utile?

Soluzione

This is clearly an application for metaclasses. That is if you want to change the behavior of a classe C then you should change the class of C itself which is a metaclass, usually type. Here is a stub of solution:

class Meta(type):
    def __new__(cls, clsname, bases, dct):
        res = type.__new__(cls, clsname, bases, dct)
        for cls in bases:
            if isinstance(cls, Meta):
                try:
                    cls.extending(res)
                except AttributeError:
                    pass
        return res

I'm inheriting from type to make a metaclass intercepting the creation of the class and storing the created class in the base _subclass attribute. Then with

class Base(object):
    __metaclass__ = Meta

    subclasses = {}

    @classmethod
    def extending(cls, subclass):
        cls.subclasses[subclass.__name__] = subclass

class Extend1(Base):
    pass

class Extend2(Base):
    pass

Then

>>> Base.subclasses
{'Extend1': __main__.Extend1, 'Extend2': __main__.Extend2}

A very important point: metaclass are inherited (I think this is not the exactly the proper term here). Therefore Extend1 and Extend2 are also instances of Meta:

>>> type(Extend1)
<class '__main__.Meta'>

Altri suggerimenti

actually, if you're only interested in subclasses, python can give you that automatically. all new style classes have a __subclasses__() method which returns all of that classes' direct descendants.

>>> class Base(object):
...     pass
...
>>> class Child(Base):
...     pass
...
>>> Base.__subclasses__()
[<class '__main__.Child'>]
>>>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top