python subclasscheck &子类钩子 [英] python subclasscheck & subclasshook

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

问题描述

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

解决方案

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

__subclasscheck__

<块引用>

class.__subclasscheck__(self, subclass)

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

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

该方法是负责自定义issubclass检查的特殊方法.像注"一样声明它必须在元类上实现!

class YouWontFindSubclasses(type):def __subclasscheck__(cls, 子类):打印(CLS,子类)返回错误类 MyCls(metaclass=YouWontFindSubclasses):经过类 MySubCls(MyCls):经过

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

<预><代码>>>>issubclass(MySubCls, MyCls)<class '__main__.MyCls'><class '__main__.MySubCls'>错误的

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

class SpecialSubs(type):def __subclasscheck__(cls, 子类):required_attrs = getattr(cls, '_required_attrs', [])对于 required_attrs 中的 attr:如果有的话(sub.__dict__ 中的 attr 用于 subclass.__mro__ 中的 sub):继续返回错误返回真类 MyCls(metaclass=SpecialSubs):_required_attrs = ['__len__', '__iter__']

使用此实现,任何定义 __len____iter__ 的类将在 issubclass 检查中返回 True:

<预><代码>>>>issubclass(int, MyCls) # ints 没有 __len__ 或 __iter__错误的>>>issubclass(list, MyCls) # 但列表和字典有真的>>>issubclass(dict, MyCls)真的

在这些示例中,我没有调用超类 __subclasscheck__ 从而禁用了正常的 issubclass 行为(由 type.__subclasscheck__ 实现)).但重要的是要知道您也可以选择只扩展正常行为而不是完全覆盖它:

类元(类型):def __subclasscheck__(cls, 子类):"""只需修改不是真正子类的类的行为."""if super().__subclasscheck__(subclass):返回真别的:# 不是普通的子类,这里实现一些自定义.

__subclasshook__

<块引用>

__subclasshook__(subclass)

(必须定义为类方法.)

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

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

这里的重要一点是它在类上被定义为 classmethod 并且它被 abc.ABC.__subclasscheck__ 调用.因此,只有在处理具有 ABCMeta 元类的类时才能使用它:

导入 abc类 MyClsABC(abc.ABC):@类方法def __subclasshook__(cls, subclass):打印('在子类钩子中')返回真类 MyClsNoABC(对象):@类方法def __subclasshook__(cls, subclass):打印('在子类钩子中')返回真

这只会进入第一个的__subclasshook__:

<预><代码>>>>issubclass(int, MyClsABC)在子类钩子中真的>>>issubclass(int, MyClsNoABC)错误的

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

<预><代码>>>>issubclass(int, MyClsABC)真的

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

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

from abc import ABCMeta, abstractmethoddef _check_methods(C, *methods):mro = C.__mro__对于方法中的方法:对于 mro 中的 B:B.__dict__ 中的 if 方法:如果 B.__dict__[method] 是 None:返回 NotImplemented休息别的:返回 NotImplemented返回真哈希类(元类 = ABCMeta):__插槽__ = ()@抽象方法def __hash__(self):返回 0@类方法def __subclasshook__(cls, C):如果 cls 是可哈希的:返回 _check_methods(C, "__hash__")返回 NotImplemented

因此,如果您检查某事物是否是 Hashable 的子类,它将使用由 if cls is Hashable 保护的自定义 __subclasshook__ 实现>.但是,如果您有一个实际的类实现了 Hashable 接口,您不希望它继承 __subclasshook__ 机制,而是想要普通的子类机制.

例如:

class MyHashable(Hashable):def __hash__(self):返回 10>>>issubclass(int, MyHashable)错误的

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

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

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

你总是可以使用 __subclasscheck__ 但它必须在元类上实现.

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

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

解决方案

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

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

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

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

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

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

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

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__

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

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

This will only go into the __subclasshook__ of the first:

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

>>> issubclass(int, MyClsNoABC)
False

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

>>> issubclass(int, MyClsABC)
True

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.

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

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.

For example:

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

>>> issubclass(int, MyHashable)
False

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!

When should you use __subclasscheck__ and when __subclasshook__?

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.

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

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 subclasscheck &amp;子类钩子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆