Search code examples
pythonexceptiontry-catchdefault

try-finally clause in Python behaves unexpectedly


Edit: in retrospect, this is not a well thought-out question, but I'm leaving it here for future readers who may have the same misunderstanding as me.

There may be a lapse in my understanding of how try/except/finally work in Python, but I would expect the following to work as described in comments.

from sys import argv

try:
    x = argv[1]  # Set x to the first argument if one is passed
finally:
    x = 'default'  # If no argument is passed (throwing an exception above) set x to 'default'

print(x)

I would expect that the file above (foo.py) should print default when run as python .\foo.py and would print bar if run as python .\foo.py bar.

The bar functionality works as expected, however, the default behaviour does not work; if I run python .\foo.py, I get an IndexError:

Traceback (most recent call last):
  File ".\foo.py", line 4, in <module>
    x = argv[1]
IndexError: list index out of range

As a result, I have two questions:

  • Is this a bug or is it an expected behavior in a try-finally block?
  • Should I just never use try-finally without an except clause?

Solution

  • This is expected behaviour. try:..finally:... alone doesn't catch exceptions. Only the except clause of a try:...except:... does.

    try:...finally:... only guarantees that the statements under finally are always executed, whatever happens in the try section, whether the block succeeds or is exited because of a break, continue, return or an exception. So try:...finally:... is great for cleaning up resources; you get to run code no matter what happens in the block (but note that the with statement and context managers let you encapsulate cleanup behaviour too). If you want to see examples, then the Python standard library has hundreds.

    If you need to handle an IndexError exception in a try block, then you must use an except clause. You can still use a finally clause as well, it'll be called after the except suite has run.

    And if you ever get to work with much older Python code, you'll see that in code that must run with Python 2.4 or older try:....finally:... and try:...except:... are never used together. That's because only as of Python 2.5 have the two forms been unified.