"Cell \u001b[0;32mIn [1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m1\u001b[39;49m\u001b[39m/\u001b[39;49m\u001b[39m0\u001b[39;49m)\n",
"\u001b[0;31mZeroDivisionError\u001b[0m: division by zero"
]
}
],
"source": [
"print(1/0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Obviously, we knew beforehand that we should not try to divide 1 by 0.\n",
"The program crashed when we did so anyway - but python gives us more information what happened and where. This is called a ***traceback***. This traceback can also include multiple function calls or classes to guide us to where the error occurred.\n",
"\n",
"Python uses special objects called *exceptions*. There are many pre-defined exceptions (and we can define our own) that help the user not only to understand what is happening, but also to handle exceptions in the code.\n",
"\n",
"In our case here, we get the special exception: ```ZeroDivisionError``` with the added explanation: ```division by zero```.\n",
"\n",
"## Intercepting Exceptions\n",
"\n",
"If we suspect that an operation fails, we can use a try-except clause to prevent a crash.\n",
"The general syntax is\n",
"\n",
"```\n",
"try:\n",
" # our statement here\n",
"except <exception we want to catch>:\n",
" # how to handle the error\n",
"except <other exceptions>:\n",
" # other recovery procedures\n",
"else:\n",
" # if we need to do something when everything worked\n",
"finally:\n",
" # whatever we need to do after the try-except clause completes.\n",
"```\n",
"\n",
"When we define a try-except block we should at least catch one exception. However, in case we need more elaborate error handling, we can catch several exceptions of different type and act accordingly.\n",
"\n",
"\n",
"For the example above, we could try: \\\n",
"(```\\n``` creates a new line)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Do not try to divide by zero! \n",
" Caught exeption: division by zero\n"
]
}
],
"source": [
"try:\n",
" print(1/0)\n",
"except ZeroDivisionError as e:\n",
" print('Do not try to divide by zero! \\n Caught exeption: {}'.format(e))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, we intercept the specific exception ```DivisionZeroError``` and just print out a warning. Note that the program no longer crashes: The exception is being raised, we intercept it and do something with it. In a more elaborate program we would then do some error handling and either continue with the code, or end the program in a controlled way if we find we cannot continue in a meaningful way.\n",
"\n",
"Note that we intercepted the inception as a new variable: ```as e```. \\\n",
"This allows us to work with and inspect this exception. Here we just print it and obtain the same information we had in the traceback earlier.\n",
"\n",
"If we want to catch several types of exeption that we want to treat in the same way, we can do:\n",
"In many cases, we come to a point where we need to let the user know that a situation occurred that should not have occured. In the simplest way, we could write a ```print``` statement and print out an error message and then terminate the program. \\\n",
"However, it would be better to pass an exception to the upstream code (e.g. the code calling a function or using a class) so they can inspect the error, handle it - or let the program crash if they do not.\n",
"\n",
"To raise an exception, we use the keyword ```raise```. \n",
"\n",
"Python already has a comprehensive list of [pre-defined exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions). If necessary, we can define our own as well.\n",
"\n",
"***Example***\n",
"\n",
"We define a function that divides two integer numbers. If the numbers are not integers, we raise an exception."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The result is: 0.6\n"
]
},
{
"ename": "TypeError",
"evalue": "At least one of the arguments is not an integer",
"Cell \u001b[0;32mIn [4], line 11\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mThe result is: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(result))\n\u001b[1;32m 10\u001b[0m \u001b[39m# This will crash:\u001b[39;00m\n\u001b[0;32m---> 11\u001b[0m result \u001b[39m=\u001b[39m my_divide(\u001b[39m'\u001b[39;49m\u001b[39mthree\u001b[39;49m\u001b[39m'\u001b[39;49m,\u001b[39m5\u001b[39;49m)\n\u001b[1;32m 12\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mThe result is: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(result))\n",
"Cell \u001b[0;32mIn [4], line 3\u001b[0m, in \u001b[0;36mmy_divide\u001b[0;34m(a, b)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mmy_divide\u001b[39m (a, b):\n\u001b[1;32m 2\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mtype\u001b[39m(a) \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mint\u001b[39m \u001b[39mor\u001b[39;00m \u001b[39mtype\u001b[39m(b) \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mint\u001b[39m:\n\u001b[0;32m----> 3\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mTypeError\u001b[39;00m(\u001b[39m'\u001b[39m\u001b[39mAt least one of the arguments is not an integer\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m 4\u001b[0m \u001b[39mreturn\u001b[39;00m a\u001b[39m/\u001b[39mb\n",
"\u001b[0;31mTypeError\u001b[0m: At least one of the arguments is not an integer"
]
}
],
"source": [
"def my_divide (a, b):\n",
" if type(a) is not int or type(b) is not int:\n",
" raise TypeError('At least one of the arguments is not an integer')\n",
" return a/b\n",
"\n",
"# This is the intended use\n",
"result = my_divide(3,5)\n",
"print('The result is: {}'.format(result))\n",
"\n",
"# This will crash:\n",
"result = my_divide('three',5)\n",
"print('The result is: {}'.format(result))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, we can see a more elaborate traceback.\n",
"\n",
"We note:\n",
"* We have augmented the pre-defined exception ```TypeError``` with an optional message that we can pass along to give more information about the exception or why we have raised it. It is a ```TypeError```, because we only want our function to work on specific types and one of the arguments is not of the specific type.\n",
"* The Tracebak shows the line that lead to the error \n",
"```\n",
"---> 11 result = my_divide('three',5)\n",
"```\n",
"and then the line where the error actually occured. If we have more functions or classes, then we would have a longer traceback, going through all steps.\n",
"\n",
"\n",
"We now use the try-except clause to intercept this case:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Caught an exception: At least one of the arguments is not an integer\n"
"Cell \u001b[0;32mIn [7], line 14\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mTypeError\u001b[39;00m \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m 12\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mCaught an exception: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(e))\n\u001b[0;32m---> 14\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mThe result is: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(new_result)) \n",
"\u001b[0;31mNameError\u001b[0m: name 'new_result' is not defined"
]
}
],
"source": [
"def my_divide (a, b):\n",
" if type(a) is not int or type(b) is not int:\n",
" raise TypeError('At least one of the arguments is not an integer')\n",
" return a/b\n",
"\n",
"\n",
"# Avoid the crash:\n",
"try:\n",
" new_result = my_divide('three',5)\n",
" print('The result is: {}'.format(new_result))\n",
"except TypeError as e:\n",
" print('Caught an exception: {}'.format(e))\n",
"\n",
"print('The result is: {}'.format(new_result)) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we note that the ```new_result``` is not defined outside the block because it was used inside the ```try``` block.\n",
"\n",
"Either we better create the variable first (like below) - or we need to stop the program gracefully if we intend to continue with the result but cannot do so without it."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Caught an exception: At least one of the arguments is not an integer\n",
"The result is: 0\n"
]
}
],
"source": [
"result = 0\n",
"try:\n",
" result = my_divide('three',5)\n",
" print('The result is: {}'.format(result))\n",
"except TypeError as e:\n",
" print('Caught an exception: {}'.format(e))\n",
"\n",
"print('The result is: {}'.format(result)) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now result is defined - but we need to make sure that the default value is useful. Since we have now intercepted the exception, the code no longer stops. If ```result = 0``` is not useful, we need to handle this case now as well...\n",
"Cell \u001b[0;32mIn [9], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m result \u001b[39m=\u001b[39m \u001b[39m0\u001b[39m\n\u001b[1;32m 2\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m----> 3\u001b[0m result \u001b[39m=\u001b[39m my_divide(\u001b[39m3\u001b[39;49m,\u001b[39m0\u001b[39;49m)\n\u001b[1;32m 4\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mThe result is: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mformat(result))\n\u001b[1;32m 5\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mTypeError\u001b[39;00m \u001b[39mas\u001b[39;00m e:\n",
"Cell \u001b[0;32mIn [7], line 4\u001b[0m, in \u001b[0;36mmy_divide\u001b[0;34m(a, b)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mtype\u001b[39m(a) \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mint\u001b[39m \u001b[39mor\u001b[39;00m \u001b[39mtype\u001b[39m(b) \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mint\u001b[39m:\n\u001b[1;32m 3\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mTypeError\u001b[39;00m(\u001b[39m'\u001b[39m\u001b[39mAt least one of the arguments is not an integer\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[39mreturn\u001b[39;00m a\u001b[39m/\u001b[39;49mb\n",
"\u001b[0;31mZeroDivisionError\u001b[0m: division by zero"
]
}
],
"source": [
"result = 0\n",
"try:\n",
" result = my_divide(3,0)\n",
" print('The result is: {}'.format(result))\n",
"except TypeError as e:\n",
" print('Caught an exception: {}'.format(e))\n",
"\n",
"print('The result is: {}'.format(result)) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the code crashes again!\n",
".... we have intercepted the ```TypeError``` but not ```ZeroDivisionError```.\n",
"Let's add this too:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Do not divide by zero! division by zero\n",
"The result is: 0\n"
]
}
],
"source": [
"result = 0\n",
"try:\n",
" result = my_divide(3,0)\n",
" print('The result is: {}'.format(result))\n",
"except TypeError as e:\n",
" print('Caught a TypeError: {}'.format(e))\n",
"except ZeroDivisionError as e:\n",
" print('Do not divide by zero! {}'.format(e))\n",
Obviously, we knew beforehand that we should not try to divide 1 by 0.
The program crashed when we did so anyway - but python gives us more information what happened and where. This is called a ***traceback***. This traceback can also include multiple function calls or classes to guide us to where the error occurred.
Python uses special objects called *exceptions*. There are many pre-defined exceptions (and we can define our own) that help the user not only to understand what is happening, but also to handle exceptions in the code.
In our case here, we get the special exception: ```ZeroDivisionError``` with the added explanation: ```division by zero```.
## Intercepting Exceptions
If we suspect that an operation fails, we can use a try-except clause to prevent a crash.
The general syntax is
```
try:
# our statement here
except <exception we want to catch>:
# how to handle the error
except <other exceptions>:
# other recovery procedures
else:
# if we need to do something when everything worked
finally:
# whatever we need to do after the try-except clause completes.
```
When we define a try-except block we should at least catch one exception. However, in case we need more elaborate error handling, we can catch several exceptions of different type and act accordingly.
For the example above, we could try: \
(```\n``` creates a new line)
%% Cell type:code id: tags:
``` python
try:
print(1/0)
exceptZeroDivisionErrorase:
print('Do not try to divide by zero! \n Caught exeption: {}'.format(e))
```
%% Output
Do not try to divide by zero!
Caught exeption: division by zero
%% Cell type:markdown id: tags:
%% Cell type:markdown id: tags:
Here, we intercept the specific exception ```DivisionZeroError``` and just print out a warning. Note that the program no longer crashes: The exception is being raised, we intercept it and do something with it. In a more elaborate program we would then do some error handling and either continue with the code, or end the program in a controlled way if we find we cannot continue in a meaningful way.
Note that we intercepted the inception as a new variable: ```as e```. \
This allows us to work with and inspect this exception. Here we just print it and obtain the same information we had in the traceback earlier.
If we want to catch several types of exeption that we want to treat in the same way, we can do:
In many cases, we come to a point where we need to let the user know that a situation occurred that should not have occured. In the simplest way, we could write a ```print``` statement and print out an error message and then terminate the program. \
However, it would be better to pass an exception to the upstream code (e.g. the code calling a function or using a class) so they can inspect the error, handle it - or let the program crash if they do not.
To raise an exception, we use the keyword ```raise```.
Python already has a comprehensive list of [pre-defined exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions). If necessary, we can define our own as well.
***Example***
We define a function that divides two integer numbers. If the numbers are not integers, we raise an exception.
%% Cell type:code id: tags:
``` python
defmy_divide(a,b):
iftype(a)isnotintortype(b)isnotint:
raiseTypeError('At least one of the arguments is not an integer')
----> 3 raise TypeError('At least one of the arguments is not an integer')
4 return a/b
TypeError: At least one of the arguments is not an integer
%% Cell type:markdown id: tags:
Here, we can see a more elaborate traceback.
We note:
* We have augmented the pre-defined exception ```TypeError``` with an optional message that we can pass along to give more information about the exception or why we have raised it. It is a ```TypeError```, because we only want our function to work on specific types and one of the arguments is not of the specific type.
* The Tracebak shows the line that lead to the error
```
---> 11 result = my_divide('three',5)
```
and then the line where the error actually occured. If we have more functions or classes, then we would have a longer traceback, going through all steps.
We now use the try-except clause to intercept this case:
%% Cell type:code id: tags:
``` python
defmy_divide(a,b):
iftype(a)isnotintortype(b)isnotint:
raiseTypeError('At least one of the arguments is not an integer')
returna/b
# Avoid the crash:
try:
new_result=my_divide('three',5)
print('The result is: {}'.format(new_result))
exceptTypeErrorase:
print('Caught an exception: {}'.format(e))
print('The result is: {}'.format(new_result))
```
%% Output
Caught an exception: At least one of the arguments is not an integer
---> 14 print('The result is: {}'.format(new_result))
NameError: name 'new_result' is not defined
%% Cell type:markdown id: tags:
Now we note that the ```new_result``` is not defined outside the block because it was used inside the ```try``` block.
Either we better create the variable first (like below) - or we need to stop the program gracefully if we intend to continue with the result but cannot do so without it.
%% Cell type:code id: tags:
``` python
result=0
try:
result=my_divide('three',5)
print('The result is: {}'.format(result))
exceptTypeErrorase:
print('Caught an exception: {}'.format(e))
print('The result is: {}'.format(result))
```
%% Output
Caught an exception: At least one of the arguments is not an integer
The result is: 0
%% Cell type:markdown id: tags:
Now result is defined - but we need to make sure that the default value is useful. Since we have now intercepted the exception, the code no longer stops. If ```result = 0``` is not useful, we need to handle this case now as well...