We have already talked about error messages: Python complains, tells us where the error (line) is, and terminates the program. But there is much more that we can learn about error messages (a.k.a exceptions).
In the beginning we will repeat how Python prints an error which is in a nested function.
def out_func():
return in_func(0)
def in_func(divisor):
return 1 / divisor
print(out_func())
Traceback (most recent call last):
File "/tmp/example.py", line 7, in <module>
print(out_func())
File "/tmp/example.py", line 2, in out_func
return in_func(0)
File "/tmp/example.py", line 5, in in_func
return 1 / divisor
ZeroDivisionError: division by zero
You notice that every function call that led to the error is listed here.
The actuall error is probably somewhere near that function call.
In our case it's easy. We shouldn't call in_func
with argument 0
.
Or, the in_function
must be written to handle the case that the divisor can be 0
and it should do something else than try to devide by zero.
Python can't know where the error is that needs to be fixed, so it shows you everything in the error message. This will be very useful in more complex programs.
An error, or more precisely an exception, can be also invoked by the command raise
.
After that command, write the name of the exception and some
information about what went wrong in parentheses.
LIST_SIZE = 20
def verify_number(number):
if 0 <= number < LIST_SIZE:
print('OK!')
else:
raise ValueError('The number {n} is not in the list!'.format(n=number))
All types of built-in exceptions are here, including their hierarchy.
These exceptions are important to us now:
BaseException
├── SystemExit raised by function exit()
├── KeyboardInterrupt raised after pressing Ctrl+C
╰── Exception
├── ArithmeticError
│ ╰── ZeroDivisionError zero division
├── AssertionError command `assert` failed
├── AttributeError non-existing attribute, e.g. 'abc'.len
├── ImportError failed import
├── LookupError
│ ╰── IndexError non-existing index, e.g. 'abc'[999]
├── NameError used a non-existing variable name
│ ╰── UnboundLocalError used a variable that wasn't initiated
├── SyntaxError wrong syntax – program is unreadable/unusable
│ ╰── IndentationError wrong indentation
│ ╰── TabError combination of tabs and spaces
├── TypeError wrong type, e.g. len(9)
╰── ValueError wrong value, e.g. int('xyz')
And why are there so many?
So you can catch them! :)
In the following function, the int
function can
fail when something other than a
number is given to it. It needs to be prepared for
that kind of situation with a try/except
block. (You also
commonly hear this called a try/catch
block -- mostly in other
programming languages).
def load_number():
answer = input('Enter some number: ')
try:
number = int(answer)
except ValueError:
print('That was not a number! I will continue with 0')
number = 0
return number
So how does this work?
Python runs the commands within the try
block, but if the error occurs
that you mentioned after except
, Python won't terminate the program, instead, it will
run all the commands in the exception block.
If there's no error, the except block will be skipped.
When you catch a general exception, Python also catches
exceptions that are related to it (in the diagram, they are listed as child entries) --
e.g. except ArithmeticError:
will also catch ZeroDivisionError
.
And except Exception:
will catch all usual exceptions.
There is no need to catch most of the errors.
If any unexpected error happens it's always much better to terminate the program than to continue with wrong values. In addition, Python's standard error output will make it really easy for you to find the error.
For example, catching the exception KeyboardInterrupt
could have the side effect that the program couldn't be terminated if we needed to
(with shortcut Ctrl+C).
Use the command try/except
only in situations when you
expect some exception -- when you know exactly what could happen
and why, and you have the option to correct it -- in the
except block.
A typical example would be reading input from a user. If the user
enters gibberish, it's better to ask again until the
user enters something meaningful:
>>> def retrieve_number():
... while True:
... answer = input("Type a number: ")
... try:
... return int(answer)
... except ValueError:
... print("This is not a number. Try again")
>>> print(retrieve_number())
Type a number: twenty
This is not a number. Try again
Type a number: 20
20
Additionally to except
, there are two more clauses - blocks that can
be used with try
, and these are else
and finally
.
The first one will be run if exception in the try
block didn't happen.
And finally
runs every time.
You can also have several except
blocks. Only one of them will be triggered --
the first one that can handle the raised exception.
try:
do_something()
except ValueError:
print("This will be printed if there's a ValueError.")
except NameError:
print("This will be printed if there's a NameError.")
except Exception:
print("This will be printed if there's some other exception.")
# (apart from SystemExit a KeyboardInterrupt, we don't want to catch those)
except TypeError:
print("This will never be printed")
# ("except Exception" above already caught the TypeError)
else:
print("This will be printed if there's no error in try block")
finally:
print("This will always be printed; even if there's e.g. a 'return' in the 'try' block.")
Let's add exception handling to our calculator (or to 1-D ticktactoe, if you have it)
if the user doesn't enter a number in the input.
{ "data": { "sessionMaterial": { "id": "session-material:2018/pyladies-en-prague:tests:0", "title": "Exceptions", "html": "\n \n \n\n <h1>Exceptions</h1>\n<p>We have already talked about <a href=\"/2018/pyladies-en-prague/beginners/print/\">error messages</a>: \nPython complains, tells us where the error (line) is, and terminates the program.\nBut there is much more that we can learn about error messages (a.k.a <em>exceptions</em>).</p>\n<h2>Printing errors:</h2>\n<p>In the beginning we will repeat how Python prints an error which is in a nested function.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">out_func</span><span class=\"p\">():</span>\n <span class=\"k\">return</span> <span class=\"n\">in_func</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">in_func</span><span class=\"p\">(</span><span class=\"n\">divisor</span><span class=\"p\">):</span>\n <span class=\"k\">return</span> <span class=\"mi\">1</span> <span class=\"o\">/</span> <span class=\"n\">divisor</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">out_func</span><span class=\"p\">())</span>\n</pre></div><!-- XXX: Highlight the line numbers -->\n\n<div class=\"highlight\"><pre><span></span><span class=\"gt\">Traceback (most recent call last): </span>\n File <span class=\"nb\">"/tmp/example.py"</span>, line <span class=\"m\">7</span>, in <span class=\"n\"><module></span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">out_func</span><span class=\"p\">())</span>\n File <span class=\"nb\">"/tmp/example.py"</span>, line <span class=\"m\">2</span>, in <span class=\"n\">out_func</span>\n <span class=\"k\">return</span> <span class=\"n\">in_func</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n File <span class=\"nb\">"/tmp/example.py"</span>, line <span class=\"m\">5</span>, in <span class=\"n\">in_func</span>\n <span class=\"k\">return</span> <span class=\"mi\">1</span> <span class=\"o\">/</span> <span class=\"n\">divisor</span>\n<span class=\"gr\">ZeroDivisionError</span>: <span class=\"n\">division by zero</span>\n</pre></div><p>You notice that every function call that led to the error is listed here.\nThe actuall error is probably somewhere near that function call.\nIn our case it's easy. We shouldn't call <code>in_func</code> with argument <code>0</code>.\nOr, the <code>in_function</code> must be written to handle the case that the divisor can be <code>0</code>\nand it should do something else than try to devide by zero.</p>\n<p>Python can't know where the error is that needs to be fixed, so it shows\nyou everything in the error message.\nThis will be very useful in more complex programs.</p>\n<h2>Raising an error</h2>\n<p>An error, or more precisely an <em>exception</em>, can be also invoked by the command <code>raise</code>.\nAfter that command, write the name of the exception and some\ninformation about what went wrong in parentheses.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">LIST_SIZE</span> <span class=\"o\">=</span> <span class=\"mi\">20</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">verify_number</span><span class=\"p\">(</span><span class=\"n\">number</span><span class=\"p\">):</span>\n <span class=\"k\">if</span> <span class=\"mi\">0</span> <span class=\"o\"><=</span> <span class=\"n\">number</span> <span class=\"o\"><</span> <span class=\"n\">LIST_SIZE</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'OK!'</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"ne\">ValueError</span><span class=\"p\">(</span><span class=\"s1\">'The number {n} is not in the list!'</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"o\">=</span><span class=\"n\">number</span><span class=\"p\">))</span>\n</pre></div><p>All types of built-in exceptions are\n<a href=\"https://docs.python.org/3/library/exceptions.html\">here</a>, including their hierarchy.</p>\n<p>These exceptions are important to us now:</p>\n<div class=\"highlight\"><pre><code>BaseException\n ├── SystemExit raised by function exit()\n ├── KeyboardInterrupt raised after pressing Ctrl+C\n ╰── Exception\n ├── ArithmeticError\n │ ╰── ZeroDivisionError zero division\n ├── AssertionError command `assert` failed\n ├── AttributeError non-existing attribute, e.g. 'abc'.len\n ├── ImportError failed import\n ├── LookupError\n │ ╰── IndexError non-existing index, e.g. 'abc'[999]\n ├── NameError used a non-existing variable name\n │ ╰── UnboundLocalError used a variable that wasn't initiated\n ├── SyntaxError wrong syntax – program is unreadable/unusable\n │ ╰── IndentationError wrong indentation\n │ ╰── TabError combination of tabs and spaces\n ├── TypeError wrong type, e.g. len(9)\n ╰── ValueError wrong value, e.g. int('xyz')</code></pre></div><h2>Handling Exceptions</h2>\n<p>And why are there so many?\nSo you can catch them! :)\nIn the following function, the <code>int</code> function can \nfail when something other than a\nnumber is given to it. It needs to be prepared for\nthat kind of situation with a <code>try/except</code> block. (You also\ncommonly hear this called a <code>try/catch</code> block -- mostly in other\nprogramming languages).</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">load_number</span><span class=\"p\">():</span>\n <span class=\"n\">answer</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">'Enter some number: '</span><span class=\"p\">)</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"n\">number</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"n\">answer</span><span class=\"p\">)</span>\n <span class=\"k\">except</span> <span class=\"ne\">ValueError</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'That was not a number! I will continue with 0'</span><span class=\"p\">)</span>\n <span class=\"n\">number</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n <span class=\"k\">return</span> <span class=\"n\">number</span>\n</pre></div><p>So how does this work?\nPython runs the commands within the <code>try</code> block, but if the error occurs\nthat you mentioned after <code>except</code>, Python won't terminate the program, instead, it will\nrun all the commands in the exception block.\nIf there's no error, the except block will be skipped.</p>\n<p>When you catch a general exception, Python also catches\nexceptions that are related to it (in the diagram, they are listed as child entries) -- \ne.g. <code>except ArithmeticError:</code> will also catch <code>ZeroDivisionError</code>.\nAnd <code>except Exception:</code> will catch all usual exceptions.</p>\n<h2>Don't catch'em all!</h2>\n<p>There is no need to catch most of the errors.</p>\n<p>If any unexpected error happens \nit's always <em>much</em> better to terminate the program\nthan to continue with wrong values.\nIn addition, Python's standard error output will make it\nreally easy for you to find the error.</p>\n<p>For example, catching the exception <code>KeyboardInterrupt</code>\ncould have the side effect that the program couldn't be terminated if we needed to\n(with shortcut <kbd>Ctrl</kbd>+<kbd>C</kbd>).</p>\n<p>Use the command <code>try/except</code> only in situations when you\nexpect some exception -- when you know exactly what could happen\nand why, and you have the option to correct it -- in the\nexcept block.\nA typical example would be reading input from a user. If the user \nenters gibberish, it's better to ask again until the\nuser enters something meaningful:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">>>> </span><span class=\"k\">def</span> <span class=\"nf\">retrieve_number</span><span class=\"p\">():</span>\n<span class=\"gp\">... </span> <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n<span class=\"gp\">... </span> <span class=\"n\">answer</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s2\">"Type a number: "</span><span class=\"p\">)</span>\n<span class=\"gp\">... </span> <span class=\"k\">try</span><span class=\"p\">:</span>\n<span class=\"gp\">... </span> <span class=\"k\">return</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"n\">answer</span><span class=\"p\">)</span>\n<span class=\"gp\">... </span> <span class=\"k\">except</span> <span class=\"ne\">ValueError</span><span class=\"p\">:</span>\n<span class=\"gp\">... </span> <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This is not a number. Try again"</span><span class=\"p\">)</span>\n\n<span class=\"gp\">>>> </span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">retrieve_number</span><span class=\"p\">())</span>\n<span class=\"go\">Type a number: twenty</span>\n<span class=\"go\">This is not a number. Try again</span>\n<span class=\"go\">Type a number: 20</span>\n<span class=\"go\">20</span>\n</pre></div><h2>Other clauses</h2>\n<p>Additionally to <code>except</code>, there are two more clauses - blocks that can \nbe used with <code>try</code>, and these are <code>else</code> and <code>finally</code>.\nThe first one will be run if exception in the <code>try</code> block didn't happen.\nAnd <code>finally</code> runs every time.</p>\n<p>You can also have several <code>except</code> blocks. Only one of them will be triggered -- \nthe first one that can handle the raised exception.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"n\">do_something</span><span class=\"p\">()</span>\n<span class=\"k\">except</span> <span class=\"ne\">ValueError</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This will be printed if there's a ValueError."</span><span class=\"p\">)</span>\n<span class=\"k\">except</span> <span class=\"ne\">NameError</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This will be printed if there's a NameError."</span><span class=\"p\">)</span>\n<span class=\"k\">except</span> <span class=\"ne\">Exception</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This will be printed if there's some other exception."</span><span class=\"p\">)</span>\n <span class=\"c1\"># (apart from SystemExit a KeyboardInterrupt, we don't want to catch those)</span>\n<span class=\"k\">except</span> <span class=\"ne\">TypeError</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This will never be printed"</span><span class=\"p\">)</span>\n <span class=\"c1\"># ("except Exception" above already caught the TypeError)</span>\n<span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This will be printed if there's no error in try block"</span><span class=\"p\">)</span>\n<span class=\"k\">finally</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"This will always be printed; even if there's e.g. a 'return' in the 'try' block."</span><span class=\"p\">)</span>\n</pre></div><h2>Task</h2>\n<p>Let's add exception handling to our calculator (or to 1-D ticktactoe, if you have it)<br>\nif the user doesn't enter a number in the input.</p>\n<div class=\"solution\" id=\"solution-0\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2018/pyladies-en-prague/beginners-en/exceptions/index/solutions/0/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <p>Possible solution for the calculator:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"n\">side</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">'Enter the side of a square in centimeters: '</span><span class=\"p\">))</span>\n <span class=\"k\">except</span> <span class=\"ne\">ValueError</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'That was not a number!'</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">if</span> <span class=\"n\">side</span> <span class=\"o\"><=</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'That does not make sense!'</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">break</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"The perimeter of a square with a side of"</span><span class=\"p\">,</span> <span class=\"n\">side</span><span class=\"p\">,</span><span class=\"s2\">"cm is "</span><span class=\"p\">,</span> <span class=\"n\">side</span> <span class=\"o\">*</span> <span class=\"mi\">4</span><span class=\"p\">,</span><span class=\"s2\">"cm."</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"The area of a square with a side of"</span><span class=\"p\">,</span> <span class=\"n\">side</span><span class=\"p\">,</span><span class=\"s2\">"cm is"</span><span class=\"p\">,</span> <span class=\"n\">side</span> <span class=\"o\">*</span> <span class=\"n\">side</span><span class=\"p\">,</span> <span class=\"s2\">"cm2."</span><span class=\"p\">)</span>\n</pre></div><p>Possible solution for 1-D ticktactoe:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">load_number</span><span class=\"p\">(</span><span class=\"n\">field</span><span class=\"p\">):</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"n\">position</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">'Which position do you want to fill? (0..19) '</span><span class=\"p\">))</span>\n <span class=\"k\">except</span> <span class=\"ne\">ValueError</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'This is not a number!'</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">if</span> <span class=\"n\">position</span> <span class=\"o\"><</span> <span class=\"mi\">0</span> <span class=\"ow\">or</span> <span class=\"n\">position</span> <span class=\"o\">>=</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">field</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'You can not play outside the field!'</span><span class=\"p\">)</span>\n <span class=\"k\">elif</span> <span class=\"n\">field</span><span class=\"p\">[</span><span class=\"n\">position</span><span class=\"p\">]</span> <span class=\"o\">!=</span> <span class=\"s1\">'-'</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'That position is not free!'</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">break</span>\n\n <span class=\"n\">field</span> <span class=\"o\">=</span> <span class=\"n\">field</span><span class=\"p\">[:</span><span class=\"n\">position</span><span class=\"p\">]</span> <span class=\"o\">+</span> <span class=\"s1\">'o'</span> <span class=\"o\">+</span> <span class=\"n\">field</span><span class=\"p\">[</span><span class=\"n\">position</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">:]</span>\n <span class=\"k\">return</span> <span class=\"n\">field</span>\n\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">player_move</span><span class=\"p\">(</span><span class=\"s1\">'-x----'</span><span class=\"p\">))</span>\n</pre></div>\n </div>\n</div>\n\n\n " } } }