问题
假设我有一个类Root
,并希望访问(例如初始化)其所有子类。但是可能有一些子类需要以编程方式忽略。
示例
class Root(object):
pass
class Parent(Root):
ignore_me = True
class Child(Parent):
pass
def get_subclasses(klass):
result = klass.__subclasses__()
for subclass in result:
result += get_subclasses(subclass)
return result
subs = [sub for sub in get_subclasses(Root) if not sub.ignore_me]
所以我想要的是Child
类包含在subs
列表中,而不是Parent
类。
琐碎的解决方案
当然,我可以为每个子类定义ignore_me
属性,但关键是我要将子类与该细节隔离开来,以便他们甚至不会意识到它。
问题
如何仅通过ignore_me
类定义Parent
属性来实现目标?
答案 0 :(得分:2)
您可以使用if sub.__dict__.get('ignore_me', False)
检查ignore_me
属性是否直接出现在给定的子类中(未继承)。
但是,您仍然需要以不同方式执行此操作,因为__subclasses__
仅返回立即子类(如documented)。如果要通过所有后代类进行递归,则需要编写一些代码,这些代码在层次结构中的每个类上递归调用__subclasses__
。像这样:
def getSubs(cls):
for sub in cls.__subclasses__():
if not sub.__dict__.get('ignore_me', False):
yield sub
for desc in getSubs(sub):
yield desc
然后:
>>> list(getSubs(Root))
[<class '__main__.Child'>]
答案 1 :(得分:1)
这实际上非常简单:
class Parent(Root):
@classmethod
def ignore(cls):
return cls == Parent
class Child(Parent):
pass
>>> Parent().ignore()
True
>>> Child().ignore()
False
如果你想要一个将它转移到子类的类,那么只需用return cls == Parent
替换return True
。
答案 2 :(得分:0)
您可以尝试使用虚拟子类,并覆盖虚拟子类的子类钩子。
from abc import ABCMeta
class Ignore(object):
__metaclass__ = ABCMeta
@classmethod
def __subclasshook__(ignore_cls, cls):
"""only direct, and registered lasses are considered a subclass"""
return cls in ignore_cls._abc_registry
class Root(object):
pass
class Parent(Root):
pass
Ignore.register(Parent) # slightly more elegant in python 3 as register can be used as a decorator
class Child(Parent):
pass
print(Parent, issubclass(Parent, Ignore))
print(Child, issubclass(Child, Ignore))
def get_subclasses(klass):
result = klass.__subclasses__()
for subclass in result:
result += get_subclasses(subclass)
return result
result = get_subclasses(Root)
print [cls for cls in result if not issubclass(cls, Ignore)]