python 子类检查和子类钩子
python subclasscheck & subclasshook
方法__subclasscheck__
和__subclasshook__
用于判断一个class是否被视为另一个class的subclass。但是,他们的文档非常有限,即使是高级 Python 书籍也是如此。它们的用途是什么?它们的区别是什么(更高优先级、它们所指的关系方面等...)?
__subclasshook__
and __subclasscheck__
用于自定义 issubclass
函数的行为。
abc source code.
中有更多信息
__subclasscheck__
是在 class 的类型 (metaclass) 上查找的。不应为普通 class.
定义
__subclasshook__
检查 subclass 是否被认为是某个 ABC 的 subclass。这意味着您可以进一步自定义 issubclass
的行为,而无需在要考虑 ABC 的子 class 的每个 class 上调用 register()。
这意味着您可以在您的 ABC class 中定义 __subclasshook__
一些条件,并且所有满足该条件的 class 都将被视为子 class。
例如:
from abc import ABCMeta
class Sized(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
if any("__len__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
class A(object):
pass
class B(object):
def __len__(self):
return 0
issubclass(A, Sized) # False
issubclass(B, Sized) # True
这两种方法都可用于自定义 issubclass()
内置函数的结果。
__subclasscheck__
class.__subclasscheck__(self, subclass)
Return true if subclass should be considered a (direct or indirect) subclass of class. If defined, called to implement issubclass(subclass, class)
.
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.
此方法是负责自定义issubclass
检查的特殊方法。就像“注意”指出的那样,它必须在 metaclass!
上实现
class YouWontFindSubclasses(type):
def __subclasscheck__(cls, subclass):
print(cls, subclass)
return False
class MyCls(metaclass=YouWontFindSubclasses):
pass
class MySubCls(MyCls):
pass
即使您有真正的子 classes:
,此实现也会 return 错误
>>> 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__
的任何 class 将 return 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
在这些示例中,我没有调用 superclasses __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__
__subclasshook__(subclass)
(Must be defined as a class method.)
Check whether subclass is considered a subclass of this ABC. This means that you can customize the behavior of issubclass
further without the need to call register()
on every class you want to consider a subclass of the ABC. (This class method is called from the __subclasscheck__()
method of the ABC.)
This method should return True
, False
or NotImplemented
. If it returns True
, the subclass is considered a subclass of this ABC. If it returns False
, the subclass is not considered a subclass of this ABC, even if it would normally be one. If it returns NotImplemented
, the subclass check is continued with the usual mechanism.
这里重要的一点是它在 class 上定义为 classmethod
并由 abc.ABC.__subclasscheck__
调用。所以你只能在处理具有 ABCMeta
metaclass:
的 classes 时使用它
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
请注意,您通常会检查第一个参数是否是 class 本身。这是为了避免 subclasses“继承”__subclasshook__
而不是使用正常的 subclass-determination.
例如(来自 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
的子 class,它将使用由 if cls is Hashable
保护的自定义 __subclasshook__
实现。但是,如果您有一个实际的 class 实现 Hashable
接口,您不希望它继承 __subclasshook__
机制,但您需要正常的 subclass 机制。
例如:
class MyHashable(Hashable):
def __hash__(self):
return 10
>>> issubclass(int, MyHashable)
False
即使 int
实现了 __hash__
并且 __subclasshook__
检查了 __hash__
实现,但在这种情况下结果是 False
。它仍然进入 Hashable
的 __subclasshook__
但它立即 returns NotImplemented
向 ABCMeta
发出信号,表明它应该继续使用正常的实现。如果它没有 if cls is Hashable
那么 issubclass(int, MyHashable)
会 return True
!
什么时候应该使用 __subclasscheck__
,什么时候应该使用 __subclasshook__
?
这真的取决于。 __subclasshook__
可以在 class 而不是元 class 上实现,但需要您使用 ABCMeta
(或 ABCMeta
的子 class ) 作为 metaclass 因为 __subclasshook__
方法实际上只是 Pythons abc
模块引入的约定。
您可以随时使用 __subclasscheck__
,但必须在 metaclass.
上实现
实际上,如果您实现接口(因为这些接口通常使用 abc
)并且想要自定义 subclass 机制,则您会使用 __subclasshook__
。如果你想发明自己的约定,你可以使用 __subclasscheck__
(就像 abc
那样)。所以在 99.99% 的正常(不好玩)情况下你只需要 __subclasshook__
.
方法__subclasscheck__
和__subclasshook__
用于判断一个class是否被视为另一个class的subclass。但是,他们的文档非常有限,即使是高级 Python 书籍也是如此。它们的用途是什么?它们的区别是什么(更高优先级、它们所指的关系方面等...)?
__subclasshook__
and __subclasscheck__
用于自定义 issubclass
函数的行为。
abc source code.
__subclasscheck__
是在 class 的类型 (metaclass) 上查找的。不应为普通 class.
__subclasshook__
检查 subclass 是否被认为是某个 ABC 的 subclass。这意味着您可以进一步自定义 issubclass
的行为,而无需在要考虑 ABC 的子 class 的每个 class 上调用 register()。
这意味着您可以在您的 ABC class 中定义 __subclasshook__
一些条件,并且所有满足该条件的 class 都将被视为子 class。
例如:
from abc import ABCMeta
class Sized(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
if any("__len__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
class A(object):
pass
class B(object):
def __len__(self):
return 0
issubclass(A, Sized) # False
issubclass(B, Sized) # True
这两种方法都可用于自定义 issubclass()
内置函数的结果。
__subclasscheck__
class.__subclasscheck__(self, subclass)
Return true if subclass should be considered a (direct or indirect) subclass of class. If defined, called to implement
issubclass(subclass, class)
.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.
此方法是负责自定义issubclass
检查的特殊方法。就像“注意”指出的那样,它必须在 metaclass!
class YouWontFindSubclasses(type):
def __subclasscheck__(cls, subclass):
print(cls, subclass)
return False
class MyCls(metaclass=YouWontFindSubclasses):
pass
class MySubCls(MyCls):
pass
即使您有真正的子 classes:
,此实现也会 return 错误>>> 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__
的任何 class 将 return 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
在这些示例中,我没有调用 superclasses __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__
__subclasshook__(subclass)
(Must be defined as a class method.)
Check whether subclass is considered a subclass of this ABC. This means that you can customize the behavior of
issubclass
further without the need to callregister()
on every class you want to consider a subclass of the ABC. (This class method is called from the__subclasscheck__()
method of the ABC.)This method should return
True
,False
orNotImplemented
. If it returnsTrue
, the subclass is considered a subclass of this ABC. If it returnsFalse
, the subclass is not considered a subclass of this ABC, even if it would normally be one. If it returnsNotImplemented
, the subclass check is continued with the usual mechanism.
这里重要的一点是它在 class 上定义为 classmethod
并由 abc.ABC.__subclasscheck__
调用。所以你只能在处理具有 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
这只会进入第一个__subclasshook__
:
>>> issubclass(int, MyClsABC)
in subclasshook
True
>>> issubclass(int, MyClsNoABC)
False
请注意,后续 issubclass
调用不再进入 __subclasshook__
,因为 ABCMeta
缓存了结果:
>>> issubclass(int, MyClsABC)
True
请注意,您通常会检查第一个参数是否是 class 本身。这是为了避免 subclasses“继承”__subclasshook__
而不是使用正常的 subclass-determination.
例如(来自 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
的子 class,它将使用由 if cls is Hashable
保护的自定义 __subclasshook__
实现。但是,如果您有一个实际的 class 实现 Hashable
接口,您不希望它继承 __subclasshook__
机制,但您需要正常的 subclass 机制。
例如:
class MyHashable(Hashable):
def __hash__(self):
return 10
>>> issubclass(int, MyHashable)
False
即使 int
实现了 __hash__
并且 __subclasshook__
检查了 __hash__
实现,但在这种情况下结果是 False
。它仍然进入 Hashable
的 __subclasshook__
但它立即 returns NotImplemented
向 ABCMeta
发出信号,表明它应该继续使用正常的实现。如果它没有 if cls is Hashable
那么 issubclass(int, MyHashable)
会 return True
!
什么时候应该使用 __subclasscheck__
,什么时候应该使用 __subclasshook__
?
这真的取决于。 __subclasshook__
可以在 class 而不是元 class 上实现,但需要您使用 ABCMeta
(或 ABCMeta
的子 class ) 作为 metaclass 因为 __subclasshook__
方法实际上只是 Pythons abc
模块引入的约定。
您可以随时使用 __subclasscheck__
,但必须在 metaclass.
实际上,如果您实现接口(因为这些接口通常使用 abc
)并且想要自定义 subclass 机制,则您会使用 __subclasshook__
。如果你想发明自己的约定,你可以使用 __subclasscheck__
(就像 abc
那样)。所以在 99.99% 的正常(不好玩)情况下你只需要 __subclasshook__
.