方法__subclasscheck__
和__subclasshook__
用于确定某个类是否被视为另一个类的子类。但是,即使在高级Python书籍中,它们的文档也非常有限。它们的用途是什么?它们之间有什么区别(更高的优先级,他们所指的关系方面等等)?
最佳答案
这两种方法均可用于自定义 issubclass()
内置函数的结果。
__subclasscheck__
此方法是负责定制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
在这些示例中,我没有调用父类(super class)__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__
这里重要的一点是,它在类上定义为
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__
,而不是使用常规的子类确定。例如(来自CPython的
collections.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
接口(interface)的实际类,则不希望它继承__subclasshook__
机制,但希望使用普通的子类机制。例如:
class MyHashable(Hashable):
def __hash__(self):
return 10
>>> issubclass(int, MyHashable)
False
即使int
实现了__hash__
,并且__subclasshook__
检查了__hash__
的实现,在这种情况下,结果还是False
。它仍然输入__subclasshook__
的Hashable
,但立即返回NotImplemented
,向ABCMeta
发出信号,表明应使用常规实现继续进行操作。如果没有if cls is Hashable
,那么issubclass(int, MyHashable)
将返回True
!什么时候应该使用
__subclasscheck__
和何时__subclasshook__
?真的要看
__subclasshook__
可以在类上实现,而不是在元类上实现,但要求您将ABCMeta
(或ABCMeta
的子类)用作元类,因为__subclasshook__
方法实际上只是Python的abc
模块引入的约定。您可以始终使用
__subclasscheck__
,但必须在元类上实现。在实践中,如果实现接口(interface)(因为这些接口(interface)通常使用
__subclasshook__
)并且要自定义子类机制,则可以使用abc
。如果您想发明自己的约定,则可以使用__subclasscheck__
(就像abc
一样)。因此,在99.99%的正常(不好玩)的情况下,您只需要__subclasshook__
即可。关于python - python subclasscheck和subclasshook,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40764347/