• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

Justin Joyce

Practical tips and tutorials about software development.

  • Standing Invitation
  • Featured Posts
  • Latest
  • About

Python try except

Posted Jun 1, 2023 — Updated Feb 18, 2024

Try and except are the building blocks of exception handling in Python. You’ll also sometimes see finally and else. Here’s the summary:

  • try: run potentially-error-raising code in here
  • except: catches and handles errors that might have occurred in the try
  • finally: always runs after try / except, even if there were returns or re-raises
  • else: runs if try did not raise an error and try did not return

Try and Except

A simple try-except block looks like this:

try:
    risky_thing()
except Exception as e:
    print(f"oh no! exception: {e}")

You’ll usually see try and except by themselves; finally is used less often, and else even less. Here’s a (slightly) more realistic example:

me = {"name": "justin"}

def get_age(person):
    try:
        return person["age"]
    except KeyError as e:
        print(f"caught key error: {e}")

# There's no 'age' key on the dict
get_age(me)
# caught key error: 'age'

Except blocks optionally accept a specific error type—the example above will only catch a KeyError. In practice, you should always specify error type. If your code could produce multiple types of errors, just add an additional except block for each type:

try:
	  dangerous_code()
except TypeError as e:
    # handle error
except KeyError as e:
    # handle error
except IndexError as e:
    # handle error

Or group your exception handlers with parentheses and commas:

try:
    dangerous_code()
except (KeyError, IndexError) as e:
    # handle these two errors
except (ValueError, TypeError) as e:
    # handle these two errors

This way you know exactly how your code failed, and can log or fix it appropriately.

However, if you just need to ensure your code won’t blow up and you don’t care what kind of exception was raised, you can catch the Exception class:

try:
	  dangerous_code()
except Exception as e:
	  print(f"hit an error: {e}")

All non-fatal Python exception classes inherit from Exception, so you’ll catch almost any exception this way.

me = {"name": "justin", "age": 100}

def get_age(person):
	  try:
        print("Getting age")
  	    return person["age"]
	  except KeyError as e:
	      print("key error hit")
    finally:
        print("finally block hit")
        
my_age = get_age(me)
# Getting age
# finally block hit

print(my_age)
# 100

Finally

Finally executes code at the end of your try-except block and is typically used to perform some kind of cleanup action, like ensuring a file was closed1. Finally will run whether or not there was an exception, even if the exception was unhandled. Said another way, finally will always run:

me = {"name": "justin", "age": 100}

def get_age(person):
	  try:
		    print("Getting age")
		    return person["age"]
	  except KeyError as e:
		    print("key error hit")
	  finally:
        print("finally block hit")
        
my_age = get_age(me)
# Getting age
# finally block hit

print(my_age)
# 100

You might notice above that the finally block printed even though it’s down below a return statement. That’s unusual in Python; usually return exits the function before anything below it can run. Finally is an exception to this pattern, it will always run. To really drive this point home, let’s raise an unhandled exception:

# Don't do this
def bad_method():
	  try:
		    print("Starting")
        raise TypeError("AHHH")
    except KeyError as e:
        print("this won't hit, wrong error type")
    finally:
        print("the finally")
        
bad_method()
# Starting
# the finally
# TypeError: AHHH

Notice above that the raise still happened; bad_method() still raised the error, but it did it after the finally block executed.

One other important note about finally: if a finally clause includes a return statement, the returned value will be the one from the finally clause’s return statement, not the value from the try clause’s return statement2:

me = {"name": "justin", "age": 100}

def get_age(person):
	  try:
        print("Getting age")
		    return person["age"]
	  except KeyError as e:
		    print("key error hit")
	  finally:
    	  print("finally block hit")
        return "hello"
        
my_age = get_age(me)
# Getting age
# finally block hit

print(my_age)
# hello

In the example above my_age is “hello”, despite the return statement in the try block which should have returned 100. That’s because a return statement inside a finally always takes precedence. If the finally does not contain a return statement the function will use the return value from the try or except blocks.

A return statement within a finally block is always the value returned from its function, even if there’s an unhandled exception in its try / except blocks. For that reason you should never return from within a finally block—you could accidentally swallow exceptions without even knowing it.

Else

Within try / except,  an else block is used to evaluate code only if the try block did not raise an exception or return:

def else_example1()
	  try:
    	  print("no errors here")
	  except Exception as e:
    	  print("this won't print")
    else:
    	  print("this will print")
        
else_example1()
# no errors here
# this will print

def else_example2()
	  try:
    	  print("no errors here")
        return "hi"
	  except Exception as e:
    	  print("this won't print")
    else:
    	  print("this will also not print")
        
else_example2()
# no errors here
"hi"

I rarely see else used in this context, and it might indicate a code smell—in many cases you can accomplish the same thing through appropriate use of raise.


Helpful Links
  • Errors and Exceptions – Python.org
  • Why you should specify error types in your except – Stackoverflow
Notes
  1. This is also why we use context managers ↩︎
  2. This note about finally was lifted straight from the Python docs ↩︎

Filed Under: Python

Primary Sidebar

Recent Posts

  • Every Built-In Vim Color Scheme (with screenshots)
  • Reverse a string in Python
  • Meeting Cost Calculator
  • Vim find and replace
  • What makes an effective development team

Categories

  • Arrays (5)
  • Command Line (9)
  • Dates (3)
  • Featured (7)
  • Git (7)
  • Golang (5)
  • Javascript (8)
  • Productivity (8)
  • Projects (4)
  • Python (15)
  • Regex (2)
  • Ruby (3)
  • Shell (2)
  • Thoughts (2)
  • Tips (11)
  • Tools (3)
  • Tutorials (1)
  • Vim (4)

Archives

  • July 2024 (1)
  • February 2024 (1)
  • January 2024 (1)
  • December 2023 (1)
  • November 2023 (1)
  • October 2023 (4)
  • September 2023 (1)
  • August 2023 (2)
  • July 2023 (5)
  • June 2023 (3)
  • May 2023 (6)
  • April 2023 (5)
  • March 2023 (5)
  • February 2023 (10)
  • January 2023 (6)
  • December 2022 (7)

Copyright © 2025 · Contact me at justin [at] {this domain}

  • Privacy Policy