问题描述
方法 __subclasscheck__
和 __subclasshook__
用于确定一个类是否被视为另一个类的子类.然而,他们的文档非常有限,即使在高级 Python 书籍中也是如此.它们的用途是什么,它们的区别是什么(更高的优先级,它们所指的关系方面等......)?
这两种方法都可以用来自定义 issubclass()
内置函数.
__subclasscheck__
class.__subclasscheck__(self, subclass)
如果子类应被视为类的(直接或间接)子类,则返回 true.如果已定义,则调用以实现issubclass(subclass, class)
.
请注意,这些方法是在类的类型(元类)上查找的.它们不能在实际类中定义为类方法.这与在实例上调用的特殊方法的查找是一致的,只是在这种情况下,实例本身就是一个类.
该方法是负责自定义issubclass
检查的特殊方法.像注"一样声明它必须在元类上实现!
class YouWontFindSubclasses(type):def __subclasscheck__(cls, 子类):打印(CLS,子类)返回错误类 MyCls(metaclass=YouWontFindSubclasses):经过类 MySubCls(MyCls):经过
即使您有真正的子类,此实现也会返回 False:
>>>issubclass(MySubCls, MyCls)<class '__main__.MyCls'><class '__main__.MySubCls'>错误的__subclasscheck__
实现实际上还有更有趣的用途.例如:
class SpecialSubs(type):def __subclasscheck__(cls, 子类):required_attrs = getattr(cls, '_required_attrs', [])对于 required_attrs 中的 attr:如果有的话(sub.__dict__ 中的 attr 用于 subclass.__mro__ 中的 sub):继续返回错误返回真类 MyCls(metaclass=SpecialSubs):_required_attrs = ['__len__', '__iter__']
使用此实现,任何定义 __len__
和 __iter__
的类将在 issubclass
检查中返回 True
:
在这些示例中,我没有调用超类 __subclasscheck__
从而禁用了正常的 issubclass
行为(由 type.__subclasscheck__
实现)).但重要的是要知道您也可以选择只扩展正常行为而不是完全覆盖它:
类元(类型):def __subclasscheck__(cls, 子类):"""只需修改不是真正子类的类的行为."""if super().__subclasscheck__(subclass):返回真别的:# 不是普通的子类,这里实现一些自定义.
__subclasshook__
__subclasshook__(subclass)
(必须定义为类方法.)
检查子类是否被认为是这个 ABC 的子类.这意味着您可以进一步自定义 issubclass
的行为,而无需在要考虑为 ABC 的子类的每个类上调用 register()
.(这个类方法是从ABC的__subclasscheck__()
方法调用的.)
此方法应返回 True
、False
或 NotImplemented
.如果它返回True
,则该子类被认为是此ABC 的子类.如果它返回 False
,则子类不被视为此 ABC 的子类,即使它通常是一个子类.如果它返回NotImplemented
,子类检查将继续使用通常的机制.
这里的重要一点是它在类上被定义为 classmethod
并且它被 abc.ABC.__subclasscheck__
调用.因此,只有在处理具有 ABCMeta
元类的类时才能使用它:
导入 abc类 MyClsABC(abc.ABC):@类方法def __subclasshook__(cls, subclass):打印('在子类钩子中')返回真类 MyClsNoABC(对象):@类方法def __subclasshook__(cls, subclass):打印('在子类钩子中')返回真
这只会进入第一个的__subclasshook__
:
请注意,后续的 issubclass
调用不再进入 __subclasshook__
,因为 ABCMeta
缓存了结果:
请注意,您通常会检查第一个参数是否是类本身.这是为了避免子类继承"__subclasshook__
而不是使用正常的子类确定.
例如(来自 CPython collections.abc
模块):
from abc import ABCMeta, abstractmethoddef _check_methods(C, *methods):mro = C.__mro__对于方法中的方法:对于 mro 中的 B:B.__dict__ 中的 if 方法:如果 B.__dict__[method] 是 None:返回 NotImplemented休息别的:返回 NotImplemented返回真哈希类(元类 = ABCMeta):__插槽__ = ()@抽象方法def __hash__(self):返回 0@类方法def __subclasshook__(cls, C):如果 cls 是可哈希的:返回 _check_methods(C, "__hash__")返回 NotImplemented
因此,如果您检查某事物是否是 Hashable
的子类,它将使用由 if cls is Hashable
保护的自定义 __subclasshook__
实现>.但是,如果您有一个实际的类实现了 Hashable
接口,您不希望它继承 __subclasshook__
机制,而是想要普通的子类机制.
例如:
class MyHashable(Hashable):def __hash__(self):返回 10>>>issubclass(int, MyHashable)错误的
即使 int
实现了 __hash__
并且 __subclasshook__
检查了 __hash__
实现,在这种情况下的结果是错误
.它仍然进入 Hashable
的 __subclasshook__
,但它立即返回 NotImplemented
,它向 ABCMeta
发出信号,它应该使用正常执行.如果它没有 if cls is Hashable
那么 issubclass(int, MyHashable)
将返回 True
!
什么时候应该使用__subclasscheck__
,什么时候使用__subclasshook__
?
这真的取决于.__subclasshook__
可以在类上实现而不是在元类上实现,但要求您使用 ABCMeta
(或 ABCMeta
的子类)作为元类,因为__subclasshook__
方法其实只是 Pythons abc
模块引入的约定.
你总是可以使用 __subclasscheck__
但它必须在元类上实现.
在实践中,如果您实现接口(因为这些通常使用 abc
)并希望自定义子类机制,则可以使用 __subclasshook__
.如果您想发明自己的约定(如 abc
所做的那样),您可以使用 __subclasscheck__
.所以在 99.99% 的正常(不好玩)的情况下,你只需要 __subclasshook__
.
The methods __subclasscheck__
and __subclasshook__
are used to determine if a class is regarded as subclass of another. However, their documentation is very limited, even in advanced Python books. How are they meant to be used and what is their difference (higher priority, side of relationship they refer to etc...)?
Both methods can be used to customize the result of the issubclass()
built-in function.
__subclasscheck__
This method is the special method that is responsible for the customization of the issubclass
check. Like the "Note" states it has to implemented on the metaclass!
class YouWontFindSubclasses(type):
def __subclasscheck__(cls, subclass):
print(cls, subclass)
return False
class MyCls(metaclass=YouWontFindSubclasses):
pass
class MySubCls(MyCls):
pass
This implementation will return False even if you have genuine subclasses:
>>> issubclass(MySubCls, MyCls)
<class '__main__.MyCls'> <class '__main__.MySubCls'>
False
There are actually more interesting uses for __subclasscheck__
implementations. For example:
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__']
With this implementation any class that defines __len__
and __iter__
will return True
in a issubclass
check:
>>> issubclass(int, MyCls) # ints have no __len__ or __iter__
False
>>> issubclass(list, MyCls) # but lists and dicts have
True
>>> issubclass(dict, MyCls)
True
In these examples I haven't called the superclasses __subclasscheck__
and thereby disabled the normal issubclass
behavior (which is implemented by type.__subclasscheck__
). But it's important to know that you can also choose to just extend the normal behavior instead of completely overriding it:
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__
The important bit here is that it's defined as classmethod
on the class and it's called by abc.ABC.__subclasscheck__
. So you can only use it if you're dealing with classes that have an ABCMeta
metaclass:
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
This will only go into the __subclasshook__
of the first:
>>> issubclass(int, MyClsABC)
in subclasshook
True
>>> issubclass(int, MyClsNoABC)
False
Note that subsequent issubclass
calls don't go into the __subclasshook__
anymore because ABCMeta
caches the result:
>>> issubclass(int, MyClsABC)
True
Note that you generally check if the first argument is the class itself. That's to avoid that subclasses "inherit" the __subclasshook__
instead of using normal subclass-determination.
For example (from the CPython collections.abc
module):
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
So if you check if something is a subclass of Hashable
it will use the custom __subclasshook__
implementation that is guarded by the if cls is Hashable
. However if you have an actual class implementing the Hashable
interface you don't want it to inherit the __subclasshook__
mechanism but you want the normal subclass mechanism.
For example:
class MyHashable(Hashable):
def __hash__(self):
return 10
>>> issubclass(int, MyHashable)
False
Even though int
implements __hash__
and the __subclasshook__
checks for an __hash__
implementation the result in this case is False
. It still enters the __subclasshook__
of Hashable
but it immediately returns NotImplemented
which signals to ABCMeta
that it should proceed using the normal implementation. If it didn't have the if cls is Hashable
then issubclass(int, MyHashable)
would return True
!
When should you use __subclasscheck__
and when __subclasshook__
?
It really depends. __subclasshook__
can be implemented on the class instead of the metaclass, but requires that you use ABCMeta
(or a subclass of ABCMeta
) as metaclass because the __subclasshook__
method is actually just a convention introduced by Pythons abc
module.
You can always use __subclasscheck__
but it has to be implemented on the metaclass.
In practice you use __subclasshook__
if you implement interfaces (because these normally use abc
) and want to customize the subclass mechanism. And you use __subclasscheck__
if you want to invent your own conventions (like abc
did). So in 99.99% of the normal (not fun) cases you only need __subclasshook__
.
这篇关于python subclasscheck &子类钩子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!