Executing code after construction of class object
python
Solution
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.
Problem
I was hoping to make a list of all subclasses of a given class by having each subclass register itself in a list that a parent class holds, ie something like this: ``` class Monster(object): monsters = list() class Lochness(Monster): Monster.monsters.append(Lochness) class Yeti(Monster): Monster.monsters.append(Yeti) ``` This obviously doesn't work because the classes haven't been created yet when I want to add them to the list. And, it'd be much nicer if it were done automatically (like __subclass__) I'm aware that __subclass__ has this functionality, but I was wondering (for my own edification) how you'd implement it yourself. It seems like you'd want to create some sort of subclass of the metaclass which is creating everything to register it with Monster? Or is that completely off base