1. Overview: Python's exception handling mechanism is very similar to Java's exception handling mechanism. It has a try...except format that looks as follows:
    try:
        protected code
    except ExceptionName1 as e1:
        error handling code
    except ExceptionName2 as e2:
        error handling code
    except ExceptionName3:
        error handling code
    except:
        unconditional error handling code
        raise   # re-raises the exception
    else:
        code to execute if the try completes successfully. 
        This code is non-protected code (i.e., code we do not
        expect to cause an exception)
    finally: 
        code executed regardless of whether an exception occurs, and
        regardless of whether an exception is handled if one occurs
    
    Here is an example from the Python tutorial:
    import sys
    
    try:
        f = open('myfile.txt')
        s = f.readline()
        i = int(s.strip())
    except IOError as e:
        print ("I/O error({0}): {1}".format(e.errno, e.strerror))
    except ValueError:
        print ("Could not convert data to an integer.")
    except:
        print ("Unexpected error:", sys.exc_info()[0])
        raise
    
    The try mechanism operates as follows:

    It is permissable to write user-defined exceptions, which we will consider in the section on user exceptions.

  2. What to put in try and what to put in else

    1. You should put code that you suspect might cause an exception in the try clause.
    2. You should put code that should conclude the try statement and that should not cause an exception in the else clause. Thus if an unexpected error occurs, the exception most likely will be thrown to an outer try statement, rather than being handled by the current try statement's except clauses.

  3. The except clause

    1. An except clause may contain a comma-separated, parenthesized list of exceptions:
      except (RuntimeError, TypeError, NameError):
      
      Make sure that the list is parenthesized. Old-style Python allowed you to bind a variable to an exception object using the syntax:
      except Exception, variable
      
      so if you omit the parentheses in the above statement, Python will try to bind the variable named TypeError to the exception object for RuntimeError, which is not what you intend.

    2. You can bind a variable to the exception object using the syntax:
      except Exception as variable:
      
      For example:
      except IOError as e:
      
      e.args will contain a list of the arguments passed to the exception object.

    3. The first exception matched in the except list is the clause that is executed. The remaining clauses will be ignored.

      1. Exceptions should be listed from more specific to more general. Otherwise the general exception will mask the specific exception. For example:
        except Exception:
           ...
        except IOError:
           ...
        
        The "except IOError" will never get executed because an exception will always match the first clause.

      2. If an exception occurs in an except clause, it is immediately passed on to an outer try handler. However the current try's finally method is executed first.

  4. Raising Exceptions
    1. The raise statement raises an exception. Usually it would be used to raise a user-defined exception by creating an instance of a user-defined class:
      raise NameError('HiThere')
      
    2. You can re-raise an exception by simply typing:
      raise
      
      in an except clause. It will then re-raise the exception by passing the exception to an outer try clause. Obviously this should be the last statement in your except clause.
  5. User Exceptions
    1. User exceptions are created by subclassing the pre-defined Python Exception class:
      class NameError(Exception):
        pass
      
    2. Any arguments you pass to the constructor for a user-defined exception will be automatically stored in an attribute named args. For example:
      try:
        raise NameError('Hi There', 5)
      except NameError as e:
        for v in e.args:
          print (v)
      
      produces the output:
      Hi There
      5
      
    3. You can also define a constructor that will store the arguments in specific attributes. In this case an except clause must access the arguments through the specific attributes rather than the args attribute. The args attribute disappears since it was created by the default constructor, which you have now overridden.
      class NameError(Exception):
        def __init__(self, name, value):
          self.name = name
          self.value = value
      
      try:
        raise NameError('Hi There', 5)
      except NameError as e:
        print ('NameError:', e.name, "and error code =", e.value)
      
    4. Common Bug: Don't forget to make your exception subclass Exception. If you write:
      class NameError:
        pass
      
      try:
        raise NameError('Hi There', 5)
      except NameError as e:
        for v in e.args:
          print (v)
      
      then you will get the error message:
      TypeError: this constructor takes no arguments
      
      The reason is that NameError is no longer a subclass of Exception and hence there is no default constructor that converts the constructor's arguments to a list that is placed in the args attribute.