There’s Something About Code

May 15, 2009

When True is not None

Filed under: Code — Tags: , , — Knut Eldhuset @ 14:16

Sometimes, when you try to explain something using an example, people will pick on the details of your example instead of seeing the greater picture. Scott Hanselman got some flack for daring to write something == true. Whether or not he was justified in writing his code that way, there certainly are good reasons to check for explicit truth values, especially in Python.

When using keyword arguments with a default value of None, it’s easy to check if the argument was supplied or not:

def do_expensive_thing(times):
    for n in range(times + 2):
        print "Index: ", n
 
def do_it(argument_for_expensive_operation=None):
    if argument_for_expensive_operation:
        do_expensive_thing(argument_for_expensive_operation)
    print "It's done"

Let’s try it out with some different parameters:

In [3]: do_it()
It's done

In [4]: do_it(2)
Index:  0
Index:  1
Index:  2
Index:  3
It's done

Works nicely. Let’s try a third time:

In [5]: do_it(0)
It's done

Whooah! What happened this time? Conveniently, not only None gives a truth value of False. Other examples are the integer value zero and the empty list. This explains why the above example fails. The correct way is to explicitly check if the supplied argument is None:

def do_it_right(argument_for_expensive_operation=None):
    if argument_for_expensive_operation is not None:
        do_expensive_thing(argument_for_expensive_operation)
    print "It's done"

Now it prints what we would expect:

In [7]: do_it_right(0)
Index:  0
Index:  1
It's done

The Python documentation covers truth value testing in detail.

Swallowed by a Python

Filed under: Code — Tags: , , — Knut Eldhuset @ 13:57

Programming Python is mostly straightforward. Not thinking, however, can lead to subtle bugs, and this one had me stumped for a while. If a program raises an exception that is not caught further up the call stack, I expect the interpreter to exit with a whimper. Consider the following code:

def throwing_function():
    try:
        print 1 / 0
    finally:
        print "Finally"
        return "Useful string"
 
print throwing_function()

Running this will print

Finally
Useful string

If you can spot the bug immediately, good for you, but I didn’t. I expected this code to fail with a ZeroDivisionError exception. Not so. It turns out that the return statement hides the exception.

The correct code should look like this:

def throwing_function():
    try:
        print 1 / 0
    finally:
        print "Finally"
    return "Useful string"
 
print throwing_function()

Now it behaves like expected:

Finally
Traceback (most recent call last):
  File "swallowed_exception_fixed.py", line 8, in 
    print throwing_function()
  File "swallowed_exception_fixed.py", line 3, in throwing_function
    print 1 / 0
ZeroDivisionError: integer division or modulo by zero

A good rule of thumb would be to never put a return statement inside a finally clause.

Powered by WordPress