How to find all the subclasses of a class given its name? How to find all the subclasses of a class given its name? python python

How to find all the subclasses of a class given its name?

New-style classes (i.e. subclassed from object, which is the default in Python 3) have a __subclasses__ method which returns the subclasses:

class Foo(object): passclass Bar(Foo): passclass Baz(Foo): passclass Bing(Bar): pass

Here are the names of the subclasses:

print([cls.__name__ for cls in Foo.__subclasses__()])# ['Bar', 'Baz']

Here are the subclasses themselves:

print(Foo.__subclasses__())# [<class '__main__.Bar'>, <class '__main__.Baz'>]

Confirmation that the subclasses do indeed list Foo as their base:

for cls in Foo.__subclasses__():    print(cls.__base__)# <class '__main__.Foo'># <class '__main__.Foo'>

Note if you want subsubclasses, you'll have to recurse:

def all_subclasses(cls):    return set(cls.__subclasses__()).union(        [s for c in cls.__subclasses__() for s in all_subclasses(c)])print(all_subclasses(Foo))# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}

Note that if the class definition of a subclass hasn't been executed yet - for example, if the subclass's module hasn't been imported yet - then that subclass doesn't exist yet, and __subclasses__ won't find it.

You mentioned "given its name". Since Python classes are first-class objects, you don't need to use a string with the class's name in place of the class or anything like that. You can just use the class directly, and you probably should.

If you do have a string representing the name of a class and you want to find that class's subclasses, then there are two steps: find the class given its name, and then find the subclasses with __subclasses__ as above.

How to find the class from the name depends on where you're expecting to find it. If you're expecting to find it in the same module as the code that's trying to locate the class, then

cls = globals()[name]

would do the job, or in the unlikely case that you're expecting to find it in locals,

cls = locals()[name]

If the class could be in any module, then your name string should contain the fully-qualified name - something like 'pkg.module.Foo' instead of just 'Foo'. Use importlib to load the class's module, then retrieve the corresponding attribute:

import importlibmodname, _, clsname = name.rpartition('.')mod = importlib.import_module(modname)cls = getattr(mod, clsname)

However you find the class, cls.__subclasses__() would then return a list of its subclasses.

If you just want direct subclasses then .__subclasses__() works fine. If you want all subclasses, subclasses of subclasses, and so on, you'll need a function to do that for you.

Here's a simple, readable function that recursively finds all subclasses of a given class:

def get_all_subclasses(cls):    all_subclasses = []    for subclass in cls.__subclasses__():        all_subclasses.append(subclass)        all_subclasses.extend(get_all_subclasses(subclass))    return all_subclasses

The simplest solution in general form:

def get_subclasses(cls):    for subclass in cls.__subclasses__():        yield from get_subclasses(subclass)        yield subclass

And a classmethod in case you have a single class where you inherit from:

@classmethoddef get_subclasses(cls):    for subclass in cls.__subclasses__():        yield from subclass.get_subclasses()        yield subclass