静态属性仅对于具体类为True,在Python中为其子项为False

时间:2015-05-10 22:42:03

标签: python oop design-patterns python-2.x

问题

假设我有一个类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属性来实现目标?

3 个答案:

答案 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)]