How to Find All the Subclasses of a Class?

Rate this post

Problem Formulation

Given a class by name (string). How to find all the subclasses of the given class?

Example: Here’s an example scenario with a subclass hierarchy.

class Parent:
    pass

class Daughter(Parent):
    pass

class Son(Parent):
    pass

class Grandson(Son):
    pass

Desired outputs: Next, let’s quickly establish what you want to accomplish with two examples.

Given: Son
Result: Grandson


Given: Parent
Result: Daughter, Son

For the most basic cases, we don’t assume a recursive solution requirement, so we don’t consider Grandson also a subclass of Parent.

Solution If We Have the Class Object, Not Only the Class Name

Assume (for now), we have the class object. We’re looking at a slightly more complicated case without this assumption in the next section of this article.

In this case, we can simply get all subclasses of the class using the my_class.__subclasses__() magic method:

class Parent:
    pass

class Daughter(Parent):
    pass

class Son(Parent):
    pass

class Grandson(Son):
    pass


print(Parent.__subclasses__())
# [<class '__main__.Daughter'>, <class '__main__.Son'>]

If you only need a list of all names of subclasses, and not the whole clutter around the output, you can use a list comprehension statement like so:

names = [cls.__name__ for cls in Parent.__subclasses__()]
print(names)
# ['Daughter', 'Son']

Recursive for All Subclasses

You can get all direct and indirect subclasses (if hierarchical inheritance is used) using a recursive approach:

  • Create a function subclass_recursive() that takes one argument: the base class from which the subclasses should be found.
  • Find all direct subclasses using the __subclasses__() magic method.
  • Find all indirect subclasses by calling the function recursively.
  • Return the concatenation of direct and indirect subclasses to the caller.

If you need a refresher on recursion, check out my in-depth tutorial here.

Let’s dive into the code!

class Parent:
    pass

class Daughter(Parent):
    pass

class Son(Parent):
    pass

class Grandson(Son):
    pass

class GrandGrandSon(Son):
    pass


def subclasses_recursive(cls):
    direct = cls.__subclasses__()
    indirect = []
    for subclass in direct:
        indirect.extend(subclasses_recursive(subclass))
    return direct + indirect

print(subclasses_recursive(Parent))

The output is the list of all subclasses:

[<class '__main__.Daughter'>, <class '__main__.Son'>, 
 <class '__main__.Grandson'>, <class '__main__.GrandGrandSon'>]

Again, if you need to have only the class names as strings, use list comprehension with the __name__ special attribute on the subclasses.

names = [cls.__name__ for cls in subclasses_recursive(Parent)]
print(names)
# ['Daughter', 'Son', 'Grandson', 'GrandGrandSon']

If We Only Have the String Name of the Class

You can use the globals() built-in function to find out the class object, given its name:

# Get class given name of class:
base_name = 'Parent'
base_class = globals()[base_name]

Feel free to watch my background video on the function next:

If this doesn’t work, you may want to check out the locals() function too.

# Get class given name of class:
base_name = 'Parent'
base_class = locals()[base_name]

Finally, another way to get the class given its full path is outlined here:

import importlib
module_name, _, base_name = name.rpartition('.')
module = importlib.import_module(module_name)
base_class = getattr(module, base_name)

Putting It All Together

class Parent:
    pass

class Daughter(Parent):
    pass

class Son(Parent):
    pass

class Grandson(Son):
    pass

class GrandGrandSon(Son):
    pass

# Get class given name of class:
base_name = 'Parent'
base_class = globals()[base_name]

# Get list of direct subclasses:
print(base_class.__subclasses__())
# [<class '__main__.Daughter'>, <class '__main__.Son'>]

# Get list of direct subclass names:
names = [cls.__name__ for cls in base_class.__subclasses__()]
print(names)
# ['Daughter', 'Son']

# Get list of direct and indirect subclasses:
def subclasses_recursive(cls):
    direct = cls.__subclasses__()
    indirect = []
    for subclass in direct:
        indirect.extend(subclasses_recursive(subclass))
    return direct + indirect

# Get list of direct and indirect subclasse names:
names = [cls.__name__ for cls in subclasses_recursive(base_class)]
print(names)
# ['Daughter', 'Son', 'Grandson', 'GrandGrandSon']