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
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.