Search code examples
pythontry-finally

What could be practical examples of using finally in the try block in Python


When does it make sense to use finally in the try..except block? Isn't listing statements just after the try..except doing the same?

What's the difference between these two?

try:
    result = 100 / 0
except ZeroDivisionError:
    print("division by zero!")
else:
    print("result is", result)
finally:
    print("final statement")

vs.

try:
    result = 100 / 0
except ZeroDivisionError:
    print("division by zero!")
else:
    print("result is", result)
print("final statement")

Solution

  • Isn't listing statements just after the try..except doing the same?

    No, it isn't. You are assuming that you've covered all the ways the block can exit.

    finally is guaranteed to be executed even if the block is exited. That includes return, continue or break, not only exceptions.

    For your specific examples, you covered almost every possible path out of the try block. But you didn't cover KeyboardInterrupt or MemoryError however. If someone hits CTRL-C in the middle of execution, only for the first example would the print("some code at last") line be executed.

    That's because your code doesn't catch KeyboardInterrupt or MemoryError, but those exceptions could still occur. If one of the exceptions was raised for the result = 100 / 0 statement, then the exception would not be caught and the whole frame would be exited. But not before the finally: clause was executed.

    It is easier to demonstrate with a different example, one which doesn't catch the exception raised:

    mapping = {42: "The answer"}
    try:
        result = mapping[42] / 17
    except KeyError:
        print("Oops, no key 42 defined!")
    else:
        print(result)
    finally:
        del mapping
    

    Here, the finally statement will be executed, even though the above raises a TypeError exception:

    >>> mapping = {42: "The answer"}
    >>> try:
    ...     result = mapping[42] / 17
    ... except KeyError:
    ...     print("Oops, no key 42 defined!")
    ... else:
    ...     print(result)
    ... finally:
    ...     del mapping
    ...
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    TypeError: unsupported operand type(s) for /: 'str' and 'int'
    >>> mapping  # was deleted in the `finally` block
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'mapping' is not defined
    

    And that's what finally usually is used for, to clean up resources. You can even use it with return exiting a function:

    >>> def foo():
    ...     try:
    ...         return 42
    ...     finally:
    ...         print("Returning doesn't stop finally from executing!")
    ...     print("This is never reached")
    ...
    >>> foo()
    Returning doesn't stop finally from executing!
    42
    

    Note that Python also has the with statement and context managers to help do the same kind of work. Context managers encapsulate the clean-up usually done in finally and let you avoid having to look for the finally block at the end of a longer piece of code just to check if, say, a file is closed.

    So instead of:

    fileobj = open(filename)
    try:
        with line in fileobj:
            # many lines of parsing code
            # .
            # etc.
            # .
    finally:
        fileobj.close()
    

    you can use file objects as a context manager and the above is simplified to:

    with open(filename) as fileobj:
        with line in fileobj:
            # many lines of parsing code
            # .
            # etc.
            # .