我需要一种工作方法来获取从Python基类继承的所有类。


当前回答

这个答案不如使用@unutbu提到的特殊内置__subclasses__()类方法好,所以我只是把它作为一个练习。subclasses()函数的定义返回一个字典,该字典将所有子类名称映射到子类本身。

def traced_subclass(baseclass):
    class _SubclassTracer(type):
        def __new__(cls, classname, bases, classdict):
            obj = type(classname, bases, classdict)
            if baseclass in bases: # sanity check
                attrname = '_%s__derived' % baseclass.__name__
                derived = getattr(baseclass, attrname, {})
                derived.update( {classname:obj} )
                setattr(baseclass, attrname, derived)
             return obj
    return _SubclassTracer

def subclasses(baseclass):
    attrname = '_%s__derived' % baseclass.__name__
    return getattr(baseclass, attrname, None)


class BaseClass(object):
    pass

class SubclassA(BaseClass):
    __metaclass__ = traced_subclass(BaseClass)

class SubclassB(BaseClass):
    __metaclass__ = traced_subclass(BaseClass)

print subclasses(BaseClass)

输出:

{'SubclassB': <class '__main__.SubclassB'>,
 'SubclassA': <class '__main__.SubclassA'>}

其他回答

一般形式的最简单解:

def get_subclasses(cls):
    for subclass in cls.__subclasses__():
        yield from get_subclasses(subclass)
        yield subclass

和类方法,如果你有一个单一的类,你继承:

@classmethod
def get_subclasses(cls):
    for subclass in cls.__subclasses__():
        yield from subclass.get_subclasses()
        yield subclass

下面是一个简单但有效的代码版本:

def get_all_subclasses(cls):
    subclass_list = []

    def recurse(klass):
        for subclass in klass.__subclasses__():
            subclass_list.append(subclass)
            recurse(subclass)

    recurse(cls)

    return set(subclass_list)

它的时间复杂度是O(n)如果没有多重继承,n是所有子类的数目。 它比递归地创建列表或使用生成器生成类的函数更有效,后者的复杂度可能是(1)O(nlogn)当类层次结构是平衡树时,或(2)O(n²)当类层次结构是有偏树时。

我怎么能找到一个类的所有子类给它的名字?

我们当然可以很容易地做到这一点,只要能访问对象本身。

仅仅给出它的名字是一个糟糕的想法,因为可以有多个同名的类,甚至在同一个模块中定义。

我为另一个答案创建了一个实现,因为它回答了这个问题,而且它比这里的其他解决方案更优雅,下面是:

def get_subclasses(cls):
    """returns all subclasses of argument, cls"""
    if issubclass(cls, type):
        subclasses = cls.__subclasses__(cls)
    else:
        subclasses = cls.__subclasses__()
    for subclass in subclasses:
        subclasses.extend(get_subclasses(subclass))
    return subclasses

用法:

>>> import pprint
>>> list_of_classes = get_subclasses(int)
>>> pprint.pprint(list_of_classes)
[<class 'bool'>,
 <enum 'IntEnum'>,
 <enum 'IntFlag'>,
 <class 'sre_constants._NamedIntConstant'>,
 <class 'subprocess.Handle'>,
 <enum '_ParameterKind'>,
 <enum 'Signals'>,
 <enum 'Handlers'>,
 <enum 'RegexFlag'>]

虽然我非常倾向于__init_subclass__方法,这将保留定义顺序,并避免组合增长顺序,如果你有一个非常密集的层次结构,到处都有多个继承:

def descendents(cls):
    '''Does not return the class itself'''
    R = {}
    def visit(cls):
        for subCls in cls.__subclasses__():
            R[subCls] = True
            visit(subCls)
    visit(cls)
    return list(R.keys())

这是因为字典会记住键的插入顺序。列表方法也会起作用。

Python 3.6 - __init_subclass__

正如其他回答提到的,你可以检查__subclasses__属性来获得子类列表,因为python 3.6你可以通过重写__init_subclass__方法来修改这个属性的创建。

class PluginBase:
    subclasses = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.subclasses.append(cls)

class Plugin1(PluginBase):
    pass

class Plugin2(PluginBase):
    pass

这样,如果你知道你在做什么,你可以重写__subclasses__的行为,并从这个列表中省略/添加子类。