问题的提出
给定一个类的名称(字符串)。如何找到该类的所有子类?

例子。下面是一个有子类层次结构的例子。
class Parent:
pass
class Daughter(Parent):
pass
class Son(Parent):
pass
class Grandson(Son):
pass
预期的输出。接下来,让我们通过两个例子快速确定你想要完成的任务。
Given: Son
Result: Grandson
Given: Parent
Result: Daughter, Son
对于最基本的情况,我们不假设有递归的解决要求,所以我们不考虑Grandson 也是Parent 的子类。
如果我们有了类对象,而不仅仅是类名,那么就可以解决了
假设(现在)我们有类对象。在本文的下一节中,我们将看到一个略微复杂的情况,没有这个假设。
在这种情况下,我们可以简单地使用my_class.__subclasses__() 魔法方法获得该类的所有子类。
class Parent:
pass
class Daughter(Parent):
pass
class Son(Parent):
pass
class Grandson(Son):
pass
print(Parent.__subclasses__())
# [<class '__main__.Daughter'>, <class '__main__.Son'>]
如果你只需要一个所有子类的名字的列表,而不需要整个杂乱无章的输出,你可以使用一个列表理解语句,就像这样。
names = [cls.__name__ for cls in Parent.__subclasses__()]
print(names)
# ['Daughter', 'Son']
对所有子类的递归
你可以使用递归的方法获得所有直接和间接的子类(如果使用分层继承)。
- 创建一个函数
subclass_recursive(),该函数需要一个参数:基类,应该从该基类中找到子类。 - 使用
__subclasses__()魔法方法找到所有的直接子类。 - 通过递归调用该函数找到所有的间接子类。
- 将直接和间接子类的串联结果返回给调用者。
让我们深入了解一下代码吧!
class Parent:
pass
class Daughter(Parent):
pass
class Son(Parent):
pass
class Grandson(Son):
pass
class GrandGrandSon(Son):
pass
def subclasses_recursive(cls):
direct = cls.__subclasses__()
indirect = []
for subclass in direct:
indirect.extend(subclasses_recursive(subclass))
return direct + indirect
print(subclasses_recursive(Parent))
输出是所有子类的列表。
[<class '__main__.Daughter'>, <class '__main__.Son'>,
<class '__main__.Grandson'>, <class '__main__.GrandGrandSon'>]
同样,如果你只需要有字符串形式的类名,可以使用列表理解法,在子类上使用 [__name__](https://blog.finxter.com/what-does-if-__name__-__main__-do-in-python/)的特殊属性进行列表理解。
names = [cls.__name__ for cls in subclasses_recursive(Parent)]
print(names)
# ['Daughter', 'Son', 'Grandson', 'GrandGrandSon']
如果我们只拥有类的字符串名称
你可以使用 globals()内置函数来找出给定名称的类对象。
# Get class given name of class:
base_name = 'Parent'
base_class = globals()[base_name]
接下来请随意观看我关于该函数的背景视频。
如果这不起作用,你可能想检查一下 [locals()](https://blog.finxter.com/python-locals/)函数。
# Get class given name of class:
base_name = 'Parent'
base_class = locals()[base_name]
最后,这里概述了另一种获得类的完整路径的方法。
import importlib
module_name, _, base_name = name.rpartition('.')
module = importlib.import_module(module_name)
base_class = getattr(module, base_name)
把它放在一起
class Parent:
pass
class Daughter(Parent):
pass
class Son(Parent):
pass
class Grandson(Son):
pass
class GrandGrandSon(Son):
pass
# Get class given name of class:
base_name = 'Parent'
base_class = globals()[base_name]
# Get list of direct subclasses:
print(base_class.__subclasses__())
# [<class '__main__.Daughter'>, <class '__main__.Son'>]
# Get list of direct subclass names:
names = [cls.__name__ for cls in base_class.__subclasses__()]
print(names)
# ['Daughter', 'Son']
# Get list of direct and indirect subclasses:
def subclasses_recursive(cls):
direct = cls.__subclasses__()
indirect = []
for subclass in direct:
indirect.extend(subclasses_recursive(subclass))
return direct + indirect
# Get list of direct and indirect subclasse names:
names = [cls.__name__ for cls in subclasses_recursive(base_class)]
print(names)
# ['Daughter', 'Son', 'Grandson', 'GrandGrandSon']