python subclasscheck&subclasshook

blu*_*ote 24 python python-3.x

方法__subclasscheck____subclasshook__用于确定一个类是否被视为另一个类的子类.但是,他们的文档非常有限,即使在高级python书籍中也是如此.它们是如何被使用的,它们的区别是什么(更高的优先级,它们所指的关系的一面......等等)

MSe*_*ert 32

这两种方法都可用于自定义issubclass()内置函数的结果.

__subclasscheck__

class.__subclasscheck__(self, subclass)

如果子类应被视为类的(直接或间接)子类,则返回true.如果定义,则调用实现issubclass(subclass, class).

请注意,这些方法是在类的类型(元类)上查找的.它们不能在实际类中定义为类方法.这与在实例上调用的特殊方法的查找一致,只是在这种情况下,实例本身就是一个类.

此方法是负责自定义issubclass检查的特殊方法.就像"Note"状态一样,它必须在元类上实现!

class YouWontFindSubclasses(type):
    def __subclasscheck__(cls, subclass):
        print(cls, subclass)
        return False

class MyCls(metaclass=YouWontFindSubclasses):
    pass

class MySubCls(MyCls):
    pass
Run Code Online (Sandbox Code Playgroud)

即使您有真正的子类,此实现也将返回False:

>>> issubclass(MySubCls, MyCls)
<class '__main__.MyCls'> <class '__main__.MySubCls'>
False
Run Code Online (Sandbox Code Playgroud)

实际上有更多有趣的用途__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__']
Run Code Online (Sandbox Code Playgroud)

通过此实现,任何定义__len__并将在检查中__iter__返回的类:Trueissubclass

>>> issubclass(int, MyCls)  # ints have no __len__ or __iter__
False
>>> issubclass(list, MyCls)  # but lists and dicts have
True
>>> issubclass(dict, MyCls)
True
Run Code Online (Sandbox Code Playgroud)

在这些示例中,我没有调用超类__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.
Run Code Online (Sandbox Code Playgroud)

__subclasshook__

__subclasshook__(subclass)

(必须定义为类方法.)

检查子类是否被视为此ABC的子类.这意味着您可以issubclass进一步自定义行为,而无需调用register()要考虑ABC子类的每个类.(这个类方法是从__subclasscheck__()ABC 的方法调用的.)

这个方法应该返回True,False或者NotImplemented.如果它返回True,则子类被视为该ABC的子类.如果它返回False,则子类不被视为该ABC的子类,即使它通常是一个.如果它返回NotImplemented,则使用通常的机制继续子类检查.

这里重要的一点是,它被定义为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
Run Code Online (Sandbox Code Playgroud)

这只会进入__subclasshook__第一个:

>>> issubclass(int, MyClsABC)
in subclasshook
True

>>> issubclass(int, MyClsNoABC)
False
Run Code Online (Sandbox Code Playgroud)

请注意,后续issubclass调用不再进入__subclasshook__,因为ABCMeta缓存结果:

>>> issubclass(int, MyClsABC)
True
Run Code Online (Sandbox Code Playgroud)

请注意,您通常会检查第一个参数是否为类本身.这是为了避免子类"继承" __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
Run Code Online (Sandbox Code Playgroud)

因此,如果你检查某些东西是否是Hashable它的子类,它将使用由__subclasshook__守护的自定义实现if cls is Hashable.但是,如果您有实现该Hashable接口的实际类,则不希望它继承该__subclasshook__机制,但您需要正常的子类机制.

例如:

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

>>> issubclass(int, MyHashable)
False
Run Code Online (Sandbox Code Playgroud)

即使int实现__hash____subclasshook__检查__hash__实现,在这种情况下的结果是False.它仍然进入__subclasshook__,Hashable但它立即返回NotImplemented哪些信号ABCMeta应该使用正常实现继续.如果它没有if cls is Hashable那么issubclass(int, MyHashable)就会回来True!

你何时应该使用__subclasscheck__何时__subclasshook__

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

您可以随时使用,__subclasscheck__但必须在元类上实现.

在实践中,__subclasshook__如果您实现接口(因为这些通常使用abc)并且想要自定义子类机制,则使用它.__subclasscheck__如果你想发明自己的约定(比如abc做),你可以使用它.因此,您只需要99.99%的正常(不好玩)案例__subclasshook__.


小智 6

__subclasshook____subclasscheck__用于自定义issubclass函数的行为.abc源代码中的更多信息.

__subclasscheck__查找类的类型(元类).它不应该为普通类定义.

__subclasshook__检查子类是否被视为某些ABC的子类.这意味着您可以issubclass进一步自定义行为,而无需在要考虑ABC子类的每个类上调用register().

这意味着您可以__subclasshook__在ABC类中定义一些条件,并且满足该条件的所有类都将作为子类考虑.

例如:

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
Run Code Online (Sandbox Code Playgroud)

  • 为什么`if cls是大小的:`需要行? (3认同)