引发异常

已完成

现在,你已经充分了解了回溯和处理异常,接下来让我们了解引发异常。

你可能已经了解了在编写代码时可能会导致错误的情况。 在这些情况下,引发异常很有用,可以让其他代码能够认识到该问题。

引发异常还有助于对其他代码进行决策。 如前面所示,根据错误,代码可以做出明智的决策,以解决、绕开或忽略问题。

宇航员将其用水量限制为每日大约 11 升。 让我们创建一个函数,该函数根据宇航员的数目,可以计算一天或更长时间后剩余的水量:

def water_left(astronauts, water_left, days_left):
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    return f"Total water left after {days_left} days is: {total_water_left} liters"

试运行该函数,其中宇航员为 5 名,剩余水量 100 升,行程时间为 2 日:

water_left(5, 100, 2)
'Total water left after 2 days is: -10 liters'

这并不是很有用,因为水量的减少应该是一个错误。 然后,导航系统会提醒宇航员,两天后所有人都没有足够的剩余水量。 如果你是一位设计导航系统的工程师,你可能会在 water_left() 函数中引发异常,以提醒错误情况:

def water_left(astronauts, water_left, days_left):
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    if total_water_left < 0:
        raise RuntimeError(f"There is not enough water for {astronauts} astronauts after {days_left} days!")
    return f"Total water left after {days_left} days is: {total_water_left} liters"

现在再次运行:

water_left(5, 100, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in water_left
RuntimeError: There is not enough water for 5 astronauts after 2 days!

在导航系统中,发出警报信号的代码现在可以使用 RuntimeError 发出警报:

try:
    water_left(5, 100, 2)
except RuntimeError as err:
    alert_navigation_system(err)

water_left() 函数还可以更新以防止传递不受支持的类型。 尝试传递非整数的参数以检查错误输出:

water_left("3", "200", None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in water_left
TypeError: can't multiply sequence by non-int of type 'NoneType'

TypeError 中的错误在该函数预期的上下文中不太易懂。 更新函数,使其使用 TypeError,但提供更优化的消息:

def water_left(astronauts, water_left, days_left):
    for argument in [astronauts, water_left, days_left]:
        try:
            # If argument is an int, the following operation will work
            argument / 10
        except TypeError:
            # TypeError will be raised only if it isn't the right type 
            # Raise the same exception but with a better error message
            raise TypeError(f"All arguments must be of type int, but received: '{argument}'")
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    if total_water_left < 0:
        raise RuntimeError(f"There is not enough water for {astronauts} astronauts after {days_left} days!")
    return f"Total water left after {days_left} days is: {total_water_left} liters"

现在重试以获得优化错误:

water_left("3", "200", None)
Traceback (most recent call last):
  File "<stdin>", line 5, in water_left
TypeError: unsupported operand type(s) for /: 'str' and 'int'

During handling of the preceding exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in water_left
TypeError: All arguments must be of type int, but received: '3'