Is there any way in python to query a namespace for classes which inherit from a particular class? Given a class widget
I'd like to be able to call something like inheritors(widget)
to get a list of all my different kinds of widget.
Asked
Active
Viewed 5.4k times
53

kdt
- 27,905
- 33
- 92
- 139
-
You should try to search. This question has been asked more than once. – S.Lott May 04 '11 at 14:09
4 Answers
80
You want to use Widget.__subclasses__()
to get a list of all the subclasses. It only looks for direct subclasses though so if you want all of them you'll have to do a bit more work:
def inheritors(klass):
subclasses = set()
work = [klass]
while work:
parent = work.pop()
for child in parent.__subclasses__():
if child not in subclasses:
subclasses.add(child)
work.append(child)
return subclasses
N.B. If you are using Python 2.x this only works for new-style classes.

Duncan
- 92,073
- 11
- 122
- 156
-
1So in other words, if you are on Python 2.x, have your super class inherit from `object`: `class SomeClass(object):`. – RattleyCooper Apr 11 '16 at 20:53
-
This `__subclasses__()` approach only works if the parent class and subclasses are in the same module. If you define your subclasses in some other module then you won't find them – Kashif Siddiqui Mar 02 '17 at 00:33
-
3@KashifSiddiqui that's not true. `__subclasses__` includes all subclasses no matter where they're declared. However it won't see them until after the relevant subclasses have been defined, so if you just extract the list of subclasses at the end of the module that defined the base class the subclasses in other modules won't yet exist.. – Duncan Mar 02 '17 at 09:13
-
@Duncan yeah you are right that the base class will detect any subclass as soon as we import that subclass (Don't even need to instantiate it). But doesn't that defeat the purpose of "detecting" a subclass. The use case I was referring to is when we create a base let's say `Handler` or something and then define `SubHandlers` somewhere else. Then in order to detect those handlers the app would need to import all subclasses in order for the base `Handler` to detect all `__subclasses__` – Kashif Siddiqui Mar 03 '17 at 00:26
-
2@KashifSiddiqui, yes because until you import them they aren't subclasses, they're just some text in a file. So you import a bunch of files and then need to know all the subclasses they defined. It's quite a common scenario. – Duncan Mar 03 '17 at 09:12
-
You don't need the `if child not in subclasses` since set addition is idempotent, and inheritance prevents cycles. – Josh Albert Apr 30 '22 at 18:41
23
You can track inheritance with your own metaclass
import collections
class A(object):
class __metaclass__(type):
__inheritors__ = defaultdict(list)
def __new__(meta, name, bases, dct):
klass = type.__new__(meta, name, bases, dct)
for base in klass.mro()[1:-1]:
meta.__inheritors__[base].append(klass)
return klass
class B(A):
pass
class C(B):
pass
>>> A.__inheritors__
defaultdict(<type 'list'>, {<class '__main__.A'>: [<class '__main__.B'>, <class '__main__.C'>], <class '__main__.B'>: [<class '__main__.C'>]})
Anything that is inherited from A
or it's derived classes will be tracked. You will get full inheritance map when all the modules in your application are loaded.

Imran
- 87,203
- 23
- 98
- 131
-
3The subclass relationship is already tracked for you by Python's `__subclass__()` method. – Duncan May 04 '11 at 12:25
-
2Thanks for pointing it out, totally forgot about this. My solution also tracks the indirect subclasses, the extra work you talked about in your answer. – Imran May 04 '11 at 12:43
1
# return list of tuples (objclass, name) containing all subclasses in callers' module
def FindAllSubclasses(classType):
import sys, inspect
subclasses = []
callers_module = sys._getframe(1).f_globals['__name__']
classes = inspect.getmembers(sys.modules[callers_module], inspect.isclass)
for name, obj in classes:
if (obj is not classType) and (classType in inspect.getmro(obj)):
subclasses.append((obj, name))
return subclasses

formiaczek
- 385
- 3
- 7
0
You have to walk through all objects in the global namespace (globals()) and check if the related object/class is a subclass of the some other class (check the Python docs for issubclass()).