14.5: Raising Exceptions
- Page ID
- 117617
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\dsum}{\displaystyle\sum\limits} \)
\( \newcommand{\dint}{\displaystyle\int\limits} \)
\( \newcommand{\dlim}{\displaystyle\lim\limits} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\(\newcommand{\longvect}{\overrightarrow}\)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)By the end of this section you should be able to
- Use a
raisestatement to indicate that user input is invalid. - Explain the flow of execution when an exception is raised.
The raise statement
A program can raise an exception when an error is detected. Raising an exception forces a program to deal with the error. If the exception is not handled using try and except, the program displays an error message and terminates.
The following class represents a pizza for sale. The SIZES dictionary maps an abbreviation to a size's name and price. Line 11 checks if the size received by the constructor is in the dictionary. Line 12 raises a ValueError with specific error message.
Large pizza for $9.99
Traceback (most recent call last):
File "pizza.py", line 23, in <module>
p2 = Pizza("Z")
File "pizza.py", line 12, in __init__
raise ValueError(f"Unknown size '{size}'")
ValueError: Unknown size 'Z'
Raising an exception in a constructor prevents an invalid object from being constructed. In the example, p1 is constructed but p2 is not constructed.
For each code snippet, what is the output?
|
1 |
class Pizza: |
|
2 |
|
|
3 |
SIZES = { |
|
4 |
"S": ("Small", 5.99), |
|
5 |
"M": ("Medium", 7.99), |
|
6 |
"L": ("Large", 9.99), |
|
7 |
"XL": ("X-Large", 11.99), |
|
8 |
} |
|
9 |
|
|
10 |
def __init__(self, size): |
|
11 |
if size not in Pizza.SIZES: |
|
12 |
raise ValueError(f"Unknown size '{size}'") |
|
13 |
self.size = size |
|
14 |
|
|
15 |
def __str__(self): |
|
16 |
name, price = Pizza.SIZES[self.size] |
|
17 |
return f"{name} pizza for ${price}" |
|
18 |
|
|
19 |
if __name__ == "__main__": |
|
20 |
p1 = Pizza("L") |
|
21 |
print(p1) |
|
22 |
p2 = Pizza("Z") |
|
23 |
print(p2) |
Medium pizza for $7.99ValueError: Unknown size 'M'TypeError: missing argument
- Answer
-
a.
"M"is a valid size, so thePizzaobject is constructed.
raise PizzaError("Invalid size")
Invalid sizePizzaError: Invalid sizeNameError: name 'PizzaError' is not defined
- Answer
-
c.
PizzaErroris not a built-in exception type.
raise ValueError
ValueErrorNameError: name 'ValueError' is not definedSyntaxError: Missing parenthesis in call to 'ValueError'
- Answer
-
a. An exception can be raised without providing an error message. In this case, the error message is empty, but the exception name is still printed.
The file top27.csv is based on Wikipedia's list of languages by number of native speakers. A linguist would like to work with this data in an object-oriented way and has already defined a Language class. However, the code is not working correctly because of errors in the CSV file. (The CSV program cannot be changed because the program's purpose is to detect these kinds of errors.)
Your task is to modify the constructor to raise a ValueError in the following situations:
- If the line does not have exactly four values (separated by commas).
- If the number of speakers is not a positive floating point number.
Note: The provided top27.csv includes mistakes for English, Korean, and French. Your program should raise a ValueError only for these languages. No other errors (including IndexError) should be raised.
Interactive Code
- top27.csv
-
Language,Native speakers (millions),Language family,Branch
Mandarin Chinese (incl. Standard Chinese but excl. other varieties),939,Sino-Tibetan,Sinitic
Spanish,485,Indo-European,Romance
English,380+,Indo-European,Germanic
Hindi (excl. Urdu and other languages),345,Indo-European,Indo-Aryan
Portuguese,236,Indo-European,Romance
Bengali,234,Indo-European,Indo-Aryan
Russian,147,Indo-European,Balto-Slavic
Japanese,123,Japonic,Japanese
Yue Chinese (incl. Cantonese),86.1,Sino-Tibetan,Sinitic
Vietnamese,85,Austroasiatic,Vietic
Turkish,84,Turkic,Oghuz
Wu Chinese (incl. Shanghainese),83.4,Sino-Tibetan,Sinitic
Marathi,83.2,Indo-European,Indo-Aryan
Telugu,83,Dravidian,South-Central
Korean,81.7,Koreanic
French,-80.8,Indo-European,Romance
Tamil,78.6,Dravidian,South
Egyptian Spoken Arabic (excl. Saidi Arabic),77.4,Afroasiatic,Semitic
Standard German,75.3,Indo-European,Germanic
Urdu (excl. Hindi),70.6,Indo-European,Indo-Aryan
Javanese,68.3,Austronesian,Malayo-Polynesian
Western Punjabi (excl. Eastern Punjabi),66.7,Indo-European,Indo-Aryan
Italian,64.6,Indo-European,Romance
Gujarati,57.1,Indo-European,Indo-Aryan
Iranian Persian (excl. Dari and Tajik),57.2,Indo-European,Iranian
Bhojpuri,52.3,Indo-European,Indo-Aryan
Hausa,51.7,Afroasiatic,Chadic - Answer
-
class Language:
def __init__(self, line):
data = line.split(',')
if len(data) != 4:
raise ValueError("incorrect number of values")
self.name = data[0]
self.speakers = float(data[1])
if self.speakers < 0:
raise ValueError("speakers cannot be negative")
self.family = data[2]
self.branch = data[3]def __str__(self):
return f"{self.name} ({self.speakers} million)"if __name__ == "__main__":
valid = []
data = open("top27.csv")
data.readline() # Skip first line
for line in data:
try:
lang = Language(line)
valid.append(lang)
except ValueError as err:
print(err)
print(line)
for lang in valid:
print(lang)
When looking up a value in a dictionary, a key has to match exactly. Ex: Given fruit = {"apple": 1, "banana": 2}, the expression fruit["Apple"] raises a KeyError because "Apple" is not the same as "apple". Likewise, fruit["NANA"] raises a KeyError because "nana" does not exactly match "banana".
Implement the lookup() function. This function tries to find a key in the dictionary that matches the search term. A key matches if the search term is a substring of the key, ignoring case. Ex: The key "banana" matches the search term "NANA".
If no matching keys are found, lookup() should raise a KeyError with the message "search term not found". If multiple matches are found, lookup() should raise a KeyError with the message "multiple keys found". Otherwise, lookup() should return both the key and the value for the matching item in the dictionary.
Given the books.csv file, the output should be:
Found: ('Pride and Prejudice', 9780141439518)
Found: ('The Hobbit', 9780547928227)
Error: search term not found
Error: multiple keys found
Interactive Code
- books.csv
-
The Fault in Our Stars,9780142424179
To Kill a Mockingbird,9780061120084
Harry Potter and the Sorcerer's Stone,9780545010221
The Catcher in the Rye,9780316769488
The Alchemist,9780062315007
Pride and Prejudice,9780141439518
The DaVinci Code,9781400079179
Fahrenheit 451,9781451673319
Where the Sidewalk Ends,9780060256678
The Hobbit,9780547928227 - Answer
-
def lookup(dictionary, search_term):
search_term = search_term.lower()
hits = []
for key in dictionary:
if search_term in key.lower():
hits.append(key)
if len(hits) == 0:
raise KeyError("search term not found")
if len(hits) > 1:
raise KeyError("multiple keys found")
key = hits[0]
return key, dictionary[key]if __name__ == "__main__":# Read books from the file
books = {}
for line in open("books.csv"):
title, isbn = line.split(",")
books[title] = int(isbn)# Try looking up some books
for term in ["PREJ", "hob", "1984", "The"]:
try:
result = lookup(books, term)
print("Found:", result)
except KeyError as err:
print("Error:", err)


