本文最后更新于 602 天前,其中的信息可能已经过时,如有错误请发送邮件到wuxianglongblog@163.com
异常与警告
异常
写代码的时候,出现错误必不可免。
看下面这段代码:
import math
while True:
    text = input('> ')
    if text[0] == 'q':
        break
    x = float(text)
    y = math.log10(x)
    print(f"log10({x}) = {y}")这段代码接收命令行的输入,输入为数字时,计算它的对数并输出,直到输入值为 q 为止。
乍看没什么问题,然而输入0或者负数时:
import math
while True:
    text = input('> ')
    if text[0] == 'q':
        break
    x = float(text)
    y = math.log10(x)
    print(f"log10({x}) = {y}")> -1
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [1], in | ()
      6     break
      7 x = float(text)
----> 8 y = math.log10(x)
      9 print(f"log10({x}) = {y}")
ValueError: math domain error | log10 函数会报错,因为不能接受非正值。
一旦报错,程序就会停止执行,如果不希望程序停止执行,那么可以添加一对 try & except:
import math
while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = math.log10(x)
        print(f"log10({x}) = {y}")
    except ValueError:
        print("the value must be greater than 0")> -1
the value must be greater than 0
> 0
the value must be greater than 0
> 1
log10(1.0) = 0.0
> q捕捉不同的异常类型
假设将这里的 y 更改为 1 / math.log10(x),此时输入 1:
import math
while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print(f"1 / log10({x}) = {y}")
    except ValueError:
        print("the value must be greater than 0")> 1
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Input In [3], in | ()
      7         break
      8     x = float(text)
----> 9     y = 1 / math.log10(x)
     10     print(f"1 / log10({x}) = {y}")
     11 except ValueError:
ZeroDivisionError: float division by zero | 程序仍然抛出了异常,原因是ZeroDivisionError不在可处理的异常中。
可以有两种方法处理这个问题,第一种是捕获异常的父类Exception:
import math
while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print(f"1 / log10({x}) = {y}")
    except Exception:
        print("invalid value")> 1
invalid value
> 0
invalid value
> -1
invalid value
> q第二种是指定多个错误类型:
import math
while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print(f"1 / log10({x}) = {y}")
    except (ZeroDivisionError, ValueError):
        print("invalid value")> 1
invalid value
> 0
invalid value
> -1
invalid value
> q还可以分开处理:
import math
while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print(f"1 / log10({x}) = {y}")
    except ValueError:
        print("the value must be greater than 0")
    except ZeroDivisionError:
        print("the value must not be 1")> -1
the value must be greater than 0
> 1
the value must not be 1
> 2
1 / log10(2.0) = 3.321928094887362
> q还可以将异常的具体信息打出来:
import math
while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print(f"1 / log10({x}) = {y}")
    except Exception as e:
        print(e)> -1
math domain error
> 0
math domain error
> 1
float division by zero
> abcde
could not convert string to float: 'abcde'
> q可以用raise主动抛出异常,例如判断月份是否在1-12之间:
month = 13
if not 1 <= month <= 12:
    raise ValueError(f"{month} must between 1 and 12!")---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [8], in | ()
      1 month = 13
      3 if not 1 <= month <= 12:
----> 4     raise ValueError(f"{month} must between 1 and 12!")
ValueError: 13 must between 1 and 12! | finally
try/catch 块还有一个可选的关键词 finally。
不管 try 块有没有异常, finally 块的内容总是会被执行,而且会在抛出异常前执行,因此可以用来作为安全保证,比如确保打开的文件被关闭:
try:
    print(1)
finally:
    print('finally was called.')1
finally was called.如果有异常被抛出,finally的部分会在抛出异常前执行:
try:
    print(1 / 0)
finally:
    print('finally was called.')finally was called.
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Input In [11], in | ()
      1 try:
----> 2     print(1 / 0)
      3 finally:
      4     print('finally was called.')
ZeroDivisionError: division by zero | 异常被处理了,则在最后执行:
try:
    print(1 / 0)
except Exception as e:
    print(e)
finally:
    print('finally was called.')division by zero
finally was called.