why does isinstance check for abc.Sequence return False for custom classes?

Viewed 352

I've read in the abc python module docs that a Sequence is something that implements the following: __getitem__, __len__, __contains__, __iter__, __reversed__, index, and count.

Yet, when I run the following example it yields false:

from collections import abc

class Sequence():
    def __getitem__(self):
        pass
    def __len__(self):
        pass
    def index(self):
        pass
    def count(self):
        pass
    def __contains__(self):
        pass
    def __iter__(self):
        pass
    def __reversed__(self):
        pass

print(isinstance(Sequence(), abc.Sequence)) # False

When I was doing similar stuff for abc.Collection or abc.Reversed to play around I got the results I expected, for example I created a dummy class that implements __contains__, __iter__, __len__ and it was in fact detected correctly as an instance of abc.Collection.

Do you have any idea what's wrong with Sequence?

EDIT 1:

from collections import abc
class CustomIterable:
  def __iter__(self):
    pass
print(isinstance(CustomIterable(), abc.Iterable)) # True

even though my custom iterable is not from abc it still is recognized as Iterable because it implements __iter__ special method.

2 Answers

I had a similar question today.

From what I can gather, collections.abc.Iterable implements a custom __subclasshook__() method, whereas collections.abc.Sequence does not:

# _collections_abc.py

class Iterable(metaclass=ABCMeta):

    __slots__ = ()

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

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            return _check_methods(C, "__iter__") # True if __iter__ implemented
        return NotImplemented

What this means is that if a class Foo defines the required __iter__ method then isinstance(Foo(), collections.abc.Iterable) will return True:

from collections.abc import Iterable

class Foo:
    def __iter__(self):
        return []

assert isinstance(Foo(), Iterable)

I'm not sure why collections.abc.Iterable implements a custom __subclasshook__() method but collections.abc.Sequence does not.

Your custom Sequence class is different from the abc.Sequence class so isinstance will return false.

If you're looking for True, your custom class needs to inherit from abc.Sequence:

class Sequence(abc.Sequence):
   .....
Related