A side effect of this question is that I was lead to this post, which states:
Whenever isinstance is used, control flow forks; one type of object goes down one code path, and other types of object go down the other — even if they implement the same interface!
and suggests that this is a bad thing.
However, I’ve used code like this before, in what I thought was an OO way. Something like the following:
class MyTime(object):
def __init__(self, h=0, m=0, s=0):
self.h = 0
self.m = 0
self.s = 0
def __iadd__(self, other):
if isinstance(other, MyTime):
self.h += other.h
self.m += other.m
self.s += other.s
elif isinstance(other, int):
self.h += other/3600
other %= 3600
self.m += other/60
other %= 60
self.s += other
else:
raise TypeError('Addition not supported for ' + type(other).__name__)
So my question:
Is this use of isinstance “pythonic” and “good” OOP?
Not in general. An object’s interface should define its behavior. In your example above, it would be better if
otherused a consistent interface:Even though this looks like it is less functional, conceptually it is much cleaner. Now you leave it to the language to throw an exception if
otherdoes not match the interface. You can solve the problem of addinginttimes by – for example – creating aMyTime“constructor” using the integer’s “interface”. This keeps the code cleaner and leaves fewer surprises for the next guy.Others may disagree, but I feel there may be a place for
isinstanceif you are using reflection in special cases such as when implementing a plugin architecture.