How to find all the subclasses of a class given its name?
我需要一种工作方法来获取从Python中的基类继承的所有类。
新风格(I.E.subclassed from EDOCX1&0),which is the default in Python 3)have a EDOCX1&1)method which returns the subclasses:
1 2 3 4 | class Foo(object): pass class Bar(Foo): pass class Baz(Foo): pass class Bing(Bar): pass |
There are the names of the subclasses:
ZZU1
这里是Themselves的亚类:
1 2 | print(Foo.__subclasses__()) # [<class '__main__.Bar'>, <class '__main__.Baz'>] |
2.确认次类为
1 2 3 4 | for cls in Foo.__subclasses__(): print(cls.__base__) # <class '__main__.Foo'> # <class '__main__.Foo'> |
如果你想要补助金,你必须退还:
1 2 3 4 5 6 | def all_subclasses(cls): return set(cls.__subclasses__()).union( [s for c in cls.__subclasses__() for s in all_subclasses(c)]) print(all_subclasses(Foo)) # {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>} |
注:如果一个亚类的分类定义没有被执行,例如,亚类的模块没有被进口,那么亚类不存在,
你说过"给他名字"自从Python类是第一类物体,你不需要用一条带有该类物体名称的条纹来代替该类物体或任何类似物体。你可以直接使用班级,你也许应该。
如果你有一个字符串代表一个班的名称,你想找到这个班的亚班,那么就有两步:找到它的名字,然后用
如何从名称中找到这一类取决于你打算在哪找到它。如果你指望在同一个模块中找到它作为代码
1 | cls = globals()[name] |
你可以做这份工作,或者在你指望在当地找到的不寻常的情况下,
1 | cls = locals()[name] |
如果班级可以在任何模块中,那么你的名字应该包含完全合格的名字使用
1 2 3 4 | import importlib modname, _, clsname = name.rpartition('.') mod = importlib.import_module(modname) cls = getattr(mod, clsname) |
你怎么找到班级,
如果你只想直接上亚班,那么就做得好。如果你想要所有的亚类,亚类,那么你需要一个功能来为你。
这是一个简单的、可实现的功能,它可归纳出一个吉普赛人的所有亚类:
1 2 3 4 5 6 7 8 | def get_all_subclasses(cls): all_subclasses = [] for subclass in cls.__subclasses__(): all_subclasses.append(subclass) all_subclasses.extend(get_all_subclasses(subclass)) return all_subclasses |
The simplest solution in general form:
1 2 3 4 | def get_subclasses(cls): for subclass in cls.__subclasses__(): yield from get_subclasses(subclass) yield subclass |
一种分类方法,以你有一个单一的类别,你来自:
1 2 3 4 5 | @classmethod def get_subclasses(cls): for subclass in cls.__subclasses__(): yield from subclass.get_subclasses() yield subclass |
Python3.6
作为另一个答案,你可以检查
1 2 3 4 5 6 7 8 9 10 11 12 | class PluginBase: subclasses = [] def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.subclasses.append(cls) class Plugin1(PluginBase): pass class Plugin2(PluginBase): pass |
如果你知道自己在做什么,你可以超越EDOCX1的行为和OMIT/ADD的亚类。
FWIW,here's what I meant about@unutbu's answer only working with local defined classes&MDASH;and which using EDOCX1,12,instead of EDOCX1,13,13,would make it work with any accessible class,not only those defined in the current scop
对于那些用
第一个例子是利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class Foo(object): pass class Bar(Foo): pass class Baz(Foo): pass class Bing(Bar): pass # unutbu's approach def all_subclasses(cls): return cls.__subclasses__() + [g for s in cls.__subclasses__() for g in all_subclasses(s)] print(all_subclasses(vars()['Foo'])) # Fine because Foo is in scope # -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>] def func(): # won't work because Foo class is not locally defined print(all_subclasses(vars()['Foo'])) try: func() # not OK because Foo is not local to func() except Exception as e: print('calling func() raised exception: {!r}'.format(e)) # -> calling func() raised exception: KeyError('Foo',) print(all_subclasses(eval('Foo'))) # OK # -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>] # using eval('xxx') instead of vars()['xxx'] def func2(): print(all_subclasses(eval('Foo'))) func2() # Works # -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>] |
This could be improved by moving the
1 2 3 4 5 6 7 8 9 10 11 12 | # easier to use version def all_subclasses2(classname): direct_subclasses = eval(classname).__subclasses__() return direct_subclasses + [g for s in direct_subclasses for g in all_subclasses2(s.__name__)] # pass 'xxx' instead of eval('xxx') def func_ez(): print(all_subclasses2('Foo')) # simpler func_ez() # -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>] |
Lastly,it's possible,and perhaps even important in some cases,to avoid using
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def get_all_subclasses(cls): """ Generator of all a class's subclasses.""" try: for subclass in cls.__subclasses__(): yield subclass for subclass in get_all_subclasses(subclass): yield subclass except TypeError: return def all_subclasses3(classname): for cls in get_all_subclasses(object): # object is base of all new-style classes. if cls.__name__.split('.')[-1] == classname: break else: raise ValueError('class %s not found' % classname) direct_subclasses = cls.__subclasses__() return direct_subclasses + [g for s in direct_subclasses for g in all_subclasses3(s.__name__)] # no eval('xxx') def func3(): print(all_subclasses3('Foo')) func3() # Also works # -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>] |
a much shorter version for getting a list of all subclasses:
1 2 3 4 5 6 7 8 | from itertools import chain def subclasses(cls): return list( chain.from_iterable( [list(chain.from_iterable([[x], subclasses(x)])) for x in cls.__subclasses__()] ) ) |
How can I find all subclasses of a class given its name?
我们当然可以在访问对象本身的情况下轻松地做到这一点,是的。
简单地给出它的名字是一个糟糕的主意,因为可以有多个同名的类,甚至在同一个模块中定义。
我为另一个答案创建了一个实现,因为它回答了这个问题,而且比这里的其他解决方案更优雅,所以这里是:
1 2 3 4 5 6 7 8 9 | 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 |
用途:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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'>] |
这里有一个没有递归的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def get_subclasses_gen(cls): def _subclasses(classes, seen): while True: subclasses = sum((x.__subclasses__() for x in classes), []) yield from classes yield from seen found = [] if not subclasses: return classes = subclasses seen = found return _subclasses([cls], []) |
这与其他实现的不同之处在于,它返回原始类。这是因为它简化了代码,并且:
1 2 3 4 | class Ham(object): pass assert(issubclass(Ham, Ham)) # True |
如果get_子类_gen看起来有点奇怪,那是因为它是通过将尾部递归实现转换为循环生成器创建的:
1 2 3 4 5 6 7 8 9 10 11 | def get_subclasses(cls): def _subclasses(classes, seen): subclasses = sum(*(frozenset(x.__subclasses__()) for x in classes)) found = classes + seen if not subclasses: return found return _subclasses(subclasses, found) return _subclasses([cls], []) |
这不是一个很好的答案,比如使用特殊建筑工程师1〔9〕类方法,这类方法@unutbu mentions,所以我将它作为一种练习。函数定义返回一个字典,该字典映射所有子类的名称,并将它们归还给子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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) |
输出
1 2 | {'SubclassB': <class '__main__.SubclassB'>, 'SubclassA': <class '__main__.SubclassA'>} |
我无法想象它的真实世界用例,但一种健壮的方法(即使是在Python2旧样式类上)是扫描全局名称空间:
1 2 3 4 5 6 7 8 9 10 | def has_children(cls): g = globals().copy() # use a copy to make sure it will not change during iteration g.update(locals()) # add local symbols for k, v in g.items(): # iterate over all globals object try: if (v is not cls) and issubclass(v, cls): # found a strict sub class? return True except TypeError: # issubclass raises a TypeError if arg is not a class... pass return False |
它适用于python 2新样式类和python 3类以及python 2经典类。