本文介绍了python子类检查&子类挂钩的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

方法__subclasscheck____subclasshook__用于确定某个类是否被视为另一个类的子类.但是,即使在高级Python书籍中,它们的文档也非常有限.

The methods __subclasscheck__ and __subclasshook__ are used to determine if a class is regarded as subclass of another. However, their documentation is very limited, even in advanced Python books. How are they meant to be used and what is their difference (higher priority, side of relationship they refer to etc...)?

推荐答案

两种方法都可以用于自定义 issubclass() 内置函数.

Both methods can be used to customize the result of the issubclass() built-in function.

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

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检查的特殊方法.就像注"声明必须在元类上实现!

This method is the special method that is responsible for the customization of the issubclass check. Like the "Note" states it has to implemented on the metaclass!

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

class MyCls(metaclass=YouWontFindSubclasses):
    pass

class MySubCls(MyCls):
    pass

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

This implementation will return False even if you have genuine subclasses:

>>> issubclass(MySubCls, MyCls)
<class '__main__.MyCls'> <class '__main__.MySubCls'>
False

__subclasscheck__实现实际上有更有趣的用途.例如:

There are actually more interesting uses for __subclasscheck__ implementations. For example:

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:

With this implementation any class that defines __len__ and __iter__ will return True in a issubclass check:

>>> 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__实现).但重要的是要知道,您也可以选择扩展正常行为,而不是完全覆盖它:

In these examples I haven't called the superclasses __subclasscheck__ and thereby disabled the normal issubclass behavior (which is implemented by type.__subclasscheck__). But it's important to know that you can also choose to just extend the normal behavior instead of completely overriding it:

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__

(必须定义为类方法.)

(Must be defined as a class method.)

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

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

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

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元类的类时,才可以使用它:

The important bit here is that it's defined as classmethod on the class and it's called by abc.ABC.__subclasscheck__. So you can only use it if you're dealing with classes that have an 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会缓存结果:

Note that subsequent issubclass calls don't go into the __subclasshook__ anymore because ABCMeta caches the result:

>>> issubclass(int, MyClsABC)
True

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

Note that you generally check if the first argument is the class itself. That's to avoid that subclasses "inherit" the __subclasshook__ instead of using normal subclass-determination.

例如(来自CPython collections.abc模块):

For example (from the CPython collections.abc module):

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__机制,而是希望使用普通的子类机制.

So if you check if something is a subclass of Hashable it will use the custom __subclasshook__ implementation that is guarded by the if cls is Hashable. However if you have an actual class implementing the Hashable interface you don't want it to inherit the __subclasshook__ mechanism but you want the normal subclass mechanism.

例如:

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

Even though int implements __hash__ and the __subclasshook__ checks for an __hash__ implementation the result in this case is False. It still enters the __subclasshook__ of Hashable but it immediately returns NotImplemented which signals to ABCMeta that it should proceed using the normal implementation. If it didn't have the if cls is Hashable then issubclass(int, MyHashable) would return True!

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

It really depends. __subclasshook__ can be implemented on the class instead of the metaclass, but requires that you use ABCMeta (or a subclass of ABCMeta) as metaclass because the __subclasshook__ method is actually just a convention introduced by Pythons abc module.

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

You can always use __subclasscheck__ but it has to be implemented on the metaclass.

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

In practice you use __subclasshook__ if you implement interfaces (because these normally use abc) and want to customize the subclass mechanism. And you use __subclasscheck__ if you want to invent your own conventions (like abc did). So in 99.99% of the normal (not fun) cases you only need __subclasshook__.

这篇关于python子类检查&amp;子类挂钩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 11:50