名古屋出身ソフトウェアエンジニアのブログ

Python: 派生クラスをすべて取得できるクラス

公開:
更新:

メタクラスを使う方法

メタクラスでクラス定義時の挙動をカスタマイズすることで、派生したクラスをすべて追跡するクラスを定義できます。自身を除いた TrackableBase のサブクラスが tracked_classes に、クラス定義時に追加されます。

class TrackableMeta(type):
    tracked_classes = []

    def __new__(meta, name, bases, attributes, **kwargs):
        cls = super().__new__(meta, name, bases, attributes, **kwargs)
        # TrackableBase 自体は追跡しない
        try:
            TrackableBase
        except NameError:
            pass
        else:
            TrackableMeta.tracked_classes.append(cls)
        return cls


class TrackableBase(metaclass=TrackableMeta):

    def __new__(cls, *args, **kwargs):
        if cls is TrackableBase:
            raise RuntimeError()
        return super().__new__(cls, *args, **kwargs)


class A(TrackableBase):
    pass


class B(A):
    pass


class C(TrackableBase):
    pass


# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>]
print(TrackableMeta.tracked_classes)

__init_subclass__ を使う方法

Python 3.6 以降は __init_subclass__ 特殊メソッドを使うことで、同様のことをメタクラスなしに実現できます。

class TrackableBase:
    tracked_classes = []

    def __new__(cls, *args, **kwargs):
        if cls is TrackableBase:
            raise RuntimeError()
        return super().__new__(cls, *args, **kwargs)

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        TrackableBase.tracked_classes.append(cls)


class A(TrackableBase):
    pass


class B(A):
    pass


class C(TrackableBase):
    pass


# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>]
print(TrackableBase.tracked_classes)