Search code examples
pythonabc

Do I need isinstance checks when using ABCs?


Here is an example of an interface as seen in the "Fluent Python" book:

from abc import ABCMeta, abstractmethod

class IStream(metaclass=ABCMeta):
    @abstractmethod
    def read(self, maxbytes=-1):
        pass

    @abstractmethod
    def write(self, data):
        pass

A bit further in the book, one can read that "code that explicitly checks for this interface could be written as follows":

def serialize(obj, stream):
    if not isinstance(stream, IStream):
        raise TypeError('Expected an IStream")

My question is: why would I need such isinstance checks? Is it for cases when stream might be an instance of a class that does not inherit from IStream?

Otherwise, my understanding is that it should not be needed, because if the instance that gets passed as stream would not satisfy the IStream interface (by inheriting from the ABC), it would not be allowed to instantiate before it even gets passed:

class Stream(IStream):
  def read(self):
    pass

  def WRITE(self):
    pass

stream = Stream()

Traceback (most recent call last): File "c:\python projects\test.py", line 18, in stream = Stream() TypeError: Can't instantiate abstract class Stream with abstract method write


Solution

  • The metaclass restricts what you're allowed to do / forces you to do certain things when developing a class that implements it.

    However, a method like serialize is a method of a class itself, taking an instance as a parameter - someone using your class is in no way forced to pass it an object of the correct class.

    An editor or IDE might be able to tell them that they are passing an object of a class that's not expected - even more so when you add clear type hints, but even then it wouldn't be flat out disallowed.

    You either need to assume it's used correctly and trust your code to fail with an appropriate exception when your method tries to do something with the passed instance argument that it doesn't support (in the spirit of duck typing), or check for it being of the correct class explicitly with isinstance.

    As a rule, I only check using isinstance, if the code I wrote can have potentially disastrous effects simply from having an object with the wrong type passed to it - so pretty rarely. In most cases, type hints are enough.