Yes, that headline was written for dramatic effect. No, I don’t think that
None itself is evil. Yes, I realize there are many cases where
None is an appropriate return value. No, this post isn’t about those cases.
This is mostly a rant about Java, but the pitfall described below is definitely a potential problem in Python. I hope none of my fellow Djangonauts have fallen into this trap, but hopefully this post will help make sure it stays that way.
I work in Java during the day, and every day I run into the same error message, due to a popular Java philosophy: if a retrieval function can’t retrieve an appropriate object, it should return
null. I don’t have any sources that support this being any kind of official stance by the Java folks, but I’ve seen it in plenty of Java code. This is the single greatest cause of the infamous NullPointerException. For reference, the Python equivalent of this typically looks like this (though there are variations):
AttributeError: 'NoneType' object has no attribute 'xxx'
Basically, you have some code that retrieves some data. It might get it from a database, a file, or some series of operations based on input data. Where it comes from doesn’t matter. What matters is what you do if you couldn’t get a proper value. In Java, code often returns
null, which is allowed for most function definitions. This means that, even though something unexpected happened (data couldn’t be retrieved), your program continues merrily along without knowing about it.
That is, of course, until you try to use your shiny, newly-retrieved object. Java then falls over itself and dies a horrifically painful death upon realizing that you dared try to do anything with
null. This is the
NullPointerException, and it’s exceptionally (pun intended) difficult to track down. You see, the traceback only tells you where your code tried to reference some property or method of
null. What you really want to find out is where did that null come from?
This generally requires firing up the code in a debug mode, where you can step through individual lines of code, inspecting what your mystery value contains at different points of execution. Then, once you finally find out where
null is being returned, you have to figure out why a proper object couldn’t be returned, and fix that problem (which was left out of your traceback entirely).
Python, thankfully, takes a different approach. It’s so ingrained in Python philosophy that Tim Peters mentioned it in the 10th line of the Zen of Python:
Errors should never pass silently.
Essentially, that’s what happened: an error occurred. But the Java mindset is to ignore the real error and continue on anyway. I don’t know, maybe they feel that errors should only show up for catastrophic system failures, not normal every-day problems like an record not existing in a database. Even with that logic, why a
NullPointerException in the wrong place is preferable to a
RecordNotFoundError in the right place is beyond me.
If you’re using Python (which I hope you are), embrace exceptions. Raise them (or let them pass on their own, if they’re raised by code outside your control) whenever your code can’t do what it’s supposed to do.
I hope I’ve made the point well enough without writing a song called “Raise an Exception”. Whatever you do, don’t return
None represents some usefulness to your code. This isn’t usually the case, so you should generally avoid it unless you know you need it.
Disclaimer: In Python, functions that don’t explicitly return anything, automatically return
None. This is fine, because external code won’t be storing that return value anyway, as long as you’ve made it clear that its return value is irrelevant.