try/except
are for interrupting normal program flow and continuing in surrounding contextExamples of exceptions resulting from programmer error (and therefore may not worth catching)
IndentationError
SyntaxError
NameError
convert
example
DIGIT_MAP = {
'zero': '0',
'one': '1',
'two': '2',
'three': '3',
'four': '4',
'five': '5',
'six': '6',
'seven': '7',
'eight': '8',
'nine': '9'
}
def convert(s):
number = ''
for token in s:
number += DIGIT_MAP[token]
x = int(number)
return x
>>> from exceptional import convert
>>> convert("one three three seven".split())
#=> 1337
>>> convert("around two grillion".split())
#=> throws KeyError
convert
example (v2.0)
import sys
DIGIT_MAP = {
'zero': '0',
'one': '1',
'two': '2',
'three': '3',
'four': '4',
'five': '5',
'six': '6',
'seven': '7',
'eight': '8',
'nine': '9'
}
def convert(s):
try:
number = ''
for token in s:
number += DIGIT_MAP[token]
return int(number)
except (KeyError, TypeError) as e:
# `!r` results in using repl representation of value in string
# stderr === standard error
print(f"Conversion error: {e!r}", file=sys.stderr)
return -1
pass
### BAD
def do_something(x):
try:
x += "meow"
except TypeError:
return x
### GOOD
def do_something(x):
try:
x += "meow"
except TypeError:
pass
return x
try...finally
Basic syntax:
try:
# try-block
finally:
# executed no matter how the try-block terminates
finally
Not-Exception-safe
import os
def make_at(path, dir_name):
original_path = os.getcwd()
os.chdir(path)
os.mkdir(dir_name)
os.chdir(original_path)
Cleans up from Exceptions
import os
import sys
def make_at(path, dir_name):
original_path = os.getcwd()
os.chdir(path)
try:
os.mkdir(dir_name)
finally:
os.chdir(original_path)
Extended with added except
block
import os
import sys
def make_at(path, dir_name):
original_path = os.getcwd()
os.chdir(path)
try:
os.mkdir(dir_name)
except OSError as e:
print(e, file=sys.stderr)
raise
finally: ###### NOTE: this block will still run, even if exception is raised
os.chdir(original_path)
β―οΈ Moment of Zen β―οΈ "Errors should never pass silently, unless explicitly silenced" Errors are like bells And if we make them silent They are of no use
π‘ IMPORTANT π‘ instead of returning error codes, just raise an exception π
convert
example (v3.0)
import sys
from math import log
DIGIT_MAP = {
'zero': '0',
'one': '1',
'two': '2',
'three': '3',
'four': '4',
'five': '5',
'six': '6',
'seven': '7',
'eight': '8',
'nine': '9'
}
def convert(s):
try:
number = ''
for token in s:
number += DIGIT_MAP[token]
return int(number)
except (KeyError, TypeError) as e:
# `!r` results in using repl representation of value in string
# stderr === standard error
print(f"Conversion error: {e!r}", file=sys.stderr)
raise
def string_log(s):
v = convert(s)
return log(v)
Whenever possible and sensible, use Standard Exception Types
ValueError
for arguments of the right type but with invalid valueraise ValueError()
to raise new ValueError
sqrt
exampleimport sys
def sqrt(x):
"""Compute square roots using the method of Heron of Alexandria.
Args:
x: The number for which the square root is to be computed.
Returns:
The square root of x.
"""
guess = x
i = 0
# catching `ZeroDivisionError` and raising as `ValueError`
try:
while guess * guess != x and i < 20:
guess = (guess + x / guess) / 2.0
i += 1
except ZeroDivisionError:
# this is wasteful since we KNOW that the function will fail if x is a negative number
raise ValueError()
return guess
def main():
try:
print(sqrt(9))
print(sqrt(2))
print(sqrt(-1)) # raises ZeroDivisionError
print("This is never printed.")
except ZeroDivisionError:
print("Cannot compute square root of a negative number.")
print("Program execution continues normally here.")
if __name__ == '__main__':
main()
import sys
def sqrt(x):
"""Compute square roots using the method of Heron of Alexandria.
Args:
x: The number for which the square root is to be computed.
Returns:
The square root of x.
Raises:
ValueError: If x is negative.
"""
if x < 0:
raise ValueError(f"Cannot compute square root of negative number {x}")
guess = x
i = 0
while guess * guess != x and i < 20:
guess = (guess + x / guess) / 2.0
i += 1
return guess
def main():
try:
print(sqrt(9))
print(sqrt(2))
print(sqrt(-1))
print("This is never printed.")
except ValueError as e:
print(e, file=sys.stderr)
print("Program execution continues normally here.")
if __name__ == '__main__':
main()