abc模块(抽象基类)

abc: 是 Python 标准库中的一个模块,主要用于定义抽象基类(Abstract Base Classes)。抽象基类提供了一种机制,允许我们在面向对象编程中定义接口,以确保子类实现特定的方法或属性。
示例代码:

from abc import ABC, abstractmethod

class ABCBase(ABC):
    @abstractmethod
    def hello(self):
        pass

    def hi(self):
        pass

class MyClass(ABCBase):
    def fun(self):
        print('this is fun')

上述代码运行不报错。但是当对MyClass进行实例化时,就会提示错误。
myclass = MyClass()
错误提示:

Traceback (most recent call last):
  File "F:\RolandWork\PythonProjects\studyPython\forTest.py", line 15, in <module>
    myclass = MyClass()
              ^^^^^^^^^
TypeError: Can't instantiate abstract class MyClass with abstract method hello

原因是我们定义了抽像基类后,继承它的子类要实现所有基类里的抽象方法。 所以我们在子类中增加对基类中抽象方法hello的实现,就不会再报错了。

from abc import ABC, abstractmethod

class ABCBase(ABC):
    @abstractmethod
    def hello(self):
        pass

    def hi(self):
        print('基类 say hi')

class MyClass(ABCBase):
    def fun(self):
        print('this is fun')

    def hello(self):
        print('子类 say hello')

myclass = MyClass()
myclass.hello()
myclass.hi()

输出结果:

子类 say hello
基类 say hi

并且,只要是基类和子类的方法名相同,即便方法签名不同,也不报错。

from abc import ABC, abstractmethod

class ABCBase(ABC):
    @abstractmethod
    def hello(self, name):
        pass

    @abstractmethod
    def hi(self):
        print('基类 say hi')

class MyClass(ABCBase):
    def hello(self):
        print('子类 say hello')

    def hi(self, name):
        print('子类 say hi', name)

myclass = MyClass()
myclass.hello()
myclass.hi('Roland')

输出结果:

子类 say hello
子类 say hi Roland

抽象类不能被实例化。

from abc import ABC, abstractmethod

class ABCBase(ABC):
    @abstractmethod
    def hello(self, name):
        pass

abc_obj = ABCBase()

报错如下:

Traceback (most recent call last):
  File "F:\RolandWork\PythonProjects\studyPython\forTest.py", line 8, in <module>
    abc_obj = ABCBase()
              ^^^^^^^^^
TypeError: Can't instantiate abstract class ABCBase with abstract method hello

抽象类的虚拟子类机制: 一个类可以不通过继承另一个类,但却能被issubclass函数判断该类为另一个类的子类。 我们说这个类其实是另一个类的虚拟子类。
先看一下实现这种虚拟子类机制的其中一种方式,使用__subclasshook__特殊方法。

from abc import ABC, ABCMeta

class MyABC(ABC):
    @classmethod
    def __subclasshook__(cls, subclass):
        # 如果子类有 'method1' 和 'method2' 属性或方法,就认为它是 MyABC 的子类
        if hasattr(subclass, 'method1') and hasattr(subclass, 'method2'):
            return True
        return NotImplemented

class SubClass:
    def method1(self):
        pass

    def method2(self):
        pass

class AnotherClass:
    def method1(self):
        pass

# 检查子类关系
print(issubclass(SubClass, MyABC))  # True
print(issubclass(AnotherClass, MyABC))  # False

subinstance = SubClass()
print(isinstance(subinstance, MyABC))  # True  构造虚拟子类的关系后,isinstance函数判断结果也会为True

当我们让该方法返回NotImplemented时,它就会转而遵循python默认的判断子类的机制。

另一种实现虚拟子类方法是ABC.register, 这个抽象基类方法来自于元类ABCMeta。这种方法更为简单直接。

from abc import ABC

class MyABC(ABC):
    pass

class Foo:
    pass

MyABC.register(Foo)  # 使用register方法将一个类直接注册为另一个抽象基类的虚拟子类

print(issubclass(Foo, MyABC))  # True

foo = Foo()
print(isinstance(foo, MyABC))  # True

abc.get_cache_token(): 用于获取当前的抽象基类(ABC)注册缓存的版本标记。它的主要目的是在动态注册虚拟子类时,提供一种机制来检测抽象基类的子类关系缓存是否发生了变化。
我们一般不会直接使用这个函数,可能在某些特别场景下,比如元编程时会用到。

from abc import ABC, get_cache_token

print('catch token is', get_cache_token())

class MyABC(ABC):
    pass

class MyClass:
    def method(self):
        print("Method in MyClass")

print('catch token is', get_cache_token())
# 注册 MyClass 为 MyABC 的虚拟子类
MyABC.register(MyClass)
print('catch token is', get_cache_token())

class Foo:
    pass

MyABC.register(Foo)
print('catch token is', get_cache_token())

class AnotherABC(ABC):
    pass

print('catch token is', get_cache_token())
AnotherABC.register(Foo)
print('catch token is', get_cache_token())

输出结果:

catch token is 25
catch token is 25
catch token is 26
catch token is 27
catch token is 27
catch token is 28

从以上示例代码的执行结果可以看出,abc.get_cache_token()会捕获每一次的虚拟子类的变化,并生成新的catch token。 注意,__subclasshook__不会影响cache token的值。

abc.update_abstractmethods(cls): 是 Python 的 abc 模块中的一个辅助工具,用于在运行时重新计算并更新类的抽象方法集合(即类的 abstractmethods 属性)
它在动态类生成、复杂继承关系的管理以及元编程中非常有用,但日常开发中较少用到。其主要作用是维护 abstractmethods 的正确性,确保类是否为抽象类的状态与其定义保持一致。
虽然官方文档没有明确提到 __abstractmethods__,但这个属性在 ABCMeta 中起到了关键作用。__abstractmethods__ 是由 ABCMeta 自动生成并维护的一个集合,包含所有未被实现的抽象方法。

import abc
from abc import ABC, abstractmethod, update_abstractmethods

class MyABC(ABC):
    @abstractmethod
    def method1(self):
        pass

# 一个子类,没有实现所有抽象方法
class MyClass(MyABC):
    pass


print('类定义后:', MyClass.__abstractmethods__)  # {'method1'}

# 动态添加一个抽象方法
def new_abstract_method(self):
    pass

MyClass.method2 = abstractmethod(new_abstract_method)

print('动态填加抽象方法method2,但未执行update前:', MyClass.__abstractmethods__)

# 更新抽象方法集合
update_abstractmethods(MyClass)
print('动态填加抽象方法method2,执行update后:', MyClass.__abstractmethods__)  # {'method1', 'method2'}

# 动态实现一个抽象方法
MyClass.method1 = lambda self: None
update_abstractmethods(MyClass)
print('动态填加普通方法method1,执行update后:', MyClass.__abstractmethods__)  # {'method2'}

del MyClass.method2
update_abstractmethods(MyClass)
print('动态删除method2方法,执行update后:', MyClass.__abstractmethods__)
print('vars(MyClass):', vars(MyClass))
myclass = MyClass() # 实例化 所有基类的抽象方法都实现了,所以可以正常创建实例对象

输出结果:

类定义后: frozenset({'method1'})
动态填加抽象方法method2,但未执行update前: frozenset({'method1'})
动态填加抽象方法method2,执行update后: frozenset({'method1', 'method2'})
动态填加普通方法method1,执行update后: frozenset({'method2'})
动态删除method2方法,执行update后: frozenset()
vars(MyClass): {'__module__': '__main__', '__doc__': None, '__abstractmethods__': frozenset(), '_abc_impl': <_abc._abc_data object at 0x0000020F16565C40>, 'method1': <function <lambda> at 0x0000020F165639C0>}

从示例代码及输出结果可以发现,当我们动态修改类中的抽象方法后,如果不调用此方法,那么__abstractmethods__属性里的内容就不会更新,导致我们用类创建对象时,是否满足创建的条件的判断不准确。继承抽象类的子类必须要满足实现所有基类的抽象方法时才可以实例化。

抽象基类的方法中使用装饰器,并不会对子类的方法产生任何约束。

from abc import ABC, abstractmethod

class MyABC(ABC):
    @abstractmethod
    def my_abstract_method(self):
        pass

    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls):
        pass

    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod():
        pass

    @property
    @abstractmethod
    def my_abstract_property(self):
        return 'abc'


class MyClass(MyABC):
    def my_abstract_method(self):
        pass

    def my_abstract_classmethod(cls):
        pass

    def my_abstract_staticmethod(self):
        pass

    def my_abstract_property(self):
        pass


obj = MyClass()  # 实例化时没报错,说明子类只要有相同的方法名即可,不用满足那些@property, @classmethod等等装饰器。

关于abc模块里提供给我们的虚拟子类的概念,我们需要注意几点:
register()方法与__subclasshook__特殊方法都会影响虚拟子类的判断逻辑。 也会影响issubclass()和isinstance()两个函数的返回结果。并且这两个函数的返回结果是一致的。
但,无论我们虚拟子类的判断结果为True或False, 子类继承父类的属性和方法的规则都不受影响,而只受类声明时的继承链的影响。
请看下面的示例:

from abc import abstractmethod, ABC

class MyIterable(ABC):
    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, C):
        return False
        # return NotImplemented

class Foo:
    pass

class Bar(MyIterable):
    pass


MyIterable.register(Foo)  # 使用register函数将Foo类注册为MyIterable的虚拟子类
print('issubclass(Foo, MyIterable):', issubclass(Foo, MyIterable))
print('isinstance(Foo(), MyIterable):', isinstance(Foo(), MyIterable))
print("hasattr(Foo, 'get_iterator'):", hasattr(Foo, 'get_iterator'))
print('Foo.mro():', Foo.mro())

print('-' * 25, '分隔线', '-' * 25)

print('issubclass(Bar, MyIterable):', issubclass(Bar, MyIterable))
# print('isinstance(Bar(), MyIterable):', isinstance(Bar(), MyIterable))  # 子类未实现所有抽象方法,会报错
print("hasattr(Bar, 'get_iterator'):", hasattr(Bar, 'get_iterator'))
print('Bar.mro():', Bar.mro())

输出结果:

issubclass(Foo, MyIterable): False
isinstance(Foo(), MyIterable): False
hasattr(Foo, 'get_iterator'): False
Foo.mro(): [<class '__main__.Foo'>, <class 'object'>]
------------------------- 分隔线 -------------------------
issubclass(Bar, MyIterable): False
hasattr(Bar, 'get_iterator'): True
Bar.mro(): [<class '__main__.Bar'>, <class '__main__.MyIterable'>, <class 'abc.ABC'>, <class 'object'>]

从结果中可以看出,虽然Foo类已被使用register函数注册为MyIterable的虚拟子类,但MyIterable类定义了__subclasshook__方法,并且直接返回False。导致issubclass和isinstance两个函数都会返回False。
而Foo类的mro不会因为虚拟子类的判定逻辑受影响,只是真实反应了类定义时的继承链。
Foo类中是否包含某一方法,也是在其mro中的继承链的顺序来查找,也不受虚拟子类判定逻辑的影响。
Bar在类定义中继承了MyIterable类,所以可以查找到所有MyIterable类中的方法,并且mro也真实反映了其类定义时的继承关系。但由于父类中存在__subclasshook__方法,所以issubclass和isinstance两个函数的返回结果都受到了该特殊方法的控制。
如果我们即便定义了__subclasshook__方法,但想让其按照正常的虚拟子类判断逻辑走(虽然实际上不会有这种需求),我们可以直接让该方法返回NotImplemented.
比如,我们将__subclasshook__方法的实现改为如下代码:

@classmethod
def __subclasshook__(cls, C):
	# return False
	return NotImplemented

再次运行后,输出结果将变成:

issubclass(Foo, MyIterable): True
isinstance(Foo(), MyIterable): True
hasattr(Foo, 'get_iterator'): False
Foo.mro(): [<class '__main__.Foo'>, <class 'object'>]
------------------------- 分隔线 -------------------------
issubclass(Bar, MyIterable): True
hasattr(Bar, 'get_iterator'): True
Bar.mro(): [<class '__main__.Bar'>, <class '__main__.MyIterable'>, <class 'abc.ABC'>, <class 'object'>]

下面的例子只是在上面的例子基础上加了些日志,以便更浅析的观察对特殊方法调用。

from abc import abstractmethod, ABC

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, subclass):
        print('*' * 50)
        print('MyIterable.__subclasshook__ is invoked')
        print('cls is', cls, ', subclass is', subclass)
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in subclass.__mro__):
                print('MyIterable.__subclasshook__ 返回 True')
                return True
        print('MyIterable.__subclasshook__ 返回 NotImplemented')
        return NotImplemented

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class Bar(MyIterable):
    pass

print('-' * 25, '下面检验Foo类 分隔线', '-' * 25)

print('-' * 25, 'register(Foo)执行前 分隔线', '-' * 25)
MyIterable.register(Foo)  # 使用register函数将Foo类注册为MyIterable的虚拟子类

print('-' * 25, 'register(Foo)执行后 分隔线', '-' * 25)
print('issubclass(Foo, MyIterable):', issubclass(Foo, MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print('isinstance(Foo(), MyIterable):', isinstance(Foo(), MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Foo, 'get_iterator'):", hasattr(Foo, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Foo.mro():', Foo.mro())

print('-' * 25, '下面检验Bar类 分隔线', '-' * 25)

print('issubclass(Bar, MyIterable):', issubclass(Bar, MyIterable))
# print('isinstance(Bar(), MyIterable):', isinstance(Bar(), MyIterable))  # 子类未实现所有抽象方法,会报错
print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Bar, 'get_iterator'):", hasattr(Bar, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Bar.mro():', Bar.mro())

输出结果:

------------------------- 下面检验Foo类 分隔线 -------------------------
------------------------- register(Foo)执行前 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
MyIterable.__subclasshook__ 返回 NotImplemented
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.Bar'> , subclass is <class '__main__.Foo'>
MyIterable.__subclasshook__ 返回 NotImplemented
------------------------- register(Foo)执行后 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
MyIterable.__subclasshook__ 返回 NotImplemented
issubclass(Foo, MyIterable): True
------------------------- 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
MyIterable.__subclasshook__ 返回 NotImplemented
isinstance(Foo(), MyIterable): True
------------------------- 分隔线 -------------------------
hasattr(Foo, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Foo.mro(): [<class '__main__.Foo'>, <class 'object'>]
------------------------- 下面检验Bar类 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Bar'>
MyIterable.__subclasshook__ 返回 True
issubclass(Bar, MyIterable): True
------------------------- 分隔线 -------------------------
hasattr(Bar, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Bar.mro(): [<class '__main__.Bar'>, <class '__main__.MyIterable'>, <class 'abc.ABC'>, <class 'object'>]

从输出结果上,我们可以推测:

  1. register()方法会调用__subclasshook__特殊方法。
  2. 有多个类继承自同一抽象基类时,register()方法可能调用__subclasshook__特殊方法多次,以判断要检查的类是不是其他类的子类,以确定各个类之间的从属关系,最终判断要检查的类会不会间接继承抽象基类。
  3. python会根据__subclasshook__特殊方法返回True,False, NotImplemented的值,来做进一步判断。 如果返回的是NotImplemented,则register方法会让其最终仍被判定为虚拟子类。 如果返回True,当然也是虚拟子类,如果返回False,则register方法不会将其注册为虚拟子类。总之,issubclass()的返回结果优先由__subclasshook__特殊方法决定,如果返回的是NotImplemented,则再依照正常规则继续判断。如果返回True或False,则忽略其他规则。
  4. isinstance函数内部也会依赖issubclass的结果,而最终又会依赖__subclasshook__特殊方法的返回值。

我们再将__subclasshook__特殊方法的返回值改成False,再观察运行结果。

from abc import abstractmethod, ABC

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, subclass):
        print('*' * 50)
        print('MyIterable.__subclasshook__ is invoked')
        print('cls is', cls, ', subclass is', subclass)
        return False

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class Bar(MyIterable):
    pass

print('-' * 25, '下面检验Foo类 分隔线', '-' * 25)

print('-' * 25, 'register(Foo)执行前 分隔线', '-' * 25)
MyIterable.register(Foo)  # 使用register函数将Foo类注册为MyIterable的虚拟子类

print('-' * 25, 'register(Foo)执行后 分隔线', '-' * 25)
print('issubclass(Foo, MyIterable):', issubclass(Foo, MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print('isinstance(Foo(), MyIterable):', isinstance(Foo(), MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Foo, 'get_iterator'):", hasattr(Foo, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Foo.mro():', Foo.mro())

print('-' * 25, '下面检验Bar类 分隔线', '-' * 25)

print('issubclass(Bar, MyIterable):', issubclass(Bar, MyIterable))
# print('isinstance(Bar(), MyIterable):', isinstance(Bar(), MyIterable))  # 子类未实现所有抽象方法,会报错
print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Bar, 'get_iterator'):", hasattr(Bar, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Bar.mro():', Bar.mro())

输出结果:

------------------------- 下面检验Foo类 分隔线 -------------------------
------------------------- register(Foo)执行前 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
------------------------- register(Foo)执行后 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
issubclass(Foo, MyIterable): False
------------------------- 分隔线 -------------------------
isinstance(Foo(), MyIterable): False
------------------------- 分隔线 -------------------------
hasattr(Foo, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Foo.mro(): [<class '__main__.Foo'>, <class 'object'>]
------------------------- 下面检验Bar类 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Bar'>
issubclass(Bar, MyIterable): False
------------------------- 分隔线 -------------------------
hasattr(Bar, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Bar.mro(): [<class '__main__.Bar'>, <class '__main__.MyIterable'>, <class 'abc.ABC'>, <class 'object'>]

再将__subclasshook__特殊方法的返回值改成True,再观察运行结果。

from abc import abstractmethod, ABC

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, subclass):
        print('*' * 50)
        print('MyIterable.__subclasshook__ is invoked')
        print('cls is', cls, ', subclass is', subclass)
        return True

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class Bar(MyIterable):
    pass

print('-' * 25, '下面检验Foo类 分隔线', '-' * 25)

print('-' * 25, 'register(Foo)执行前 分隔线', '-' * 25)
MyIterable.register(Foo)  # 使用register函数将Foo类注册为MyIterable的虚拟子类

print('-' * 25, 'register(Foo)执行后 分隔线', '-' * 25)
print('issubclass(Foo, MyIterable):', issubclass(Foo, MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print('isinstance(Foo(), MyIterable):', isinstance(Foo(), MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Foo, 'get_iterator'):", hasattr(Foo, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Foo.mro():', Foo.mro())

print('-' * 25, '下面检验Bar类 分隔线', '-' * 25)

print('issubclass(Bar, MyIterable):', issubclass(Bar, MyIterable))
# print('isinstance(Bar(), MyIterable):', isinstance(Bar(), MyIterable))  # 子类未实现所有抽象方法,会报错
print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Bar, 'get_iterator'):", hasattr(Bar, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Bar.mro():', Bar.mro())

输出结果:

------------------------- 下面检验Foo类 分隔线 -------------------------
------------------------- register(Foo)执行前 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
------------------------- register(Foo)执行后 分隔线 -------------------------
issubclass(Foo, MyIterable): True
------------------------- 分隔线 -------------------------
isinstance(Foo(), MyIterable): True
------------------------- 分隔线 -------------------------
hasattr(Foo, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Foo.mro(): [<class '__main__.Foo'>, <class 'object'>]
------------------------- 下面检验Bar类 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Bar'>
issubclass(Bar, MyIterable): True
------------------------- 分隔线 -------------------------
hasattr(Bar, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Bar.mro(): [<class '__main__.Bar'>, <class '__main__.MyIterable'>, <class 'abc.ABC'>, <class 'object'>]

尽管使__subclasshook__特殊方法的返回值为True和False时,最终结果符合预期,但中间打印日志的时候还是看上去不那么统一。 这种细节不必在意,因为其不影响最终判断结果,只能说明python内部实现的时候还是比较复杂的。
最后,我们再观察一下屏蔽掉register方法的情况。

from abc import abstractmethod, ABC

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, subclass):
        print('*' * 50)
        print('MyIterable.__subclasshook__ is invoked')
        print('cls is', cls, ', subclass is', subclass)
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in subclass.__mro__):
                print('MyIterable.__subclasshook__ 返回 True')
                return True
        print('MyIterable.__subclasshook__ 返回 NotImplemented')
        return NotImplemented

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class Bar(MyIterable):
    pass

print('-' * 25, '下面检验Foo类 分隔线', '-' * 25)

# print('-' * 25, 'register(Foo)执行前 分隔线', '-' * 25)
# MyIterable.register(Foo)  # 使用register函数将Foo类注册为MyIterable的虚拟子类
#
# print('-' * 25, 'register(Foo)执行后 分隔线', '-' * 25)
print('issubclass(Foo, MyIterable):', issubclass(Foo, MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print('isinstance(Foo(), MyIterable):', isinstance(Foo(), MyIterable))

print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Foo, 'get_iterator'):", hasattr(Foo, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Foo.mro():', Foo.mro())

print('-' * 25, '下面检验Bar类 分隔线', '-' * 25)

print('issubclass(Bar, MyIterable):', issubclass(Bar, MyIterable))
# print('isinstance(Bar(), MyIterable):', isinstance(Bar(), MyIterable))  # 子类未实现所有抽象方法,会报错
print('-' * 25, '分隔线', '-' * 25)
print("hasattr(Bar, 'get_iterator'):", hasattr(Bar, 'get_iterator'))

print('-' * 25, '分隔线', '-' * 25)
print('Bar.mro():', Bar.mro())

输出结果:

------------------------- 下面检验Foo类 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Foo'>
MyIterable.__subclasshook__ 返回 NotImplemented
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.Bar'> , subclass is <class '__main__.Foo'>
MyIterable.__subclasshook__ 返回 NotImplemented
issubclass(Foo, MyIterable): False
------------------------- 分隔线 -------------------------
isinstance(Foo(), MyIterable): False
------------------------- 分隔线 -------------------------
hasattr(Foo, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Foo.mro(): [<class '__main__.Foo'>, <class 'object'>]
------------------------- 下面检验Bar类 分隔线 -------------------------
**************************************************
MyIterable.__subclasshook__ is invoked
cls is <class '__main__.MyIterable'> , subclass is <class '__main__.Bar'>
MyIterable.__subclasshook__ 返回 True
issubclass(Bar, MyIterable): True
------------------------- 分隔线 -------------------------
hasattr(Bar, 'get_iterator'): True
------------------------- 分隔线 -------------------------
Bar.mro(): [<class '__main__.Bar'>, <class '__main__.MyIterable'>, <class 'abc.ABC'>, <class 'object'>]

可以看到,在没有register方法的情况下,__subclasshook__也被调用两次,用于判断子类之间是否存在虚拟的继承关系。

posted @   RolandHe  阅读(107)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示