python子检查&子检查

2024-04-24 23:07:57 发布

您现在位置:Python中文网/ 问答频道 /正文

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


Tags: 方法文档关系子类书籍区别所指subclasshook
2条回答

^{}^{}用于自定义issubclass函数的行为。 更多信息请参见abc source code

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

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

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

例如:

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

这两个方法都可以用于自定义^{}内置函数的结果。

^{}

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检查的特殊方法。就像“Note”声明它必须在元类上实现一样!

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

在这些示例中,我没有调用超类__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__(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

注意,通常检查第一个参数是否是类本身。这是为了避免子类“继承”子类而不是使用常规的子类确定。

例如(来自CPythoncollections.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接口的实际类,您不希望它继承__subclasshook__机制,而希望它继承普通的子类机制。

例如:

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

>>> issubclass(int, MyHashable)
False

即使int实现了__hash__,并且__subclasshook__检查了__hash__实现,在本例中的结果是False。它仍然进入Hashable__subclasshook__,但它立即返回NotImplemented,这向ABCMeta发出信号,表明它应该使用正常实现继续。如果没有if cls is Hashable,那么issubclass(int, MyHashable)将返回True

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

这要看情况。__subclasshook__可以在类而不是元类上实现,但需要使用ABCMeta(或ABCMeta的子类)作为元类,因为__subclasshook__方法实际上只是Pythonsabc模块引入的一个约定。

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

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

相关问题 更多 >