The Open/Close principle and isinstance in python

by mandel on June 5th, 2009

I have to recognized, I nearly used isinstance in python several times, but after some careful consideration I redesigned my solution not to do it (which was actually throwing away quite a lot of work). After doing so I have decided to find what people say about the use of isinstance and why or why not they consider it harmful.

Kragen Javier Sitaker’s opinion

After some googling I found the following article at his site. Although I do agree in the different uses that people do of isinstance I do not agree in his reason of why is harmful:

“In Python, what classes your object is derived from is not a part of your object’s interface.”

Every use of isinstance is a violation of this promise, large or small. 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!

Although I do agree with the comment that isinstance forks the code I do not agree in the fact that python uses implicit interfaces and that doing so brakes the promise. From my point of view Python does not have interfaces, which is an idiom of other languages to allow polymorphism, because it support multiple inheritance. On the other hand when working with python we should be focusing in “fulfill the contract” rather than in implementing interfaces which is garbage from Java.

Dobes Vandermeer’s opinion

I also found Dobes’s opinion after gooling, and his conclusion is the following:

“So, implicit interfaces have their place, and so does isinstance(), use them both as needed!”

Well, I do not agree with this at all. I already mentioned that I do not believe that Python uses implicit interfaces but contracts. I also disagree with the use of isinstance because it brakes the Open/Close principle which from my point of implies that you design is bad.

My Opinion

In my opinion the main problem with isinstance if the fact that it brakes the Open/Close principle as well as the Liskov Substitution Principle. Lets imagine we have the following code:

class A:pass
class B(A):pass
class C(A):pass
 
def write(self, value):
    assert(isinstance(A))
    if isinstance(value, B):
        doB()
    elif isinstance(value, C):
        doC()
    else:
        raise Exception("")

As Javier mentions in this blog, isinstance does fork the code, but that from my point of view is not as bas as breaking the Open/Close principle and the Liskov Subsitution one. You to remind what they are we can define:
Open/Close principle

“software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”

Liskov Substitution Principle

“If for each object o of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o is substituted for o2 then S is a subtype of T.”

Why does it brake the Open/Close principle?
Using the code I posted already, if we wanted to add a new functionality in our code we would have to modify it because the function needs to about every possible derivative.
Why does it brake Liskov Substitution Principle?
In the code example, if we change all object B for object C the behaviour of the code will change.

Conclusion

Yes, I do agree that isinstance is dangerous, but not because of the implicit interface pattern or the fact that people do not like, it is because it brakes two very important principle of OO design. Does anyone know about other reason or other opinions?

From Design, OO, Python

Leave a Reply

You must be logged in to post a comment.