Sequence 接口的实现不足以成为 Sequence

The implementation of Sequence interface is not sufficient to be a Sequence

提问人:Ali 提问时间:8/29/2023 更新时间:8/29/2023 访问量:34

问:

我注意到,在检查类是否是抽象类的实例时,鸭子类型在一定程度上效果相当好。

例如,要成为 的实例,我们只需要定义一个方法:collections.abc.Sized__len__()

import collections.abc

class MySized:
    def __len__(self):
        return 0

assert isinstance(MySized(), collections.abc.Sized)  # It is

同样,我们可以定义实现 、 、 等接口的类。ContainerIterableCollection

当我试图写自己的.Sequence

根据文档以及 cpython 中的代码,a 应该是 a , a ,并且另外实现方法 和 。SequenceCollectionReversible__getitem__()index()count()

但是鸭子打字是不够的:

import collections.abc

class MySequence:
    def __getitem__(self, i):
        return None

    def __len__(self):
        return 0

    def __iter__(self):
        return iter([])

    def __reversed__(self):
        return reversed([])

    def __contains__(self, x):
        return True

    def index(self, value, start=0, stop=None):
        return 0

    def count(self, value):
        return 0

assert isinstance(MySequence(), collections.abc.Collection)  # Ok
assert isinstance(MySequence(), collections.abc.Reversible)  # Ok
assert isinstance(MySequence(), collections.abc.Sequence)  # Fail! It's not

当然,如果我们从抽象基类显式继承一个类,它就可以工作:

import collections.abc

class MySequence(collections.abc.Sequence):
    def __getitem__(self, i):
        return None

    def __len__(self):
        return 0

assert isinstance(MySequence(), collections.abc.Sequence)  # It is

了解这种行为的原因以及接口实现的适用性限制会很有趣。也许我错过了其他一些未记录的方法?或者对于某些容器来说,它变得有必要从抽象的祖先显式继承?

python 集合 duck-typing abc

评论

1赞 jonrsharpe 8/29/2023
您是否阅读了文档中该表上的脚注 1?它显式指示不需要显式注册实现的 ABC。
0赞 Ali 8/29/2023
非常感谢,@jonrsharpe,我完全忽略了那个脚注。

答:

2赞 matszwecja 8/29/2023 #1

来自文档(强调我的):

接口的 or 测试在一个中工作 三种方式。issubclass()isinstance()

  1. 新编写的类可以直接从其中一个抽象继承 基类

  2. 现有类和内置类可以注册为“虚拟类” 子类”。

  3. 一些简单的界面可以直接识别 存在所需的方法(除非已设置这些方法) 更改为 )。复杂接口不支持最后一种技术,因为接口不仅仅是方法名称的存在。例如,知道一个类提供 __getitem____len____iter__ 不足以将 SequenceMapping 区分开来。None

如果没有显式继承,您将依赖于案例 3,该案例被明确声明不适用于序列。