我需要一种工作方法来获取从Python基类继承的所有类。
当前回答
新风格的类(即从object继承的子类,这是Python 3中的默认值)有__subclasses__方法,该方法返回子类:
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
下面是子类的名称:
print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']
下面是子类本身:
print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]
确认子类确实将Foo列为基类:
for cls in Foo.__subclasses__():
print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>
注意,如果你想要子类,你必须递归:
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'>}
注意,如果一个子类的类定义还没有被执行——例如,如果子类的模块还没有被导入——那么这个子类还不存在,__subclasses__将找不到它。
你提到了“以其名字命名”。由于Python类是一级对象,所以不需要使用带有类名的字符串来代替类或类似的东西。您可以直接使用该类,而且您可能应该这样做。
如果你确实有一个表示类名的字符串,并且你想要找到该类的子类,那么有两个步骤:找到给定其名称的类,然后像上面那样找到带有__subclasses__的子类。
如何从名称中找到类取决于您希望在哪里找到它。如果您希望在与试图定位类的代码相同的模块中找到它,那么
cls = globals()[name]
会起作用,或者在不太可能的情况下,你期望在当地人身上找到它,
cls = locals()[name]
如果这个类可以在任何模块中,那么你的名称字符串应该包含完全限定的名称——比如'pkg.module '。Foo'而不是Foo'。使用importlib加载类的模块,然后检索相应的属性:
import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
无论你如何找到这个类,cls.__subclasses__()将返回它的子类列表。
其他回答
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__的行为,并从这个列表中省略/添加子类。
这个答案不如使用@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'>}
虽然我非常倾向于__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())
这是因为字典会记住键的插入顺序。列表方法也会起作用。
新风格的类(即从object继承的子类,这是Python 3中的默认值)有__subclasses__方法,该方法返回子类:
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
下面是子类的名称:
print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']
下面是子类本身:
print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]
确认子类确实将Foo列为基类:
for cls in Foo.__subclasses__():
print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>
注意,如果你想要子类,你必须递归:
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'>}
注意,如果一个子类的类定义还没有被执行——例如,如果子类的模块还没有被导入——那么这个子类还不存在,__subclasses__将找不到它。
你提到了“以其名字命名”。由于Python类是一级对象,所以不需要使用带有类名的字符串来代替类或类似的东西。您可以直接使用该类,而且您可能应该这样做。
如果你确实有一个表示类名的字符串,并且你想要找到该类的子类,那么有两个步骤:找到给定其名称的类,然后像上面那样找到带有__subclasses__的子类。
如何从名称中找到类取决于您希望在哪里找到它。如果您希望在与试图定位类的代码相同的模块中找到它,那么
cls = globals()[name]
会起作用,或者在不太可能的情况下,你期望在当地人身上找到它,
cls = locals()[name]
如果这个类可以在任何模块中,那么你的名称字符串应该包含完全限定的名称——比如'pkg.module '。Foo'而不是Foo'。使用importlib加载类的模块,然后检索相应的属性:
import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
无论你如何找到这个类,cls.__subclasses__()将返回它的子类列表。
获取所有子类列表的一个更短的版本:
from itertools import chain
def subclasses(cls):
return list(
chain.from_iterable(
[list(chain.from_iterable([[x], subclasses(x)])) for x in cls.__subclasses__()]
)
)
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录