Python抽象基类中的subclasshook方法详解及解析

引言

在 Python 编程中,抽象基类和虚拟子类是重要的概念。Alex 在“水禽和抽象基类”一文中,通过示例展示了即便不注册,抽象基类也能把一个类识别为虚拟子类,而这背后的关键就在于 __subclasshook__ 方法。

抽象基类与虚拟子类的识别

示例说明

文中给出了 Struggle 类的例子:

class Struggle: 
    def __len__(self): return 23 
 
from collections import abc 
isinstance(Struggle(), abc.Sized)  # 输出 True 
issubclass(Struggle, abc.Sized)  # 输出 True 

通过 issubclass 和 isinstance 函数确认,Struggle 是 abc.Sized 的子类。这是因为 abc.Sized 实现了 __subclasshook__ 特殊类方法。

__subclasshook__ 方法源码分析

Sized 类的 __subclasshook__ 方法源码如下:

class Sized(metaclass=ABCMeta): 
    __slots__ = () 
    @abstractmethod 
    def __len__(self): 
        return 0 
    @classmethod 
    def __subclasshook__(cls, C): 
        if cls is Sized: 
            if any("__len__" in B.__dict__ for B in C.__mro__): 
                return True 
        return NotImplemented 
  • 该方法会检查 C.__mro__(即 C 及其超类)中所列的类,如果类的 __dict__ 属性中有名为 __len__ 的属性,就返回 True,表明 C 是 Sized 的虚拟子类。
  • 否则,返回 NotImplemented,让子类检查。
  • __subclasshook__:白鹅类型中的鸭子类型踪迹

    subclasshook 在白鹅类型中添加了鸭子类型的踪迹。我们可以使用抽象基类定义正式接口,通过 isinstance 检查,也可以使用不相关的类,只要实现特定的方法即可(或者做些事情让__subclasshook__信服)。不过,只有提供 __subclasshook__ 方法的抽象基类才能这么做。

    自定义抽象基类中实现 __subclasshook__ 的思考

    实现的可靠性问题
    在 Python 源码中,只有 Sized 这一个抽象基类实现了 __subclasshook__ 方法,且 Sized 只声明了一个特殊方法,只用检查 __len__ 方法。但对于其他特殊方法和基本的抽象基类,很难确定实现特定方法的类就一定符合要求。例如,映射实现了 __len____getitem__ __iter__,但不能把它们视作 Sequence 的子类型。

    建议

    在自己编写的抽象基类中实现 __subclasshook__ 方法可靠性很低。程序员最好让类继承抽象基类,或者使用注册的方式(如 Tombola.register(Spam) )来确保类符合抽象基类的要求。虽然自己实现的 __subclasshook__ 方法还可以检查方法签名和其他特性,但这样做并不值得。

    总结

    __subclasshook__ 方法为 Python 抽象基类识别虚拟子类提供了便利,但在自定义抽象基类时,要谨慎使用该方法,优先考虑继承和注册的方式来保证代码的可靠性。

    作者:钢铁男儿

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python抽象基类中的subclasshook方法详解及解析

    发表回复