Introduction

Python will let you know when you get your syntax wrong. It'll immediately fail and not allow your program to run.

What about when your code has a different type of problem? Those are called exceptions, and they tend to be harder to catch. It's up to you to recognize situations where hey might come up and catch them to prevent your program from crashing altogether.

Imagine a scenario where you need user input. Do you want your program to crash every time a user mistypes something or enters something erroneous? That's far from ideal. Since you know there could be a problem there, you can tell Python to look out for one, and recover gracefully.

Try and Except

When you know there's a chance an exception will pop up, you ask Python to try the piece of code and look out for an exception. Take a look at it in the context of the number example from before.
try:
	number = int(input("Enter any number: "))
except:
	print("That didn't work…")
Now, instead of crashing, you program will continue, even if the user enters garbage.

You can also tell Python exactly what type of exception to look for. In this case, it would be a ValueError.
try:
	number = int(input("Enter any number: "))
except ValueError:
	print("You didn't enter a number!")
If Python knows what type of exception it's looking for, you can tell it how to respond accordingly.

Make Your Own

You can make your own exceptions by writing them as classes that inherit from the Exception base class or any one of the other exception classes.
class NewException(Exception):
	pass
You have to build into your program how to use your custom exceptions.

Raising Exceptions

There is an option to manually raise exceptions with raise.
try:
	raise NewException
except NewException:
	print("It failed as planned!")

Multiple Excepts

If you're expecting that there could be more than one exception, you can built multiples in with try. Start off by making a couple of custom exceptions.
class ExceptionA(Exception):
	pass

class ExceptionB(Exception):
	pass
Now, use them with try.
try:
	raise ExceptionB
except (ExceptionA, ExceptionB):
	print("It was either A or B")
Of course, that's not the best because in a real scenario, you wouldn't know which exception actually occurred. Try using separate except clauses instead.
try:
	raise ExceptionB
except ExceptionA:
	print("ExceptionA Occurred")
except ExceptionB:
	print("ExceptionB Occurred")
except:
	print("Something else happened…")
The third except acts to catch any other exception. That's the default behavior when a specific exception isn't given.

Else and Finally

There are two additional clauses for handling what happens after exceptions are handled. They help your code respond to exceptions.

The else clause allows you to run code only if an exception hasn't been raised.
try:
	number = int(input("Enter any number: "))
except ValueError:
	print("You didn't enter a number!")
else:
	print(number ** 2)
You only want to try performing an operation on the number if one is entered.

The finally cause will run regardless of any exceptions. It will always run at the end of the try block.
try:
	number = int(input("Enter any number: "))
except ValueError:
	print("You didn't enter a number!")
else:
	print(number ** 2)
finally:
	print("This will always run.")

Closing Thoughts

It's important to understand what can go wrong with your programs. It's just as important to know that thing will go wrong. By accounting for exceptions, you can make your code as stable as possible and not frustrate your users.

Exercises

  1. Create some code that raises an unhandled exception.
  2. Use a try block to handle that exception.
  3. Create a custom exception class
  4. Raise that custom exception in a try block.
  5. Create a try block with a piece of code that only runs if an exception isn't raised.