<p>这两个方法都可以用于自定义<a href="https://docs.python.org/library/functions.html#issubclass" rel="noreferrer">^{<cd1>}</a>内置函数的结果。</p>
<h2><a href="https://docs.python.org/reference/datamodel.html#class.__subclasscheck__" rel="noreferrer">^{<cd2>}</a></h2>
<blockquote>
<p><code>class.__subclasscheck__(self, subclass)</code></p>
<p>Return true if subclass should be considered a (direct or indirect) subclass of class. If defined, called to implement <code>issubclass(subclass, class)</code>.</p>
<p>Note that these methods are looked up on the type (metaclass) of a class. They cannot be defined as class methods in the actual class. This is consistent with the lookup of special methods that are called on instances, only in this case the instance is itself a class.</p>
</blockquote>
<p>这个方法是负责定制<code>issubclass</code>检查的特殊方法。就像“Note”声明它必须在元类上实现一样!</p>
<pre><code>class YouWontFindSubclasses(type):
def __subclasscheck__(cls, subclass):
print(cls, subclass)
return False
class MyCls(metaclass=YouWontFindSubclasses):
pass
class MySubCls(MyCls):
pass
</code></pre>
<p>即使您有真正的子类,此实现也将返回False:</p>
<pre><code>>>> issubclass(MySubCls, MyCls)
<class '__main__.MyCls'> <class '__main__.MySubCls'>
False
</code></pre>
<p>实际上,<code>__subclasscheck__</code>实现有更有趣的用途。例如:</p>
<pre><code>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__']
</code></pre>
<p>使用此实现,任何定义<code>__len__</code>和<code>__iter__</code>的类都将在<code>issubclass</code>检查中返回<code>True</code></p>
<pre><code>>>> issubclass(int, MyCls) # ints have no __len__ or __iter__
False
>>> issubclass(list, MyCls) # but lists and dicts have
True
>>> issubclass(dict, MyCls)
True
</code></pre>
<p>在这些示例中,我没有调用超类<code>__subclasscheck__</code>,因此禁用了正常的<code>issubclass</code>行为(它由<code>type.__subclasscheck__</code>实现)。但重要的是要知道,您也可以选择仅扩展正常行为而不是完全覆盖它:</p>
<pre><code>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.
</code></pre>
<h2><a href="https://docs.python.org/library/abc.html#abc.ABCMeta.__subclasshook__" rel="noreferrer">^{<cd12>}</a></h2>
<blockquote>
<p><code>__subclasshook__(subclass)</code></p>
<p>(Must be defined as a class method.)</p>
<p>Check whether subclass is considered a subclass of this ABC. This means that you can customize the behavior of <code>issubclass</code> further without the need to call <code>register()</code> on every class you want to consider a subclass of the ABC. (This class method is called from the <code>__subclasscheck__()</code> method of the ABC.)</p>
<p>This method should return <code>True</code>, <code>False</code> or <code>NotImplemented</code>. If it returns <code>True</code>, the subclass is considered a subclass of this ABC. If it returns <code>False</code>, the subclass is not considered a subclass of this ABC, even if it would normally be one. If it returns <code>NotImplemented</code>, the subclass check is continued with the usual mechanism.</p>
</blockquote>
<p>这里重要的一点是,它在类上定义为<code>classmethod</code>,并由<code>abc.ABC.__subclasscheck__</code>调用。因此,只有在处理具有<code>ABCMeta</code>元类的类时才能使用它:</p>
<pre><code>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
</code></pre>
<p>这只会进入第一个的<code>__subclasshook__</code>:</p>
<pre><code>>>> issubclass(int, MyClsABC)
in subclasshook
True
>>> issubclass(int, MyClsNoABC)
False
</code></pre>
<p>注意,后续的<code>issubclass</code>调用不再进入<code>__subclasshook__</code>,因为<code>ABCMeta</code>缓存结果:</p>
<pre><code>>>> issubclass(int, MyClsABC)
True
</code></pre>
<p>注意,通常检查第一个参数是否是类本身。这是为了避免子类“继承”子类而不是使用常规的子类确定。</p>
<p>例如(来自CPython<code>collections.abc</code>模块):</p>
<pre><code>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
</code></pre>
<p>因此,如果您检查某个东西是否是<code>Hashable</code>的子类,它将使用由<code>if cls is Hashable</code>保护的自定义<code>__subclasshook__</code>实现。但是,如果您有一个实现<code>Hashable</code>接口的实际类,您不希望它继承<code>__subclasshook__</code>机制,而希望它继承普通的子类机制。</p>
<p>例如:</p>
<pre><code>class MyHashable(Hashable):
def __hash__(self):
return 10
>>> issubclass(int, MyHashable)
False
</code></pre>
<p>即使<code>int</code>实现了<code>__hash__</code>,并且<code>__subclasshook__</code>检查了<code>__hash__</code>实现,在本例中的结果是<code>False</code>。它仍然进入<code>Hashable</code>的<code>__subclasshook__</code>,但它立即返回<code>NotImplemented</code>,这向<code>ABCMeta</code>发出信号,表明它应该使用正常实现继续。如果没有<code>if cls is Hashable</code>,那么<code>issubclass(int, MyHashable)</code>将返回<code>True</code>!</p>
<h2>什么时候应该使用<code>__subclasscheck__</code>,什么时候应该使用<code>__subclasshook__</code>?</h2>
<p>这要看情况。<code>__subclasshook__</code>可以在类而不是元类上实现,但需要使用<code>ABCMeta</code>(或<code>ABCMeta</code>的子类)作为元类,因为<code>__subclasshook__</code>方法实际上只是Pythons<code>abc</code>模块引入的一个约定。</p>
<p>您始终可以使用<code>__subclasscheck__</code>,但它必须在元类上实现。</p>
<p>在实践中,如果您实现接口(因为这些接口通常使用<code>abc</code>),并且希望自定义子类机制,则使用<code>__subclasshook__</code>。如果你想发明自己的约定(比如<code>abc</code>),你可以使用<code>__subclasscheck__</code>。所以在99.99%的正常(不好玩)情况下,你只需要<code>__subclasshook__</code>。</p>