两种方法均可用于自定义issubclass()
内置函数的结果。
class.__subclasscheck__(self, subclass)
如果子类应被视为类的(直接或间接)子类,则返回true。如果定义,则调用实现issubclass(subclass, class)
。
请注意,这些方法是在类的类型(元类)上查找的。它们不能定义为实际类中的类方法。这与在实例上调用的特殊方法的查找一致,仅在这种情况下,实例本身是一个类。
此方法是负责issubclass
检查定制的特殊方法。就像“注释”中所述,它必须在元类上实现!
class YouWontFindSubclasses(type):
def __subclasscheck__(cls, subclass):
print(cls, subclass)
return False
class MyCls(Metaclass=YouWontFindSubclasses):
pass
class MySubCls(MyCls):
pass
即使您拥有真正的子类,此实现也会返回False:
>>> issubclass(MySubCls, MyCls)
<class '__main__.MyCls'> <class '__main__.MySubCls'>
False
实际上,__subclasscheck__
实现还有更多有趣的用途。例如:
class SpecialSubs(type):
def __subclasscheck__(cls, subclass):
required_attrs = getattr(cls, '_required_attrs', [])
for attr in required_attrs:
if any(attr in sub.__dict__ for sub in subclass.__mro__):
continue
return False
return True
class MyCls(Metaclass=SpecialSubs):
_required_attrs = ['__len__', '__iter__']
有了这个实现的任何类,定义__len__
和__iter__
返回True
的issubclass
检查:
>>> issubclass(int, MyCls) # ints have no __len__ or __iter__
False
>>> issubclass(list, MyCls) # but lists and dicts have
True
>>> issubclass(dict, MyCls)
True
在这些示例中,我没有调用超类__subclasscheck__
,因此禁用了正常issubclass
行为(由实现type.__subclasscheck__
)。但是重要的是要知道,您还可以选择只是扩展 正常行为,而不是完全覆盖它:
class Meta(type):
def __subclasscheck__(cls, subclass):
"""Just modify the behavior for classes that aren't genuine subclasses."""
if super().__subclasscheck__(subclass):
return True
else:
# Not a normal subclass, implement some customization here.
__subclasshook__(subclass)
(必须定义为类方法。)
检查子类是否被视为此ABC的子类。这意味着您可以自定义issubclass
进一步的行为,而无需调用register()
要考虑为ABC的子类的每个类。(此类方法是从__subclasscheck__()
ABC的方法中调用的。)
这个方法应该返回True
,False
或NotImplemented
。如果返回True
,则将该子类视为此ABC的子类。如果返回False
,则即使该子类通常是一个子类,也不会将该子类视为该ABC的子类。如果返回NotImplemented
,则使用常规机制继续子类检查。
这里重要的一点是,它是classmethod
在类上定义的,并由调用abc.ABC.__subclasscheck__
。因此,只有在处理具有ABCMeta
元类的类时才可以使用它:
import abc
class MyClsABC(abc.ABC):
@classmethod
def __subclasshook__(cls, subclass):
print('in subclasshook')
return True
class MyClsNoABC(object):
@classmethod
def __subclasshook__(cls, subclass):
print('in subclasshook')
return True
这只会进入__subclasshook__
第一个:
>>> issubclass(int, MyClsABC)
in subclasshook
True
>>> issubclass(int, MyClsNoABC)
False
请注意,后续的issubclass
调用不会再进入__subclasshook__
,因为会ABCMeta
缓存结果:
>>> issubclass(int, MyClsABC)
True
请注意,通常检查第一个参数是否是类本身。这是为了避免子类“继承”,__subclasshook__
而不是使用常规子类确定。
例如(来自cpythoncollections.abc
模块):
from abc import ABCMeta, abstractmethod
def _check_methods(C, *methods):
mro = C.__mro__
for method in methods:
for B in mro:
if method in B.__dict__:
if B.__dict__[method] is None:
return NotImplemented
break
else:
return NotImplemented
return True
class Hashable(Metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __hash__(self):
return 0
@classmethod
def __subclasshook__(cls, C):
if cls is Hashable:
return _check_methods(C, "__hash__")
return NotImplemented
因此,如果您检查某物是否是其子类,Hashable
则将使用由__subclasshook__
守护的自定义实现if cls is Hashable
。但是,如果您有一个实际的类来实现该Hashable
接口,则您不希望它继承该__subclasshook__
机制,而是希望使用普通的子类机制。
例如:
class MyHashable(Hashable):
def __hash__(self):
return 10
>>> issubclass(int, MyHashable)
False
即使int
实现__hash__
并__subclasshook__
检查__hash__
实现,这种情况下的结果也是False
。它仍然输入__subclasshook__
of,Hashable
但是它立即返回使用正常实现应继续进行处理的NotImplemented
信号ABCMeta
。如果没有,if cls is Hashable
则issubclass(int, MyHashable)
返回True
!
真的要看__subclasshook__
可以在类上而不是在元类上实现,但要求您使用ABCMeta
(或的子类ABCMeta
)作为元类,因为该__subclasshook__
方法实际上只是Pythonsabc
模块引入的约定。
您可以随时使用,__subclasscheck__
但必须在元类上实现。
在实践中,__subclasshook__
如果要实现接口(因为这些接口通常使用abc
)并且要自定义子类机制,则使用。而你使用__subclasscheck__
,如果你想创造自己的约定(如abc
没有)。因此,在99.99%的正常(不好玩)情况下,您只需要__subclasshook__
。