Python isinstance considered useful

I seem to occasionally bump into this idea in Python that you shouldn’t use “isinstance” to check the type of something.  You should instead infer what type of object you have by checking for the presence or absence of particular methods.  Every time I do, I just completely fail to agree with the arguments against isinstance().

The argument is that these “implicit interfaces” provide more flexbility to whoever calls into your object.  The argument is that if two objects happen to implement methods with the same “meaning”, that because these objects are theoretically interchangeable they should really be interchangeable.  The classic example of this is python’s famous “file-like objects”, which typically implement read and/or write in the same way and are accepted by various python functions.  I believe the DB API is another well-used example of this.  In both cases, there is no common base-class, so it’s impossible to use isinstance() to check whether a particular object is, in fact, file-like or a database object.

The majority of the good cases for “implicit interfaces” are relying on a particular context where the nature of the object being given is completely clear – the function is documented as taking one type or another, and generally if it accepts more than one type, the possible “types” of objects are unlikely to collide.  In the case of file-like objects, you’re often given the option of passing a string, which is then treated as a URL or a filename and opened for you as a convenience.

I think that implicit interfaces would benefit from being replaced with explicit interfaces.  Python doesn’t have great support for these, but you can declare a class with all empty methods and use that as the interface.  Python allows multiple inheritance, so you can implement all the interfaces your object supports by making them superclasses.  Ideally python would include an interface called FileLike and an interface DatabaseLike and we’d implement that in our file-like and database-like objects, and python would provide a way to verify (in the test suite, I suppose) that all interfaces are fully implemented.  That way when a new version of DatabaseLike comes out, we’ll get a notification that the database class needs to be updated.

There are two cases where isinstance() might fall down, but testing for attributes wouldn’t.  First, when you use reload() to reload a module, the interfaces might be re-created, and your isinstance() tests would incorrectly fail.  However, I suggest that this indicates a problem with the use of reload() – really, if you use reload(), you should make sure you’ve reloaded the entire class hierarchy.  Second, if you have some kind of object proxy, for RPC or weak references, it might not handle isinstance() as expected.

When designing an API, the majority of the functions’ parameters will expect only one type of argument, and if you like the idea of flexibility, just don’t assert against the parameters at all – let the app fail when it discovers a needed operation is missing.  That’ll allow callers to use subclassing or not, as they choose.  However, when you need to take a different path based on the type of object you’ve been given, you’ll have to decide whether to use implicit interfaces, which you check for by checking for certain attributes, or explicit interfaces which you check for using isinstance().  I think that for an interface with relatively few methods and which is likely to be replaced by the callers not only with their own implementation, but possibly with other objects they didn’t write but “luckily” have the right methods, then an implicit interface is a good thing.  If your interface is more complex or you know there aren’t any other objects which have compatible methods, you can create an explicit interface without method bodies, and ask the user to provide an appropriate subclass of that interface.  Finally, if you know that you’re only passing subclasses of a certain class and have no use for any other type of implementation, just pass those subclasses – there’s a little thing called “keep it simple” and this is probably great most of the time.

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

Advertisements

6 Responses to Python isinstance considered useful

  1. paddy3118 says:

    Maybe some of your problem with duck typing as it is called is reflected in your first paragraph:

    ” You should instead infer what type of object you have by checking for the presence or absence of particular methods”.

    That isn’t quite right. You should just go ahead and use whatever object you have assuming it is the correct object and trust Pythons run-time type checking and your programming team to “do the right thing”. It isn’t that explicit interface checking isn’t needed in Python – its just not the only way, or necessarily the first way to try when programming Python.

    – Paddy.

    Ref: http://en.wikipedia.org/wiki/Duck_typing

  2. dobes says:

    I am aware of that – in the beginning of the last paragraph I wrote “When designing an API, the majority of the functions’ parameters will expect only one type of argument, and if you like the idea of flexibility, just don’t assert against the parameters at all – let the app fail when it discovers a needed operation is missing. That’ll allow callers to use subclassing or not, as they choose.”

    The main point of the article is not to decry duck typing, but instead to defend isinstance() as something which is not harmful, but useful.

    I’ve heard the argument that checking the types of things shows a problem with the design of your code, and you should never use hasattr or isinstance. Maybe so – but most of the suggested solutions wouldn’t work for objects I didn’t create, or require a lot of extra unnecessary code.

    Recently I’ve been introduced into PyProtocols, which seems to provide an elegant solution to the problems with both interfaces (hard to adapt other people’s object to an interface) and duck typing (fragility in the face of changes).

  3. […] also found Dobes’s opinion after gooling, and his conclusion is the following: “So, implicit interfaces have their […]

  4. John Fries says:

    My current python programming practice agrees with this post. I use isinstance and hasattr all the time in my code, especially inside assert statements.

    I buy the theoretical argument for using hasattr instead of isinstance, but it’s not as practical when you are using multiple methods from the same class. Ideally, you could simply define an interface as “anything that implements these methods”, and Python would infer that the object supports that interface from the presence of those methods.

    Thanks for the pointer to PyProtocols, I’ll check it out.

  5. Tim says:

    Very good post title. I hate those “xxxx considered harmful” posts that populate the top of a search.

  6. bernard says:

    Personally I hate having to write boilerplate code. Explicit interfaces are just that: useless boilerplate code.

    Don’t try and drag concepts from languages like Java or C++ into your Python code, they are absent for a reason.

    isinstance() does make sense in certain, albeit very limited circumstances, e.g. in unittests.

    There’s good summary here http://stackoverflow.com/questions/1549801/differences-between-isinstance-and-type-in-python

%d bloggers like this: