方法__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/

10-11 22:44