Sunday, August 08, 2010

Python vs. C# - Exceptions

I briefly mentioned in an earlier post that I had left Cogi to start working for The Trade Desk. One of the many changes that this entailed was to stop working in Python, my preferred programing language, and start programming in C#. Both languages are quite popular, and each have their own merits: Python's "batteries included" standard library and programmer-centric philosophy allow a programmer to focus on producing software, while C#'s .NET-backed runtime and Microsoft integration give it a great backbone for software development.

As I've spent a good deal of time programming in both languages, it seems like it might be interesting to compare some aspects of the two languages. To avoid overwhelming either myself or my readers, I'll limit the discussion to one feature at a time. Assuming that anyone finds the articles interesting or informative, I will periodically post comparisons between the two languages.

Today's feature: Exceptions.

First off, I'm going to assume that it's a given that exception handling is a Good Thing. The only major alternative that I'm aware of is a combination of return codes and segmentation faults (for the lay-reader: crashes). Return codes mean you have to check the result of any potentially failing method call, making your code focus more on what can go wrong than on what should go right. This leads to fragmented code which leads to more bugs. Exception frameworks allow us to focus on what should happen, and deal with the consequences in alternative blocks of code higher up the stack where we can make more logical decisions. Both Python and C# fall into the exception-oriented camp (for the most part).

Both languages treat exceptions as first class objects and provide a good try/catch/finally mechanism that allows a developer to handle and deal with exceptions of both specific and general types:

def TestMethod():
        print "Oops!"

public void TestMethod()
    catch ( Exception )
        Console.WriteLine( "Oops!" );

Both Python (as of 3.0) and C# also support the idea of a "nested" or "inner" exception. This is a critical component of robust exception handling, as it can be used to ensure that an exception can always be traced back to its source. In my previous jobs, working with both C++ and earlier versions of Python, we implemented and maintained extensive libraries of code just to provide this critical debugging feature.

One area where Python and C# significantly differ from one another is in the detail included in exceptions. In particular, I'm referring to the built-in exceptions in the language libraries, since the language obviously has no control over how a developer chooses to write their code. Consider the following algorithm:

  • Create a dictionary of key-value pairs.
  • Populate the dictionary with some well known or required data.
    • Let's say the keys we're trying to put in are "a", "b", and "c".
  • Get input from somewhere.
    • Let's say we get "c".
  • Use the input to attempt to retrieve a key from the dictionary.
    • The key doesn't exist, so an exception is raised.

"Ok", you think, "that's no big deal. I just have to figure out which key it was that was looked up and figure out why it didn't exist in the dictionary." In Python, you would have no problem, as the exception tells you what you were looking for:

    Traceback (most recent call last):
      File "", line 1, in
    KeyError: 'c'

Given that info, we know that something was wrong with the code populating the dictionary, since the expected key "c" didn't exist. If that KeyError had indicated that "x" was looked up, we would know that the input we were given was bad, since we didn't expect "x" to exist. From there, it's just a matter of tracing down the code that caused the problem.

On the other hand, in C# all you're given is that the data wasn't found:

    Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at ExceptionExample.Program.Main(String[] args) in     C:\Users\chris\AppData\Local\Temporary Projects\ExceptionExample\Program.cs:line 13

Well, what do we do with that? At this point, we have to start debugging manually -- either by adding some print statements or by setting a breakpoint at the indicated line. Remember that in Python, we already had a starting place -- we knew in which direction we had to look for the bug, and possibly could have solved the problem without ever starting a debugger. In C#, we can't even begin to investigate the problem without a debugger because we weren't given enough information to start with -- information that was obviously available at the time the exception was thrown!

And it's not just KeyNotFoundException that has this problem; it's nearly every exception I've seen thrown by .NET that could contain information about the problem that caused it. Another example is the dreaded NullReferenceException. Where C# forces you to drop into the code and hope the line numbers line up (and let's hope you're not trying to use reflection -- but that's a Python vs. C# topic all on its own!), Python handles its equivalent beautifully:

    >>> a = None
    >>> a.hi
    Traceback (most recent call last):
      File "", line 1, in
    AttributeError: 'NoneType' object has no attribute 'hi'

Winner: Python.

No comments:

Post a Comment