Skip to content

Commit 9fe8ede

Browse files
authored
Merge pull request #12 from Warwick-xSoc/w6-except-theres-excpetions
Adds a section at the start of Week 6 on recursion.
2 parents 2a1d6da + 0ff532b commit 9fe8ede

File tree

1 file changed

+187
-3
lines changed

1 file changed

+187
-3
lines changed

week6/w6.ipynb

Lines changed: 187 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,190 @@
1818
"---"
1919
]
2020
},
21+
{
22+
"cell_type": "markdown",
23+
"metadata": {},
24+
"source": [
25+
"## Expect Everything\n",
26+
"Throughout the course, you may have run into an error message or two (or three or...). In Python, we typically refer to these as **Exceptions**.\n",
27+
"\n",
28+
"It would be really inconvenient if all programs, ever, crashed the first time they ran into a problem. Luckily, we can write code to deal with these exceptions, should they arise - this is **Exception Handling**. For example, look at this short script that takes an input, converts it to an `int` and doubles it:"
29+
]
30+
},
31+
{
32+
"cell_type": "code",
33+
"execution_count": null,
34+
"metadata": {},
35+
"outputs": [],
36+
"source": [
37+
"num = int(input(\"Enter a number: \"))\n",
38+
"num *= 2\n",
39+
"print(num)"
40+
]
41+
},
42+
{
43+
"cell_type": "markdown",
44+
"metadata": {},
45+
"source": [
46+
"Seems fine? But what if the user decides they *don't* want to enter a number, what if, e.g., they enter \"Hello\"? Python doesn't know how to convert a string to an integer, so it will throw a **ValueError** and exit the program.\n",
47+
"\n",
48+
"In reality, entering the wrong type of data isn't the end of the world. To stop Python from exiting immeadiately, we can surround any potentially problem-causing code in a `try-expect` block like this:"
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": null,
54+
"metadata": {},
55+
"outputs": [],
56+
"source": [
57+
"try:\n",
58+
" num = int(input(\"Enter a number\"))\n",
59+
" num *= 2\n",
60+
" print(num)\n",
61+
"except ValueError:\n",
62+
" print(\"uh oh - that wasn't a number\")"
63+
]
64+
},
65+
{
66+
"cell_type": "markdown",
67+
"metadata": {},
68+
"source": [
69+
"The `try` statement tells the Python interpreter that something could go wrong in the following statements, but we know and have ways to deal with it. \n",
70+
"\n",
71+
"Note: we need `num *= 2` and `print(num)` in the `try` block because if the user inputs something wrong, `num` won't be defined. If the statements were *after* the try statement, this could cause an error as `num` wouldn't be defined.\n",
72+
"\n",
73+
"We can also catch multiple types of exceptions!"
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"metadata": {},
80+
"outputs": [],
81+
"source": [
82+
"try:\n",
83+
" num = int(input(\"Enter a number\"))\n",
84+
" result = 2 / num\n",
85+
" print(result)\n",
86+
"except ValueError:\n",
87+
" print(\"uh oh - that wasn't a number\")\n",
88+
"except ZeroDivisionError:\n",
89+
" print(\"Friends don't let friends divide by zero\")"
90+
]
91+
},
92+
{
93+
"cell_type": "markdown",
94+
"metadata": {},
95+
"source": [
96+
"All exceptions are a type of class, all inheriting from the **Exception** class. Also, certain exceptions are also subclasses of other exceptions, e.g. `ZeroDivisionError` is a subclass of `ArithmeticError`, here's a full hierarchy: \n",
97+
"\n",
98+
"***Image of Exception hierarchy goes here***"
99+
]
100+
},
101+
{
102+
"cell_type": "markdown",
103+
"metadata": {},
104+
"source": [
105+
"So what if we were to do this:"
106+
]
107+
},
108+
{
109+
"cell_type": "code",
110+
"execution_count": null,
111+
"metadata": {},
112+
"outputs": [],
113+
"source": [
114+
"try:\n",
115+
" num = int(input(\"Enter a number\"))\n",
116+
" result = 2 / num\n",
117+
" print(result)\n",
118+
"except Exception:\n",
119+
" print(\"Something went wrong :(\")\n",
120+
"except ValueError:\n",
121+
" print(\"uh oh - that wasn't a number\")\n",
122+
"except ZeroDivisionError:\n",
123+
" print(\"Friends don't let friends divide by zero\")"
124+
]
125+
},
126+
{
127+
"cell_type": "markdown",
128+
"metadata": {},
129+
"source": [
130+
"Try running the above block and input a string. Now input 0. We've caused two different errors, but the same statement is being executed. Why?\n",
131+
"\n",
132+
"Since all exceptions are a subclass of **Exception**, Python considers them to be of type *Exception*. Why does this matter? Python executes the *first relevant* catch statement - in this case it will *always* be an Exception, so the first statement will execute *regardless* of what type of exception we're throwing. You need to make sure that you put any catch statements for subclasses *above* those of their parent classes, otherwise the subclasses will never execute:"
133+
]
134+
},
135+
{
136+
"cell_type": "code",
137+
"execution_count": null,
138+
"metadata": {},
139+
"outputs": [],
140+
"source": [
141+
"try:\n",
142+
" num = int(input(\"Enter a number\"))\n",
143+
" result = 2 / num\n",
144+
" print(result)\n",
145+
"except ValueError:\n",
146+
" print(\"uh oh - that wasn't a number\")\n",
147+
"except ZeroDivisionError:\n",
148+
" print(\"Friends don't let friends divide by zero\")\n",
149+
"except Exception:\n",
150+
" print(\"Something went wrong :(\")"
151+
]
152+
},
153+
{
154+
"cell_type": "markdown",
155+
"metadata": {},
156+
"source": [
157+
"Sometimes we may not want to continue the program if we encounter an error, if we want to throw an exception, we use the `raise` keyword. For example:"
158+
]
159+
},
160+
{
161+
"cell_type": "code",
162+
"execution_count": null,
163+
"metadata": {},
164+
"outputs": [],
165+
"source": [
166+
"def division(num1: int, num2: int) -> float:\n",
167+
" if (num2 == 0):\n",
168+
" raise ZeroDivisionError(\"We've been through this - you can't divide by zero\")\n",
169+
" else:\n",
170+
" return num1 / num2\n",
171+
"\n",
172+
"inp_1 = input(\"Enter first number: \")\n",
173+
"inp_2 = input(\"Enter second number: \")\n",
174+
"try:\n",
175+
" inp_1 = int(inp_1)\n",
176+
" inp_2 = int(inp_2)\n",
177+
"except ValueError:\n",
178+
" print(\"uh oh - one of those numbers wasn't a number\")\n",
179+
"\n",
180+
"print(division(inp_1, inp_2))"
181+
]
182+
},
183+
{
184+
"cell_type": "markdown",
185+
"metadata": {},
186+
"source": [
187+
"We can also write our own custom exceptions. We define them the same way we define a class, making sure to say it inherits from **Exception**. Then, we can call it anywhere in our code by using `raise`:"
188+
]
189+
},
190+
{
191+
"cell_type": "code",
192+
"execution_count": null,
193+
"metadata": {},
194+
"outputs": [],
195+
"source": [
196+
"class MyException(Exception):\n",
197+
" def __init__(self, msg):\n",
198+
" super().__init__(msg)\n",
199+
"\n",
200+
"thing = input(\"Enter your favourite society: \")\n",
201+
"if thing != \"UWCS\":\n",
202+
" raise MyException(\"Ding dong, your opinion is wrong\")"
203+
]
204+
},
21205
{
22206
"cell_type": "markdown",
23207
"metadata": {},
@@ -1005,7 +1189,7 @@
10051189
],
10061190
"metadata": {
10071191
"kernelspec": {
1008-
"display_name": "Python 3.10.4 64-bit",
1192+
"display_name": "Python 3.10.8 64-bit",
10091193
"language": "python",
10101194
"name": "python3"
10111195
},
@@ -1019,12 +1203,12 @@
10191203
"name": "python",
10201204
"nbconvert_exporter": "python",
10211205
"pygments_lexer": "ipython3",
1022-
"version": "3.10.7"
1206+
"version": "3.10.8"
10231207
},
10241208
"orig_nbformat": 4,
10251209
"vscode": {
10261210
"interpreter": {
1027-
"hash": "268a003517095b3e00ec49a0832a87899bac50083c754616205934d33472dfcd"
1211+
"hash": "bb33f6d328f18c07440802b8c66874c52744b86bff5cfe8eb1d71afeb55a2150"
10281212
}
10291213
}
10301214
},

0 commit comments

Comments
 (0)