Skip to content

Latest commit

 

History

History
474 lines (397 loc) · 12.3 KB

exceptions.md

File metadata and controls

474 lines (397 loc) · 12.3 KB

Exceptions

So far we have made programs that ask the user to enter a string, and we also know how to convert that to an integer.

text=input("Enter something: ") number=int(text) print("Your number doubled:", number*2)

That works.

Enter a number: 3 Your number doubled: 6 

But that doesn't work if the user does not enter a number.

Enteranumber: lolTraceback (mostrecentcalllast): File"/some/place/file.py", line2, in<module>number=int(text) ValueError: invalidliteralforint() withbase10: 'lol'

So how can we fix that?

What are exceptions?

In the previous example we got a ValueError. ValueError is an exception. In other words, ValueError is an error that can occur in our program. If an exception occurs, the program will stop and we get an error message. The interactive prompt will display an error message and keep going.

>>>int('lol') Traceback (mostrecentcalllast): File"<stdin>", line1, in<module>ValueError: invalidliteralforint() withbase10: 'lol'>>>

Exceptions are classes.

>>>ValueError<class'ValueError'>>>>

We can also create exceptions. We won't get an error message by doing that, but we'll use this for displaying our own error messages later.

>>>the_problem=ValueError('oh no') >>>the_problemValueError('oh no',) >>>

Catching exceptions

If we need to try to do something and see if we get an exception, we can use try and except. This is also known as catching the exception.

>>>try: ... print(int('lol')) ... exceptValueError: ... print("Oops!") ... Oops! >>>

The except part doesn't run if the try part succeeds.

>>>try: ... print("Hello World!") ... exceptValueError: ... print("What the heck? Printing failed!") ... HelloWorld! >>>

ValueError is raised when something gets an invalid value, but the value's type is correct. In this case, int can take a string as an argument, but the string needs to contain a number, not lol. If the type is wrong, we will get a TypeError instead.

>>>123+'hello'Traceback (mostrecentcalllast): File"<stdin>", line1, in<module>TypeError: unsupportedoperandtype(s) for+: 'int'and'str'>>>

Exceptions always interrupt the code even if we catch them. Here the print never runs because it's after the error but inside the try block. Everything after the try block runs normally.

>>>try: ... 123+'hello' ... print("This doesn't get printed.") ... exceptTypeError: ... print("Oops!") ... Oops! >>>

Does an except ValueError also catch TypeErrors?

>>>try: ... print(123+'hello') ... exceptValueError: ... print("Oops!") ... Traceback (mostrecentcalllast): File"<stdin>", line2, in<module>TypeError: unsupportedoperandtype(s) for+: 'int'and'str'>>>

No, it doesn't. But maybe we could except for both ValueError and TypeError?

>>>try: ... int('lol') ... exceptValueError: ... print('wrong value') ... exceptTypeError: ... print('wrong type') ... wrongvalue>>>try: ... 123+'hello' ... exceptValueError: ... print('wrong value') ... exceptTypeError: ... print('wrong type') ... wrongtype>>>

Seems to be working.

We can also also catch multiple exceptions by catching a tuple of exceptions:

>>>try: ... 123+'hello' ... except (ValueError, TypeError): ... print('wrong value or type') ... wrongvalueortype>>>try: ... int('lol') ... except (ValueError, TypeError): ... print('wrong value or type') ... wrongvalueortype>>>

Catching Exception will catch all errors. We'll learn more about why it does that in a moment.

>>>try: ... 123+'hello' ... exceptException: ... print("Oops!") ... Oops! >>>try: ... int('lol') ... exceptException: ... print("Oops!") ... Oops! >>>

It's also possible to catch an exception and store it in a variable. Here we are catching an exception that Python created and storing it in our_error.

>>>try: ... 123+'hello' ... exceptTypeErrorase: ... our_error=e ... >>>our_errorTypeError("unsupported operand type(s) for +: 'int' and 'str'",) >>> type(our_error) <class'TypeError'>>>>

When should we catch exceptions?

Do not do things like this:

try: # many lines of codeexceptException: print("Oops! Something went wrong.")

There's many things that can go wrong in the try block. If something goes wrong all we have is an oops message that doesn't tell us which line caused the problem. This makes fixing the program really annoying. If we want to catch exceptions we need to be specific about what exactly we want to catch and where instead of catching everything we can in the whole program.

There's nothing wrong with doing things like this:

try: withopen('some file', 'r') asf: content=f.read() exceptOSError: # we can't read the file but we can work without itcontent=some_default_content

Usually catching errors that the user has caused is also a good idea:

importsystext=input("Enter a number: ") try: number=int(text) exceptValueError: print(f"'{text}' is not a number.", file=sys.stderr) sys.exit(1) print(f"Your number doubled is {(number*2)}.")

Raising exceptions

Now we know how to create exceptions and how to handle errors that Python creates. But we can also create error messages manually. This is known as raising an exception and throwing an exception.

Raising an exception is easy. All we need to do is to type raise and then an exception we want to raise:

>>>raiseValueError("lol is not a number") Traceback (mostrecentcalllast): File"<stdin>", line1, in<module>ValueError: lolisnotanumber>>>

Of course, we can also raise an exception from a variable.

>>>oops=ValueError("lol is not a number") >>>raiseoopsTraceback (mostrecentcalllast): File"<stdin>", line1, in<module>ValueError: lolisnotanumber>>>

If we define a function that raises an exception and call it we'll notice that the error message also says which functions we ran to get to that error.

>>>defoops(): ... raiseValueError("oh no!") ... >>>defdo_the_oops(): ... oops() ... >>>do_the_oops() Traceback (mostrecentcalllast): File"<stdin>", line1, in<module>File"<stdin>", line2, indo_the_oopsFile"<stdin>", line2, inoopsValueError: ohno! >>>

If our code was in a file we would also see the line of code that raised the error.

When should we raise exceptions?

Back in the module chapter we learned to display error messages by printing to sys.stderr and then calling sys.exit(1), so when should we use that and when should we raise an exception?

Exceptions are meant for programmers, so if we are writing something that other people will import we should use exceptions. If our program is working like it should be and the user has done something wrong, it's usually better to use sys.stderr and sys.exit.

Exception hierarchy

Exceptions are organized like this. I made this tree with this program on Python 3.7. You may have more or less exceptions than I have if your Python is newer or older than mine, but they should be mostly similar.

Exception ├── ArithmeticError │ ├── FloatingPointError │ ├── OverflowError │ └── ZeroDivisionError ├── AssertionError ├── AttributeError ├── BufferError ├── EOFError ├── ImportError │ └── ModuleNotFoundError ├── LookupError │ ├── IndexError │ └── KeyError ├── MemoryError ├── NameError │ └── UnboundLocalError ├── OSError │ ├── BlockingIOError │ ├── ChildProcessError │ ├── ConnectionError │ │ ├── BrokenPipeError │ │ ├── ConnectionAbortedError │ │ ├── ConnectionRefusedError │ │ └── ConnectionResetError │ ├── FileExistsError │ ├── FileNotFoundError │ ├── InterruptedError │ ├── IsADirectoryError │ ├── NotADirectoryError │ ├── PermissionError │ ├── ProcessLookupError │ └── TimeoutError ├── ReferenceError ├── RuntimeError │ ├── NotImplementedError │ └── RecursionError ├── StopAsyncIteration ├── StopIteration ├── SyntaxError │ └── IndentationError │ └── TabError ├── SystemError ├── TypeError ├── ValueError │ └── UnicodeError │ ├── UnicodeDecodeError │ ├── UnicodeEncodeError │ └── UnicodeTranslateError └── Warning ├── BytesWarning ├── DeprecationWarning ├── FutureWarning ├── ImportWarning ├── PendingDeprecationWarning ├── ResourceWarning ├── RuntimeWarning ├── SyntaxWarning ├── UnicodeWarning └── UserWarning 

Catching an exception also catches everything that's under it in this tree. For example, catching OSError catches errors that we typically get when processing files, and catching Exception catches all of these errors. You don't need to remember this tree, running help('builtins') should display a larger tree that this is a part of.

There are also a few exceptions that are not in this tree like SystemExit and KeyboardInterrupt, but most of the time we shouldn't catch them. Catching Exception doesn't catch them either.

Summary

  • Exceptions are classes and they can be used just like all other classes.
  • ValueError and TypeError are some of the most commonly used exceptions.
  • The try and except keywords can be used for attempting to do something and then doing something else if we get an error. This is known as catching exceptions.
  • It's possible to raise exceptions with the raise keyword. This is also known as throwing exceptions.
  • Raise exceptions if they are meant to be displayed for programmers and use sys.stderr and sys.exit otherwise.

Examples

Keep asking a number from the user until it's entered correctly.

whileTrue: try: number=int(input("Enter a number: ")) breakexceptValueError: print("That's not a valid number! Try again.") print("Your number doubled is:", number*2)

This program allows the user to customize the message it prints by modifying a file the greeting is stored in, and it can create the file for the user if it doesn't exist already. This example also uses things from the file chapter, the function defining chapter and the module chapter.

# These are here so you can change them to customize the program# easily.default_greeting="Hello World!"filename="greeting.txt"importsysdefaskyesno(question): whileTrue: answer=input(question+' (y or n) ') ifanswer=='Y'oranswer=='y': returnTrueifanswer=='N'oranswer=='n': returnFalsedefgreet(): withopen(filename, 'r') asf: forlineinf: print(line.rstrip('\n')) try: greet() exceptOSError: print(f"Cannot read '{filename}'!", file=sys.stderr) ifaskyesno("Would you like to create a default greeting file?"): withopen(filename, 'w') asf: print(default_greeting, file=f) greet()

If you have trouble with this tutorial, please tell me about it and I'll make this tutorial better, or ask for help online. If you like this tutorial, please give it a star.

You may use this tutorial freely at your own risk. See LICENSE.

Previous | Next | List of contents

close