Tim
Tim

Reputation: 99526

Equivalence to a try / except / finally statement

From Python in a Nutshell

A try / except / finally statement, such as:

try:
    ...guarded clause...
except ...expression...:
    ...exception handler code...
finally:
    ...clean-up code...

is equivalent to the nested statement:

try:
    try:
        ...guarded clause...
    except ...expression...:
        ...exception handler code...
finally:
    ...clean-up code...
  1. Why is it equivalent to a nested form?
  2. Can it be written equivalent to a form without finally?

    Is it equivalent to

    try:
        ...guarded clause...
    except ...expression...:
        ...exception handler code...
        ...clean-up code...
    ...clean-up code...
    

Thanks.

Upvotes: 1

Views: 1710

Answers (3)

JohnMudd
JohnMudd

Reputation: 13823

Here's what I use to avoid creating a function for the finally code and having to call it from two different places.

try:
    pass
    pass
    1/0
    pass
    pass
    pass
    raise Exception('Success')
except Exception, e:
    if e.message != 'Success':
        import traceback
        print traceback.format_exc()

    print 'in finally'
    print 'in finally'
    print 'in finally'

    if e.message != 'Success':
        raise


Output on Linux:

Traceback (most recent call last):
  File "./test_finally.py", line 34, in <module>
    1/0
ZeroDivisionError: integer division or modulo by zero

in finally
in finally
in finally
Traceback (most recent call last):
  File "./test_finally.py", line 34, in <module>
    1/0
ZeroDivisionError: integer division or modulo by zero

shell returned 1

Upvotes: 0

user2357112
user2357112

Reputation: 281643

  1. Because that's how it's defined. Because it's useful and standard to define it that way, and because there is no other useful way to define it.
  2. Yes, but not the way you did it.

    try:
        do_stuff()
    except OneProblem:
        handle_it()
    except DifferentProblem:
        handle_that()
    finally:
        cleanup()
    

    is equivalent to

    try:
        try:
            do_stuff()
        except OneProblem:
            handle_it()
        except DifferentProblem:
            handle_that()
    except:
        # Clean up if an unhandled exception happened, then restore the exception.
        cleanup()
        raise
    # Also clean up if we're not propagating an exception.
    cleanup()
    

in terms of cleanup always happening and never happening twice, though things like exception chaining and tracebacks might behave differently.

Upvotes: 1

Blckknght
Blckknght

Reputation: 104772

No, your alternative code is not quite equivalent to the try/except/finally version. To understand why, think about what would happen if a second exception was triggered inside the ...exception handler code... part of your example.

Here's a demo that shows the issue:

try:
    print('in try')     # guarded code
    1/0                 # oops, a bug
except ZeroDivisionError:
    print('top of except')  # exception handling code
    name_does_not_exist     # oops, the exception handling code is buggy too
    print('end of except')  # this is a bad place for cleanup code
finally:
    print('in finally')   # this is a much better place to do cleanup

Output:

in try
top of except
in finally
Traceback (most recent call last):

  File "<ipython-input-17-63590fc64963>", line 6, in <module>
    name_does_not_exist     # oops, the exception handling code is buggy too

NameError: name 'name_does_not_exist' is not defined

Note that the end of except message never gets printed, since the NameError occurs on the previous line. If line was the critical cleanup code, a program with only try and except would fail to run it. If you put the cleanup code in a finally block though, it's guaranteed to run regardless of any exceptions raised in any other part of the code.

Upvotes: 1

Related Questions