# Python教程 - 10 异常

# 10.1 异常的概念

什么是异常?

异常也就是程序执运行时发生的错误。

例如:我们执行 1除以0,0是不能作为除数的。

print(1 / 0)
1

执行的时候就会发生错误:

例如,以读的方式打开一个不存在的文件

file = open("test.txt", "r")
1

执行的时候也会报错:

当程序出现错误的时候,我们通常称之为:抛出异常。

程序抛出异常就无法继续执行了,但是任何程序都不可能是完美的没有bug的,只能尽可能的对错误进行预防和处理。

# 10.2 捕获异常

对异常进行预防和提前处理,这种行为通常称之为异常捕获。

一个程序出现任何错误,就停止运行肯定不是我们希望看到的,即使程序出现错误,哪怕给与用户一个错误提示,也是一种积极的处理方式。

# 1 异常捕获语法

最简单的异常捕获语法:

try:
    可能出现错误的代码
  except [Exception as e]:
    出现错误后的处理
1
2
3
4

举个栗子:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))

except:
    print("请输入正确的数字!")
1
2
3
4
5
6

执行结果:

请输入数字:abc 请输入正确的数字!

这样在发生错误的时候,可以给用户一个提示。当然具体的处理方式需要根据业务需求的处理,这里只是举个例子。

如果想获取异常的信息,可以使用 except Exception as e: :

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))

except Exception as e:
    print(e)
    print("请输入正确的数字!")
1
2
3
4
5
6
7

# 2 捕获指定类型的异常

在程序运行时,可能会出现不同类型的异常,可能需要对不同类型的异常进行不同的处理,这个时候就需要根据类型来捕获异常了。

语法:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))

except ValueError as e:
    # 针对ValueError错误进行处理
    print(e)
    print("请输入正确的数字!")
1
2
3
4
5
6
7
8

上面捕获了ValueError类型的异常,在捕获异常后,打印了异常信息和提示信息。

如果不想获取异常信息,还可以省略 as :

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))

except ValueError:
    print("请输入正确的数字!")
1
2
3
4
5
6

需要注意,上面只是捕获了ValueError类型的异常,如果有代码抛出了其他类型的异常,是无法捕获的。

例如,我们修改代码如下:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
    print(12 / num)

except ValueError:
    print("请输入正确的数字!")
1
2
3
4
5
6
7

当输入0的时候,执行结果:

无法捕获除数为0的异常ZeroDivisionError。

# 3 捕获多个异常

我们可以同时捕获多个异常,并对。

语法:

try:
    # 可能出现错误的代码
except 错误类型1:
    # 捕获错误类型1
except 错误类型2:
    # 捕获错误类型2
except (错误类型3, 错误类型4):
    # 可以同时捕获错误类型3和错误类型4
except Exception as e:
    # 捕获其他类型
    print("未知错误:%s" % e)
1
2
3
4
5
6
7
8
9
10
11

如果真多多种异常类型采用相同的处理可以使用 except (错误类型3, 错误类型4): 这样的捕获方式。

如果我们不能判断是否还会抛出其他类型的错误,可以在最后添加 except Exception as e: 用来捕获其他类型的错误。

举个栗子:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
    print(12 / num)

except ValueError:
    print("请输入正确的数字!")
except ZeroDivisionError:
    print("除数不能为0")
except Exception as e:
    # 捕获其他类型
    print("未知错误:%s" % e)
1
2
3
4
5
6
7
8
9
10
11
12

# 4 异常else

else表示的是如果没有发生异常要执行的代码。

语法:

try:
    # 可能出现错误的代码
except Exception as e:
    # 捕获异常
else:
    # 没有发生异常要执行的代码
1
2
3
4
5
6

举个栗子:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
    print(12 / num)
    
except Exception as e:
    print(e)
else:
    print("没有发生异常")
1
2
3
4
5
6
7
8
9

输入12,执行结果:

请输入数字:12 1.0 没有发生异常

输入0,执行结果:

请输入数字:0 division by zero

# 5 异常finally

finally表示的是无论是否发生异常都要执行的代码。

举个栗子:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
    print(12 / num)

except Exception as e:
    print(e)
finally:
    print("执行完成")
1
2
3
4
5
6
7
8
9

当输入0的时候,执行结果:

请输入数字:0 division by zero 执行完成

finally一般在文件操作的时候用的比较多,就是不管是否发生错误都要关闭文件。

举个栗子:

try:
    f = open('test.txt', 'r')
except Exception as e:
    print(e)
finally:
    try:
        f.close()
    except Exception as e:
        print("关闭文件出错:%s" % e)
1
2
3
4
5
6
7
8
9

因为关闭文件也很有可能报错,所以在关闭文件的时候,也进行了异常捕获

# 6 异常捕获的完整语法

将之前的异常捕获、else 和 finally 都添加上,完整的异常捕获代码如下:

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
    print(12 / num)

except ValueError:
    print("请输入正确的整数!")
except ZeroDivisionError:
    print("除数不能为0")
except Exception as result:
    print("未知错误:%s" % result)
else:
    print("程序正常执行")
finally:
    print("执行完成")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

当然也不是所有的模块都需要,按照自己需要使用即可。

# 10.3 异常的传递

什么是异常的传递?

异常的传递,就是当函数或方法执行的时候出现异常,如果没有进行捕获,就会将异常传递给该函数或方法的调用者,如果调用者仍未处理异常,则继续向上传递,直到传递给主程序,如果主程序仍然没有进行异常处理,则程序将被终止。

举个栗子:

def input_fun():
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
    print(12 / num)


def call_fun():				# 调用上面的input_fun()
    input_fun()


def main():						# 调用上面的call_fun()
    try:
       call_fun()
    except Exception as e:
        print(e)


main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

上面代码,执行的时候输入0,会在input_fun() 函数中抛出异常,因为input_fun()函数中没有进行异常处理,则异常会抛给call_fun()函数,call_fun()没有进行异常处理,则抛给main()函数,main()函数中则对异常进行了处理,程序不会崩溃。

利用异常的传递性,如果我们在main()函数中进行了异常捕获,无论程序哪里发生了错误,最终都会被传递到main()函数中,保证所有的异常都会被捕获。

# 10.4 主动抛出异常

除了代码执行的时候出错,Python解释器会抛出异常,我们还可以根据实际的业务需要,主动抛出异常。

首先创建一个异常对象,然后使用 raise 关键字抛出异常对象。

举个栗子:

def input_username():
    # 输入密码
    name = input("请输入用户名:")

    if len(name) > 16 or len(name) < 8:
        raise Exception("用户名格式错误")			# 主动抛出异常

    return name


try:
    password = input_username()
    print(f"输入的用户名:{password}")
except Exception as e:
    print("用户名格式错误,请重新输入")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

当输入abc的时候,执行结果:

请输入用户名:abc 用户名格式错误,请重新输入

# 10.5 自定义异常类

上面我们使用了Python中存在的异常类,我们还可以自定义异常类。

自定义异常类需要继承Exception类,类名建议以Error结尾。

举个栗子:

下面我们首先自定义了两个异常类,然后根据需要抛出这两个异常类的对象,在捕获异常的时候,根据异常类型进行不同的处理。

并在自定义异常类中可以封装自定义的参数。

class UsernameEmptyError(Exception):		# 自定义异常类
    pass


class UsernameLengthError(Exception):		# 自定义异常类

    def __init__(self, message, min_length, max_length):
        super().__init__(self, message)     # 推荐先调用父类的方法
        
        self.min_length = min_length        # 可以封装自定义的信息
        self.max_length = max_length


def input_username():
    # 输入密码
    name = input("请输入用户名:")

    if len(name) < 1:
        raise UsernameEmptyError()								  				# 抛出异常

    if len(name) > 16 or len(name) < 8:
        raise UsernameLengthError("用户名格式错误", 8, 16)			# 抛出异常

    return name


try:
    password = input_username()
    print(f"输入的用户名:{password}")
except UsernameEmptyError:
    print("用户名不能为空")
except UsernameLengthError as e:
    print(f"用户名长度为{e.min_length}~{e.max_length}个字符")		# 可以获取异常中的信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

yz