仅对具体 class 为 True 的静态属性,对其在 Python 中的子项为 False
Static attribute that is True only for a concrete class, and False for its children in Python
问题
假设我有一个 class Root
并且想要访问(例如初始化)它的所有子class。但是可能有一些需要以编程方式忽略的子classes。
例子
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
class 包含在 subs
列表中,而不是 Parent
class.
简单的解决方案
当然,我可以为每个 subclass 定义 ignore_me
属性,但关键是我想将 subclasses 从那个细节中分离出来,这样他们甚至不会意识到它。
问题
如何仅在 Parent
class 中定义 ignore_me
属性来实现目标?
您可以使用 if sub.__dict__.get('ignore_me', False)
来检查 ignore_me
属性 是否直接出现在给定的子 class 中(不是继承的)。
但是,您仍然需要以不同的方式执行此操作,因为 __subclasses__
只有 returns 立即 subclass 是(如 documented).如果要递归遍历所有后代 class,则需要编写一些代码,在层次结构中的每个 class 上递归调用 __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'>]
这是另一个尝试:
class SubclassIgnoreMark(object):
def __init__(self, cls_name):
super(SubclassIgnoreMark, self).__init__()
self._cls_name = cls_name
def __get__(self, instance, owner):
return owner.__name__ == self._cls_name
def IgnoreSubclass(klass):
setattr(klass, 'ignore_me', SubclassIgnoreMark(klass.__name__))
return klass
有了这些,我就可以用 IgnoreSubclass
装饰器装饰 Parent
class...
@IgnoreSubclass
class Parent(Root):
pass
class Child(Parent):
pass
...然后:
>>> Parent.ignore_me
True
>>> Child.ignore_me
False
这其实很简单:
class Parent(Root):
@classmethod
def ignore(cls):
return cls == Parent
class Child(Parent):
pass
>>> Parent().ignore()
True
>>> Child().ignore()
False
如果您想要 class 并将其转移到子 class,则只需将 return cls == Parent
替换为 return True
。
你可以尝试使用虚子类,重写虚子类的子类钩子。
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)]
问题
假设我有一个 class Root
并且想要访问(例如初始化)它的所有子class。但是可能有一些需要以编程方式忽略的子classes。
例子
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
class 包含在 subs
列表中,而不是 Parent
class.
简单的解决方案
当然,我可以为每个 subclass 定义 ignore_me
属性,但关键是我想将 subclasses 从那个细节中分离出来,这样他们甚至不会意识到它。
问题
如何仅在 Parent
class 中定义 ignore_me
属性来实现目标?
您可以使用 if sub.__dict__.get('ignore_me', False)
来检查 ignore_me
属性 是否直接出现在给定的子 class 中(不是继承的)。
但是,您仍然需要以不同的方式执行此操作,因为 __subclasses__
只有 returns 立即 subclass 是(如 documented).如果要递归遍历所有后代 class,则需要编写一些代码,在层次结构中的每个 class 上递归调用 __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'>]
这是另一个尝试:
class SubclassIgnoreMark(object):
def __init__(self, cls_name):
super(SubclassIgnoreMark, self).__init__()
self._cls_name = cls_name
def __get__(self, instance, owner):
return owner.__name__ == self._cls_name
def IgnoreSubclass(klass):
setattr(klass, 'ignore_me', SubclassIgnoreMark(klass.__name__))
return klass
有了这些,我就可以用 IgnoreSubclass
装饰器装饰 Parent
class...
@IgnoreSubclass
class Parent(Root):
pass
class Child(Parent):
pass
...然后:
>>> Parent.ignore_me
True
>>> Child.ignore_me
False
这其实很简单:
class Parent(Root):
@classmethod
def ignore(cls):
return cls == Parent
class Child(Parent):
pass
>>> Parent().ignore()
True
>>> Child().ignore()
False
如果您想要 class 并将其转移到子 class,则只需将 return cls == Parent
替换为 return True
。
你可以尝试使用虚子类,重写虚子类的子类钩子。
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)]