Iterar sobre subclases de una clase determinada en un módulo determinado

StackOverflow https://stackoverflow.com/questions/44352

  •  09-06-2019
  •  | 
  •  

Pregunta

En Python, dado un módulo X y una clase Y, ¿cómo puedo iterar o generar una lista de todas las subclases de Y que existen en el módulo X?

¿Fue útil?

Solución

Aquí tienes una forma de hacerlo:

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

Otros consejos

Aunque la sugerencia de Quamrana funciona bien, hay un par de posibles mejoras que me gustaría sugerir para hacerla más pitónica.Se basan en el uso del módulo de inspección de la biblioteca estándar.

  1. Puedes evitar la llamada getattr usando inspect.getmembers()
  2. El try/catch se puede evitar usando inspect.isclass()

Con ellos, puedes reducir todo a una sola lista de comprensión si lo deseas:

def find_subclasses(module, clazz):
    return [
        cls
            for name, cls in inspect.getmembers(module)
                if inspect.isclass(cls) and issubclass(cls, clazz)
    ]

¿Puedo sugerir que ninguna de las respuestas de Chris AtLee y zacherates cumple con los requisitos?Creo que esta modificación a la respuesta de zacerates es mejor:

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

La razón por la que no estoy de acuerdo con las respuestas dadas es que la primera no produce clases que sean una subclase distante de la clase dada, y la segunda incluye la clase dada.

Dado el módulo 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'>)]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top