Iterar sobre subclasses de uma determinada classe em um determinado módulo

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

  •  09-06-2019
  •  | 
  •  

Pergunta

Em Python, dado um módulo X e uma classe Y, como posso iterar ou gerar uma lista de todas as subclasses de Y que existem no módulo X?

Foi útil?

Solução

Esta é uma maneira de fazer isso:

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

Outras dicas

Embora a sugestão de Quamrana funcione bem, há algumas melhorias possíveis que gostaria de sugerir para torná-la mais pitônica.Eles dependem do uso do módulo inspecionar da biblioteca padrão.

  1. Você pode evitar a chamada getattr usando inspect.getmembers()
  2. O try/catch pode ser evitado usando inspect.isclass()

Com eles, você pode reduzir tudo a uma única compreensão de lista, se desejar:

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

Posso sugerir que nenhuma das respostas de Chris AtLee e zacherates atende aos requisitos?Acho que esta modificação na resposta do zacerates é melhor:

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

A razão pela qual discordo das respostas dadas é que a primeira não produz classes que sejam uma subclasse distante da classe dada, e a segunda inclui a classe dada.

Dado o 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top