hayaliv / Hi! Python / Level 2
Exceptions
try/except lets a program handle a known error on purpose. Catch specific exceptions and raise your own when data is bad.
Think of it like this
An exception is like a smoke alarm in a kitchen. When something goes wrong, the alarm sounds and normal cooking stops. try/except is the cook who hears the alarm and deals with that exact problem.
Where the analogy stops being true. A smoke alarm only warns you, while Python stops the normal flow unless some except block handles the exception.
What to know
- Put risky code in a try block and handle a named exception in an except block.
- Name the specific exception you expect, such as KeyError or ValueError.
- The else block runs after the try block finishes without raising an exception.
- The finally block runs whether or not an exception happened.
- Use raise to signal bad data yourself, as check_label does for an unknown label.
- json.loads raises json.JSONDecodeError for text that is not valid JSON. It is a subclass of ValueError, and the example lists it first.
- Make your own exception by writing a class that derives from Exception, as the docs recommend.
- A bare except: hid a misspelled name in the third predict item. Naming the exception avoids that.
Example
import json
VALID = {"meeting_request", "not_interested"}
def check_label(label):
if label not in VALID:
raise ValueError(f"bad label: {label!r}")
return label
for line in ['{"label": "meeting_request"}', '{"label": "spam"}', "not json", "{}"]:
try:
label = check_label(json.loads(line)["label"])
except json.JSONDecodeError:
print("bad json")
except KeyError:
print("missing label")
except ValueError as err:
print("invalid:", err)
else:
print("ok:", label)
finally:
print("done")
It prints:
ok: meeting_request
done
invalid: bad label: 'spam'
done
bad json
done
missing label
done
Predict, then check
1. What does this print?
try:
print("a")
x = {"k": 1}["z"]
print("b")
except KeyError:
print("c")
finally:
print("d")Show the answer
a
c
dThe KeyError skips the rest of the try block, so b never prints. The except block prints c, then finally prints d.
2. What does this print?
class EmptyReply(Exception):
pass
def need_text(t):
if not t:
raise EmptyReply("no text")
return t
try:
need_text("")
except EmptyReply as err:
print("caught", err)Show the answer
caught no textneed_text raises EmptyReply because an empty string is false. The except block catches it and prints its message.
3. What does this print? (Look at the spelling inside try.)
def get_label():
try:
return lable
except:
return "fallback"
print(get_label())Show the answer
fallbackThe name lable is a typo, so Python raises NameError. The bare except hides it and returns the fallback, so the bug stays invisible.
Try it
Write parse_label(line). The line is a JSON text like '{"label": "meeting_request"}'. Return the label if it is in VALID. Return None if the text is not valid JSON. Raise ValueError if the label is not in VALID.
import json
VALID = {"meeting_request", "not_interested"}
def parse_label(line):
return None
Show a solution
import json
VALID = {"meeting_request", "not_interested"}
def parse_label(line):
try:
data = json.loads(line)
except json.JSONDecodeError:
return None
label = data["label"]
if label not in VALID:
raise ValueError(f"bad label: {label!r}")
return labelCommon mistake
A bare except: can hide a typo. In the third predict item, a misspelled name raised NameError and the bare except turned it into a quiet "fallback". Name the exception you expect instead.
In a small-language-model project
A real labelled dataset often has broken lines or labels you did not plan for. A good habit is to catch the specific errors, skip or report the bad row, and let unexpected errors stop the run so you notice them.
Sources
Each quote below appears word for word on the cited page of the official Python documentation (version 3.12).
It is useful for code that must be executed if the try clause does not raise an exception.
docs.python.org/3.12/tutorial/errors.html — The else clause runs if the try clause does not raise an exception.The finally clause runs whether or not the try statement produces an exception.
docs.python.org/3.12/tutorial/errors.html — The finally clause runs whether or not an exception occurred.The raise statement allows the programmer to force a specified exception to occur.
docs.python.org/3.12/tutorial/errors.html — raise forces a specified exception to occur.it is good practice to be as specific as possible with the types of exceptions that we intend to handle
docs.python.org/3.12/tutorial/errors.html — The docs advise being specific about which exceptions you handle.Exceptions should typically be derived from the Exception class, either directly or indirectly.
docs.python.org/3.12/tutorial/errors.html — Custom exceptions should derive from Exception.Raised when a mapping (dictionary) key is not found in the set of existing keys.
docs.python.org/3.12/library/exceptions.html — KeyError is raised when a mapping key is not found.If the data being deserialized is not a valid JSON document, a JSONDecodeError will be raised.
docs.python.org/3.12/library/json.html — json.loads raises JSONDecodeError for an invalid JSON document.Subclass of ValueError with the following additional attributes
docs.python.org/3.12/library/json.html — json.JSONDecodeError is a subclass of ValueError.Raised when a local or global name is not found.
docs.python.org/3.12/library/exceptions.html — NameError is raised when a name is not found.