views:

165

answers:

4

I have a basic "best practices" Python question. I see that there are already StackOverflow answers tangentially related to this question but they're mired in complicated examples or involve multiple factors.

Given this code:

#!/usr/bin/python

def test_function():
   try:
      a = str(5)
      raise
      b = str(6)
   except:
      print b

test_function()

what is the best way to avoid the inevitable "UnboundLocalError: local variable 'b' referenced before assignment" that I'm going to get in the exception handler?

Does python have an elegant way to handle this? If not, what about an inelegant way? In a complicated function I'd prefer to avoid testing the existence of every local variable before I, for example, printed debug information about them.

+1  A: 
def test_function():
   try:
      a = str(5)
      raise
      b = str(6)
   except:
      print b

b = str(6) is never run; the program exits try block just after raise. If you want to print some variable in the except block, evaluate it before raising an exception and put them into the exception you throw.

class MyException(Exception):
   def __init__(self, var):
      self.var = var

def test_function():
   try:
      a = str(5)
      b = str(6)
      raise MyException(b)
   except MyException,e:
      print e.var
jetxee
Never raise `Exception`. Like in this case, it is never sane to catch it. This would catch a `TypeError` if you'd shadowed `str` with something noncallable above and a `MemoryError` if you ran out of memory and couldn't create these objects and plenty of other things you cannot account for.
Mike Graham
I agree and edited the example.
jetxee
+1  A: 

You can initialize your variables outside of the try block

a = None
b = None       
try:
    a = str(5)
    raise
    b = str(6)
except:
    print b
Ivan
+3  A: 

Does python have an elegant way to handle this?

To avoid exceptions from printing unbound names, the most elegant way is not to print them; the second most elegant is to ensure the names do get bound, e.g. by binding them at the start of the function (the placeholder None is popular for this purpose).

If not, what about an inelegant way?

try: print 'b is', b
except NameError: print 'b is not bound'

In a complicated function I'd prefer to avoid testing the existence of every local variable before I, for example, printed debug information about them

Keeping your functions simple (i.e., not complicated) is highly recommended, too. As Hoare wrote 30 years ago (in his Turing acceptance lecture "The Emperor's old clothes", reprinted e.g. in this PDF):

There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.

Achieving and maintaining simplicity is indeed difficult: given that you have to implement a certain total functionality X, it's the most natural temptation in the world to do so via complicated accretion into a few complicated classes and functions of sundry bits and pieces, "clever" hacks, copy-and-paste-and-edit-a-bit episodes of "drive-by coding", etc, etc.

However, it's a worthwhile effort to strive instead to keep your functions "so simple that there are obviously no deficiencies". If a function's hard to completely unit-test, it's too complicated: break it up (i.e., refactor it) into its natural components, even though it will take work to unearth them. (That's actually one of the way in which a strong focus on unit testing helps code quality: by spurring you relentlessly to keep all the code perfectly testable, it's at the same time spurring you to make it simple in its structure).

Alex Martelli
A: 

You could check to see if the variable is defined in local scope using the built-in method locals()

http://docs.python.org/library/functions.html#locals

#!/usr/bin/python

def test_function():
  try:
      a = str(5)
      raise
      b = str(6)
  except:
      if 'b' in locals(): print b

test_function()
adam