[Tutor] am I missing another simpler structure?

Jeff Shannon jeff at ccvcorp.com
Thu Dec 16 21:07:54 CET 2004


Blake Winton wrote:

>>>> def is_leap_year(year):
>>>>     is_leap = True
>>>>     try:
>>>>         datetime.date(year, 2, 29)
>>>>     except ValueError:
>>>>         is_leap = False
>>>>     return is_leap
>>>
>>>
>>> I would write
>>> def is_leap_year(year):
>>>     try:
>>>         datetime.date(year, 2, 29)
>>>         return True
>>>     except ValueError:
>>>         return False
>>>

If one insists on single-return style, then I'd prefer to do it this way:

def is_leap_year(year):
     try:
         datetime.date(year, 2, 29)
         is_leap = True
     except ValueError:
         is_leap = False
     return is_leap

You still have to type 'is_leap' multiple times, but at least it's a 
little bit more clear which conditions will result in it being True or 
False.

> [...]  Only having one
> return point from a function is a long-standing convention that is 
> supposed to make programs easier to read/debug/optimize/prove correct.

Indeed, it is supposed (by some) to be better, and is thought by 
others to sometimes make things more complicated.  In the above code, 
a single return point gives you one place to set breakpoints during 
debugging, etc, but it also means that you introduce a new variable 
that you must type at least three times, introducing multiple 
opportunities for typos.  As with most programming choices, 
single-return has both a benefit and a cost.  In some situations the 
cost can be very large relative to the benefit; in others the cost is 
very small.  I find it best not to be too dogmatic about most such 
stylistic questions -- I try to stay aware of the costs of a 
particular approach, but I'll happily use it when the net effect is to 
make the code simpler and more understandable.

Similar to the example that Kent later points out, in my work life I 
often find myself writing functions (to use as the body of a loop) 
where a number of unrelated tests must be done before proceeding with 
processing.  (This is actually in an old, crufty dialect of Basic, but 
the same principle applies)  I have a few choices in this case.  I can 
  simply write the loop body in-line and use gotos to skip segments if 
the tests fail (ew).  I can write the loop body in-line and use 
multiply nested if/else statements, but it can get difficult to track 
what nesting level I'm at.  I can use nested functions, each with just 
one test in it, something like this:

def func1(data):
     result = None
     if test1:
         result = func2(data)
     return result

def func2(data):
     result = None
     if test2:
         result = func3(data)
     return result

def func3(data):
     [...]

This gets pretty darn ugly to trace through, especially when I've got 
six or eight different tests.  Or, I can simply use multiple returns:

def func(data)
     if not test1:
         return
     if not test2:
         return
     if not test3:
         return
     # do stuff with data...

It really does make it much simpler to track through the code, since I 
don't care, later in the function, about the specific results of each 
test, only about whether I should continue afterwards or not.  In this 
case, I'm weighing the cost of multiple returns against the cost of 
multiple nestings or of using flag variables, and I find multiple 
returns to be the lowest-cost option.

Jeff Shannon
Technician/Programmer
Credit International




More information about the Tutor mailing list