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']