Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

I’ve noticed that duck typing works reasonably well up to a certain point in checking whether a class is an instance of an abstract class.

For example, to be an instance of collections.abc.Sized, we only need to define a __len__() method:

import collections.abc

class MySized:
    def __len__(self):
        return 0

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

In the same way, we can define classes that implement interfaces of Container, Iterable, Collection, and so on.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Failure befell me when I tried to write my own Sequence.

According to the documentation, as well as the code at cpython, a Sequence should be a Collection, a Reversible, and additionally implement methods __getitem__(), index() and count().

But duck typing is not enough:

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

Of course, if we explicitly inherit a class from an abstract base class, it works:

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

It would be interesting to understand the reasons for this behaviour and the limits of applicability of the interface implementation only. Perhaps I missed some other undocumented method? Or for some containers it’s becoming necessary to explicitly inherit from an abstract ancestor?

>Solution :

From the docs (emphasis mine):

An issubclass() or isinstance() test for an interface works in one
of three ways.

  1. A newly written class can inherit directly from one of the abstract
    base classes

  2. Existing classes and built-in classes can be registered as “virtual
    subclasses” of the ABCs.

  3. Some simple interfaces are directly recognizable by the
    presence of the required methods (unless those methods have been set
    to None). Complex interfaces do not support this last technique
    because an interface is more than just the presence of method names.
    For example, knowing that a class supplies __getitem__,
    __len__, and __iter__ is insufficient for distinguishing a
    Sequence from a Mapping.

Without explicit inheritance you are relying on case 3, which is explicitly stated to not work for Sequences.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading