gpt4 book ai didi

python子类检查和子类 Hook

转载 作者:IT老高 更新时间:2023-10-28 20:32:45 26 4
gpt4 key购买 nike

方法 __subclasscheck____subclasshook__ 用于判断一个类是否是另一个类的子类。但是,它们的文档非常有限,即使在高级 Python 书籍中也是如此。它们的用途是什么?它们的区别是什么(更高的优先级、它们所指的关系方面等...)?

最佳答案

这两种方法都可以用来自定义 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检查的特殊方法。就像“注释”状态一样,它必须在元类上实现!

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__ 的类都将在 issubclass 检查中返回 True:

>>> 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__

__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.

这里重要的是它被定义为类上的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 的子类,它将使用由 if cls is Hashable 保护的自定义 __subclasshook__ 实现>。但是,如果您有一个实现 Hashable 接口(interface)的实际类,则您不希望它继承 __subclasshook__ 机制,而是希望使用普通的子类机制。

例如:

class MyHashable(Hashable):
def __hash__(self):
return 10

>>> issubclass(int, MyHashable)
False

即使 int 实现 __hash__ 并且 __subclasshook__ 检查 __hash__ 实现,这种情况下的结果是错误。它仍然进入 Hashable__subclasshook__ 但它立即返回 NotImplementedABCMeta 发出信号,它应该继续使用正常执行。如果它没有 if cls is Hashable 那么 issubclass(int, MyHashable) 将返回 True!

什么时候应该使用__subclasscheck__,什么时候应该使用__subclasshook__

这真的取决于。 __subclasshook__ 可以在类而不是元类上实现,但要求您使用 ABCMeta(或 ABCMeta 的子类)作为元类,因为__subclasshook__ 方法实际上只是 Python 的 abc 模块引入的一个约定。

您始终可以使用 __subclasscheck__,但它必须在元类上实现。

实际上,如果您实现接口(interface)(因为这些接口(interface)通常使用 abc)并希望自定义子类机制,则使用 __subclasshook__。如果你想发明自己的约定(就像 abc 那样),你可以使用 __subclasscheck__。因此,在 99.99% 的正常(不好玩)情况下,您只需要 __subclasshook__

关于python子类检查和子类 Hook ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40764347/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com