Classes already register what subclasses are defined; call the class.__subclasses__()
method to get a list:
>>> class Monster(object):
... pass
...
>>> class Lochness(Monster):
... pass
...
>>> class Yeti(Monster):
... pass
...
>>> Monster.__subclasses__()
[<class '__main__.Lochness'>, <class '__main__.Yeti'>]
.__subclasses__()
returns a list of currently still alive subclasses. If you ever would clear all references to Yeti
(del Yeti
in the module, delete all instances, subclasses, imports, etc.) then it'd no longer be listed when you call .__subclasses__()
. Note that in essence, .__subclasses__()
is a CPython implementation detail, but the method is present in all Python versions that support new-style classes (2.2 and up, all the way to 3.x).
Otherwise, the canonical way to hook into class creation is to define a metaclass:
class MonstersMeta(type):
def __new__(metaclass, name, bases, namespace):
cls = super(MonstersMeta, metaclass).__new__(metaclass, name, bases, namespace)
if issubclass(cls, Monster) and not cls is Monster:
Monster.monsters.append(cls)
return cls
class Monster(object):
__metaclass__ = MonstersMeta
monsters = []
class Lochness(Monster):
pass
class Yeti(Monster):
pass
Demo:
>>> class Monster(object):
... __metaclass__ = MonstersMeta
... monsters = []
...
>>> class Lochness(Monster):
... pass
...
>>> class Yeti(Monster):
... pass
...
>>> Monster.monsters
[<class '__main__.Lochness'>, <class '__main__.Yeti'>]
or you can use a class decorator:
def registered_monster(cls):
Monster.monsters.append(cls)
return cls
class Monster(object):
monsters = []
@registered_monster
class Lochness(Monster):
pass
@registered_monster
class Yeti(Monster):
pass
Demo:
>>> class Monster(object):
... monsters = []
...
>>> @registered_monster
... class Lochness(Monster):
... pass
...
>>> @registered_monster
... class Yeti(Monster):
... pass
...
>>> Monster.monsters
[<class '__main__.Lochness'>, <class '__main__.Yeti'>]
The difference being where you put the responsibility of registering monsters; with the base MonstersMeta
type, or with explicit decorators.
Either way, the metaclass or the class decorator registers a permanent reference. You can use the weakref
module if you really, really want to emulate the .__subclasses__()
behaviour.