It has happened before, and it was happening again. I had a weird problem, coded up an "interesting" solution, and then tried to find out why I felt strange about it.
In this case, I was writing a test function in a Django project to loop over a set of Tastypie api endpoints and make sure each endpoint responded without any errors. To allow this test to grow with the file describing the api endpoints and to follow basic DRY principles I decided to import that file, find all the classes of a given type find the attribute I needed to build up the url of the corresponding api endpoint, and use the test client to render that url and check for errors.
My first attempt looked like this:
It may work, but it's not pretty. I'm explicitly checking whether each object I'm looking for is of the correct class, then I'm checking for an attribute, and after that I'm still having to wrap everything in an Exception to handle the case where the first argument to "issubclass" is not, in fact, a class.
So, with the smell of bad code wafting through the air, I went off in search of better solutions. In this search I started in the place where all good programmer start: StackOverflow.
I started with this question about checking if a class is a subclass of another class. At first I dismissed the comment about "python is not java" as a preachy comments on dynamic languages, but that got me thinking: I've heard the same people advocating duck typing and at the same time saying not to use exceptions to control flow. I hadn't really put these 2 together solidly before, but now that I have this problem to work on, I see that these are quite related, and you can't have it both ways. Either you type check, or you handle the unexpected with exceptions.
Coming from a HPC world, I instinctively avoided exceptions because they are usually though of as slow. But after reading this very well phrased question on the topic of asking permission vs asking forgiveness (i.e. type checking vs. exception handling), my mind was starting to change. In python the performance penalty is not so large that Exceptions need only be used for "exeptional" circumstances. After all, iterators are controlled with the StopIteration Exception, and that's part of the core language.
My mind was made after reading a third great question and response explicitly addressing duck typing. The top answer recommends using Exceptions whenever possible as long as nothing really strange is going to happen if you try an operation on the wrong type of object. Most of the time it's better to just document your functions well, let your users decide if they want to use it, and raise and Exception up to them if something went wrong.
So after all this, I went back and tried to put back together my simple function using what I learned. I think the second take is more readable and probably just as fast as the original (the running of the test client for each url is definitely the bottleneck here anyway). The substitution of the getmembers function of the Inspect module helps too.
If you were running into a similar problem with smelly code or preachy internet pythonistas, I hope this helps clear things up.
P.S. - The code snippets were embedded by adding the following snippet directly in the body of the post using the HTML editor feature of Blogger:
<script src="https://gist.github.com/[username]/[gistID]/[gistVersion].js"></script>
No comments:
Post a Comment