In Python, given a module X and a class Y, how can I iterate or generate a list of all subclasses of Y that exist in module X?
+2
A:
Here's one way to do it:
import inspect
def get_subclasses(mod, cls):
"""Yield the classes in module ``mod`` that inherit from ``cls``"""
for name, obj in inspect.getmembers(mod):
if hasattr(obj, "__bases__") and cls in obj.__bases__:
yield obj
Chris AtLee
2008-09-04 18:20:21
My solution won't return classes that are not direct descendants of 'cls'. quamrana's solution below will find any class that has 'cls' somewhere in its ancestry.
Chris AtLee
2008-09-15 15:41:36
+1
A:
Given the module foo.py
class foo(object): pass
class bar(foo): pass
class baz(foo): pass
class grar(Exception): pass
def find_subclasses(module, clazz):
for name in dir(module):
o = getattr(module, name)
try:
if issubclass(o, clazz):
yield name, o
except TypeError: pass
>>> import foo
>>> list(foo.find_subclasses(foo, foo.foo))
[('bar', <class 'foo.bar'>), ('baz', <class 'foo.baz'>), ('foo', <class 'foo.foo'>)]
>>> list(foo.find_subclasses(foo, object))
[('bar', <class 'foo.bar'>), ('baz', <class 'foo.baz'>), ('foo', <class 'foo.foo'>), ('grar', <class 'foo.grar'>)]
>>> list(foo.find_subclasses(foo, Exception))
[('grar', <class 'foo.grar'>)]
Aaron Maenpaa
2008-09-04 18:29:59
+4
A:
Can I suggest that neither of the answers from Chris AtLee and zacherates fulfill the requirements? I think this modification to zacerates answer is better:
def find_subclasses(module, clazz):
for name in dir(module):
o = getattr(module, name)
try:
if (o != clazz) and issubclass(o, clazz):
yield name, o
except TypeError: pass
The reason I disagree with the given answers is that the first does not produce classes that are a distant subclass of the given class, and the second includes the given class.
quamrana
2008-09-05 22:54:42
+8
A:
Although Quamrana's suggestion works fine, there are a couple of possible improvements I'd like to suggest to make it more pythonic. They rely on using the inspect module from the standard library.
- You can avoid the getattr call by using
inspect.getmembers()
- The try/catch can be avoided by using
inspect.isclass()
With those, you can reduce the whole thing to a single list comprehension if you like:
def find_subclasses(module, clazz):
return [ cls for cls in inspect.getmembers(module) if inspect.isclass(cls) and
issubclass(cls, clazz) ]
runeh
2009-01-03 01:56:21
inspect.getmembers returns a tuple, so you wantcls for name, cls in inspect.getmembers(module)
jelovirt
2009-07-20 09:23:05
Works great, but my response also returns the base class (the one I send in with clazz), any idea?
fredrik
2010-02-12 14:55:27
Fredrik, turns out issubclass(Foo, Foo) is True. Easy fix though. add "and not cls is clazz" to the list comprehension
runeh
2010-03-19 23:32:46