如何根据给定名称查找一个类的所有子类?
我需要一种获取从 Python 基类继承的所有类的工作方法。
I need a working approach of getting all classes that are inherited from a base class in Python.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
新式类(即从
object
派生的子类,这是 Python 3 中的默认设置)具有返回子类的__subclasses__
方法:以下是子类的名称:
这里是子类本身:
确认子类确实将
Foo
列为其基类:请注意,如果您想要子子类,则必须递归:
请注意,如果子类的类定义尚未定义尚未执行 - 例如,如果子类的模块尚未导入 - 那么该子类尚不存在,并且 __subclasses__ 不会找到它。
你提到“给它的名字”。由于 Python 类是一等对象,因此您不需要使用带有类名的字符串来代替类或类似的内容。您可以直接使用该类,而且您可能应该这样做。
如果您确实有一个表示类名称的字符串,并且想要查找该类的子类,则有两个步骤:根据名称查找该类,然后使用上面的
__subclasses__
查找子类。如何从名称找到类取决于您期望在哪里找到它。如果您希望在与尝试定位该类的代码相同的模块中找到它,那么
就可以完成这项工作,或者在不太可能的情况下,您希望在局部变量中找到它,
如果该类可能位于任何模块,那么您的名称字符串应包含完全限定的名称 - 类似
'pkg.module.Foo'
而不仅仅是'Foo'
。使用importlib
加载类的模块,然后检索相应的属性:无论您如何找到该类,
cls.__subclasses__()
都会返回其子类的列表。New-style classes (i.e. subclassed from
object
, which is the default in Python 3) have a__subclasses__
method which returns the subclasses:Here are the names of the subclasses:
Here are the subclasses themselves:
Confirmation that the subclasses do indeed list
Foo
as their base:Note if you want subsubclasses, you'll have to recurse:
Note that if the class definition of a subclass hasn't been executed yet - for example, if the subclass's module hasn't been imported yet - then that subclass doesn't exist yet, and
__subclasses__
won't find it.You mentioned "given its name". Since Python classes are first-class objects, you don't need to use a string with the class's name in place of the class or anything like that. You can just use the class directly, and you probably should.
If you do have a string representing the name of a class and you want to find that class's subclasses, then there are two steps: find the class given its name, and then find the subclasses with
__subclasses__
as above.How to find the class from the name depends on where you're expecting to find it. If you're expecting to find it in the same module as the code that's trying to locate the class, then
would do the job, or in the unlikely case that you're expecting to find it in locals,
If the class could be in any module, then your name string should contain the fully-qualified name - something like
'pkg.module.Foo'
instead of just'Foo'
. Useimportlib
to load the class's module, then retrieve the corresponding attribute:However you find the class,
cls.__subclasses__()
would then return a list of its subclasses.如果您只想要直接子类,那么
.__subclasses__()
工作正常。如果您想要所有子类、子类的子类等等,您将需要一个函数来为您执行此操作。这是一个简单、可读的函数,它递归地查找给定类的所有子类:
If you just want direct subclasses then
.__subclasses__()
works fine. If you want all subclasses, subclasses of subclasses, and so on, you'll need a function to do that for you.Here's a simple, readable function that recursively finds all subclasses of a given class:
一般形式的最简单的解决方案:
以及一个类方法,如果您有一个继承自的类:
The simplest solution in general form:
And a classmethod in case you have a single class where you inherit from:
Python 3.6 -
__init_subclass__
正如其他答案提到的,您可以检查
__subclasses__
属性来获取子类列表,因为 python 3.6 您可以修改此属性通过覆盖创建__init_subclass__
方法。这样,如果您知道自己在做什么,则可以覆盖
__subclasses__
的行为并从此列表中省略/添加子类。Python 3.6 -
__init_subclass__
As other answer mentioned you can check the
__subclasses__
attribute to get the list of subclasses, since python 3.6 you can modify this attribute creation by overriding the__init_subclass__
method.This way, if you know what you're doing, you can override the behavior of of
__subclasses__
and omit/add subclasses from this list.注意:我看到有人(不是 @unutbu)更改了引用的答案,使其不再使用
vars()['Foo']
- 因此我帖子的主要观点不再适用.FWIW,这就是我关于 @unutbu 的答案 仅使用本地定义的类的意思 - 并且使用 < code>eval() 而不是
vars()
将使其适用于任何可访问的类,而不仅仅是当前范围中定义的类。对于那些不喜欢使用 eval() 的人,还提供了一种避免使用它的方法。
首先,这是一个具体示例,演示了使用
vars()
的潜在问题:可以通过将
eval('ClassName')
向下移动到定义的函数中来改进,这使得使用它更容易,而不损失通过使用eval()
获得的额外通用性,它与vars()
不同,它不是上下文相关的:最后,它是可能的,甚至可能很重要在某些情况下,出于安全原因避免使用
eval()
,因此这里有一个没有它的版本:Note: I see that someone (not @unutbu) changed the referenced answer so that it no longer uses
vars()['Foo']
— so the primary point of my post no longer applies.FWIW, here's what I meant about @unutbu's answer only working with locally defined classes — and that using
eval()
instead ofvars()
would make it work with any accessible class, not only those defined in the current scope.For those who dislike using
eval()
, a way is also shown to avoid it.First here's a concrete example demonstrating the potential problem with using
vars()
:This could be improved by moving the
eval('ClassName')
down into the function defined, which makes using it easier without loss of the additional generality gained by usingeval()
which unlikevars()
is not context-sensitive:Lastly, it's possible, and perhaps even important in some cases, to avoid using
eval()
for security reasons, so here's a version without it:下面是一个简单但高效的代码版本:
它的时间复杂度为
O(n)
,其中n
是如果没有多重继承的话所有子类的数量。它比使用生成器递归创建列表或生成类的函数更有效,当类层次结构是平衡树时,其复杂度可能为 (1)
O(nlogn)
或 (2)O (n^2)
当类层次结构是偏向树时。Here is a simple but efficient version of code:
Its time complexity is
O(n)
wheren
is the number of all subclasses if there's no multiple inheritance.It's more efficient than the functions that recursively create lists or yield classes with generators, whose complexity could be (1)
O(nlogn)
when the class hierarchy is a balanced tree or (2)O(n^2)
when the class hierarchy is a biased tree.用于获取所有子类列表的更短版本:
A much shorter version for getting a list of all subclasses:
这是一个没有递归的版本:
这与其他实现的不同之处在于它返回原始类。
这是因为它使代码更简单,并且:
如果 get_subclasses_gen 看起来有点奇怪,那是因为它是通过将尾递归实现转换为循环生成器创建的:
Here's a version without recursion:
This differs from other implementations in that it returns the original class.
This is because it makes the code simpler and:
If get_subclasses_gen looks a bit weird that's because it was created by converting a tail-recursive implementation into a looping generator:
如果可以访问对象本身,我们当然可以轻松地做到这一点,是的。
简单地给出它的名称是一个糟糕的主意,因为可以有多个同名的类,甚至是在同一个模块中定义的。
我为另一个答案创建了一个实现,因为它回答了这个问题并且比这里的其他解决方案更优雅,这是:
用法:
We can certainly easily do this given access to the object itself, yes.
Simply given its name is a poor idea, as there can be multiple classes of the same name, even defined in the same module.
I created an implementation for another answer, and since it answers this question and it's a little more elegant than the other solutions here, here it is:
Usage:
这不像使用 @unutbu 提到的特殊内置 __subclasses__() 类方法那么好,所以我仅将其作为练习。定义的 subclasses() 函数返回一个字典,它将所有子类名称映射到子类本身。
输出:
This isn't as good an answer as using the special built-in
__subclasses__()
class method which @unutbu mentions, so I present it merely as an exercise. Thesubclasses()
function defined returns a dictionary which maps all the subclass names to the subclasses themselves.Output:
使用
__subclasses()__
方法的局限性是:A 类
->B(A) 类
->C(B) 类
。A.__subclasses__()
只会给你类B
而不是类C
。这是因为类C
继承自类B
而不是A
!因此,为了解决这两个限制,除了上面的精彩答案之外,我还有一个稍微不同的方法。
此函数将获取包中的所有类:
如何使用上面的类的示例:
my_classes
现在看起来像这样[MyModel1、MyModel2、MyModel3 等...]
得到这个后,只需像这样过滤列表:
The limitations of using the
__subclasses()__
approach are:class A
->class B(A)
->class C(B)
.A.__subclasses__()
will only give you classB
not classC
. This is because classC
inherits from classB
notA
!Therefore, to address these 2 limitations I have a slightly different approach in addition to the awesome answers above.
This function will get all the classes in the package:
Example of how to use the class above:
my_classes
will now look like this[MyModel1, MyModel2, MyModel3, etc...]
After getting this, simply filter through the list like this:
虽然我非常偏爱
__init_subclass__
方法,但如果您有一个非常密集的层次结构并且到处都有多重继承,这将保留定义顺序,并避免增长的组合顺序:这是有效的,因为字典会记住插入顺序他们的钥匙。
While I'm very partial to the
__init_subclass__
approach, this will preserve definition order, and avoid combinatorial order of growth if you have a very dense hierarchy with multiple inheritance everywhere:This works because dictionaries remember the insertion order of their keys.