10000 feat(errors): add error handling lecture with best practice on using … · eknathk/complete-python-course@ff61b34 · GitHub
[go: up one dir, main page]

Skip to content

Commit ff61b34

Browse files
committed
feat(errors): add error handling lecture with best practice on using 'else'
1 parent 43ae7eb commit ff61b34

File tree

3 files changed

+254
-4
lines changed

3 files changed

+254
-4
lines changed

course_contents/5_errors/README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
---
2-
group: Introduction
3-
hidden: true
4-
---
51
# Errors in Python
62

73
In this section we'll learn about errors and exceptions in Python. We can use them for flow control by following the "ask for forgiveness rather than permission" part of the Python ethos.
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
---
2+
description: Review your knowledge of error handling with Python via a number of examples.
3+
sidebar_label: "S05 L07: Handling User Errors Quiz"
4+
---
5+
6+
# Handling User Errors Quiz
7+
8+
This lecture follows a quiz in the course that asks several questions about error handling. Let's take a moment to go over the quiz questions and their answers!
9+
10+
## Question 1
11+
12+
A quick question to warm you up!
13+
14+
Ben and Dan are two novice software developers. They are trying to build a function that asks user for a number and calculate its power of 2. Here is their first version of code:
15+
16+
```python
17+
def power_of_two():
18+
n = input('Please enter a number: ')
19+
n_square = n ** 2
20+
return n_square
21+
```
22+
23+
What do you think of the code, will it work? Before submitting your answer, think about why.
24+
25+
### Answer
26+
27+
The answer here is that this code will never work, because the `input()` function always returns a string, and you can't square a string.
28+
29+
## Question 2
30+
31+
In this second question, Ben and Dan improve the code slightly:
32+
33+
```python
34+
def power_of_two():
35+
user_input = input('Please enter a number: ')
36+
n = float(user_input)
37+
n_square = n ** 2
38+
return n_square
39+
```
40+
41+
### Answer
42+
43+
Now the code will work on some inputs, but not others. That's because if the user decides to not enter a number (e.g. a letter), the program will crash.
44+
45+
## Question 3
46+
47+
Now, Ben and Dan add some error handling:
48+
49+
```python
50+
def power_of_two():
51+
user_input = input('Please enter a number: ')
52+
try:
53+
n = float(user_input)
54+
except ValueError:
55+
print('Your input was invalid.')
56+
finally:
57+
n_square = n ** 2
58+
return n_square
59+
```
60+
61+
Dan wants to test this function with some different inputs when the program asks for a number. He decides to test with two cases. First case: he enters `4`, which is a valid numeric input. Second case: he enters `'dan'` which is an invalid input.
62+
63+
What do you expect the function to return with Dan's input (Denoted as `[4, 'dan']`)? If the program breaks due to an error, we mark the function's return value as `Error`, and if it does not return anything, we mark the return value as `None`.
64+
65+
This one's a bit of a tricky one! Type the code out and try it in your editor for optimal chances of getting it right!
66+
67+
Potential answers:
68+
69+
- `[16.0, None]`
70+
- `[Error, Error]`
71+
- `[16.0, 0.0]`
72+
- `[16.0, Error]`
73+
- None of the above
74+
75+
### Answer
76+
77+
The `finally` block gets executed regardless of the occurrence of `ValueError`, so the correct answer is `[16.0, Error]`. We only defined `n` in the try block. If the input was invalid, `n` never gets its value assigned, and thus will raise an error when we try to access it in the `finally` block.
78+
79+
## Question 4
80+
81+
As Dan found the error in the code, he decided to make the following change:
82+
83+
```python
84+
def power_of_two():
85+
user_input = input('Please enter a number: ')
86+
try:
87+
n = float(user_input)
88+
except ValueError:
89+
print('Your input was invalid. Using default value 0')
90+
n = 0
91+
else:
92+
n_square = n ** 2
93+
return n_square
94+
```
95+
96+
Dan wants to test with the same test cases from last time: `[4, 'dan']`.
97+
98+
What do you expect the function to return with the two inputs (denoted as `[4, 'dan']`) this time? If the program breaks due to an error, we mark the function's return value as `Error`, and if it does not return anything, we mark the return value as `None`.
99+
100+
### Answer
101+
102+
This one's tricky! The `else` block only gets executed if no error occurs in the `try` block. So the code will finish after assigning `n` with the default value `0` if it ever reaches the `except` block. Since there is no `return` statement other than in the `else` clause, the function returns `None`.
103+
104+
## Question 5
105+
106+
Now that Dan's code still cannot work, Ben insisted on using a finally block, here is his code:
107+
108+
```python
109+
def power_of_two():
110+
user_input = input('Please enter a number: ')
111+
try:
112+
n = float(user_input)
113+
except ValueError:
114+
print('Your input was invalid. Using default value 0')
115+
n = 0
116+
else:
117+
n_square = n ** 2
118+
finally:
119+
return n_square
120+
```
121+
122+
They decided to use the same test cases: `[4, 'dan']`.
123+
124+
What do you expect the function to return with the two inputs (denoted as `[4, 'dan']`) this time? If the program breaks due to an error, we mark the function's return value as `Error`, and if it does not return anything, we mark the return value as `None`.
125+
126+
Potential answers:
127+
128+
- `[16.0, None]`
129+
- `[Error, Error]`
130+
- `[16.0, 0.0]`
131+
- `[16.0, Error]`
132+
- None of the above.
133+
134+
### Answer
135+
136+
In this case, the answer is `[16.0, Error]`. The `finally` block gets executed regardless of the occurrence of `ValueError`, while the `else` block only gets executed if no error occurs in the `try` block. However, we only defined `n_square` in the `else` block. If the input was invalid, `n_square` never gets its value assigned, thus will raise an error when we try to access it in the `finally` block.
137+
138+
## Question 6
139+
140+
Ben and Dan just won't give up, here's their last try:
141+
142+
```python
143+
def power_of_two():
144+
user_input = input('Please enter a number: ')
145+
try:
146+
n = float(user_input)
147+
except ValueError:
148+
print('Your input was invalid. Using default value 0')
149+
return 0
150+
finally:
151+
n_square = n ** 2
152+
return n_square
153+
```
154+
155+
They decided to use the same test cases: `[4, 'dan']`.
156+
157+
What do you expect the function to return with the two inputs (denoted as `[4, 'dan']`) this time? If the program breaks due to an error, we mark the function's return value as `Error`, and if it does not return anything, we mark the return value as `None`.
158+
159+
Potential answers:
160+
161+
- `[16.0, None]`
162+
- `[16.0, 0.0]`
163+
- `[16.0, 0]`
164+
- `[16.0, Error]`
165+
- None of the above.
166+
167+
### Answer
168+
169+
The answer is `[16.0, Error]`. It's a tricky one. The `finally` block will get executed regardless of the `return` statement in both `try` block and `except` block. However, we only defined `n` in the `try` block. If the input was invalid, `n` never gets its value assigned, thus will raise an error when we try to access it in the `finally` block.
170+
171+
## Improvements to the code
172+
173+
This is the latest code that Ben and Dan wrote. As you know, it still has some issues!
174+
175+
```python
176+
def power_of_two():
177+
user_input = input('Please enter a number: ')
178+
try:
179+
n = float(user_input)
180+
except ValueError:
181+
print('Your input was invalid. Using default value 0')
182+
return 0
183+
finally:
184+
n_square = n ** 2
185+
return n_square
186+
```
187+
188+
To fix this, we need to use the `else` clause!
189+
190+
First, let's think about what parts of the code can raise an exception. In our case, that's converting the user input to a float.
191+
192+
That line is already inside the `try` block, so that's good!
193+
194+
```python
195+
def power_of_two():
196+
user_input = input('Please enter a number: ')
197+
try:
198+
n = float(user_input)
199+
```
200+
201+
Next up, let's write what should happen if there is no error:
202+
203+
```python
204+
def power_of_two():
205+
user_input = input('Please enter a number: ')
206+
try:
207+
n = float(user_input)
208+
else:
209+
n_square = n ** 2
210+
return n_square
211+
```
212+
213+
Doing it this way is not absolutely necessary, of course, and you may like writing the `except` part first. It's up to you.
214+
215+
But now we've got the "happy path" of our code, or what happens if there are no errors.
216+
217+
So let's handle any errors that come up. In our case that's just `ValueError`, so let's add an `except` clause for that.
218+
219+
```python
220+
def power_of_two():
221+
user_input = input('Please enter a number: ')
222+
try:
223+
n = float(user_input)
224+
except ValueError:
225+
print('Your input was invalid. Using default value 0')
226+
return 0
227+
else:
228+
n_square = n ** 2
229+
return n_square
230+
```
231+
232+
So all that was needed in the end was to change the `finally` to an `else`, because there wasn't anything that we needed to run _no matter what_.
233+
234+
A small improvement we might do is change the `return 0` for `return 0.0`, so that the function always returns a `float`:
235+
236+
```python
237+
def power_of_two():
238+
user_input = input('Please enter a number: ')
239+
try:
240+
n = float(user_input)
241+
except ValueError:
242+
print('Your input was invalid. Using default value 0')
243+
return 0.0
244+
else:
245+< 741A /span>
n_square = n ** 2
246+
return n_square
247+
```
248+
249+
That way the caller of `power_of_two()` can always expect a `float` to come back, and not "sometimes a `float`, sometimes an `int`". Consistency helps!

sidebars.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ const sidebars = {
2727
// },
2828
],
2929
},
30+
{
31+
type: "category",
32+
label: "Errors in Python",
33+
items: ["errors/lectures/handling_user_errors_exercise/README"],
34+
},
3035
],
3136
};
3237

0 commit comments

Comments
 (0)
0