Python异常处理

Posted 摘星小筑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python异常处理相关的知识,希望对你有一定的参考价值。

异常分类

  程序中难免出现错误,总共可分为两种。

  1.逻辑错误

  2.语法错误

  对于刚接触编程的人来说,这两个错误都会经常去犯,但是随着经验慢慢的积累,语法错误的情况会越来越少反而逻辑错误的情况会越来越多(因为工程量巨大)。不论多么老道的程序员都不可避免出现这两种错误。

异常的三大信息

  异常其实就是程序运行时发生错误的信号,我们写代码的过程中不可避免也最害怕的就是出现异常,然而当程序抛出异常时实际上会分为三部分,即三大信息。

image-20200624135540455

常用的异常类

  在Python中一切皆对象,异常本身也是由一个类生成的,NameError其实本身就是一个异常类,其他诸如此类的异常类还有很多。

Python中常见的异常类 
AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo并没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 试图使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(其实就是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的 全局变量,导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

异常处理

  我们可以来用某些方法进行异常捕捉,当出现异常时我们希望代码以另一种逻辑运行,使得我们的程序更加健壮,这个就叫做异常处理。异常处理是非常重要的,本身也并不复杂,千万不可马虎大意。

  但是切记不可滥用异常处理,这会使得你的代码可读性变差。

if else处理异常

  ifelse本身就具有处理异常的功能,他们更多的是在我们能预测到可能出现的范围内进行规避异常,对于我们不能预测的异常来说就显得不是那么的好用。如下:

# ==== if else 处理异常 ====

while 1:
    select = input("请输入数字0进行关机:").strip()
    if select.isdigit():  # 我们可以防止用户输入非数字的字符
        if select == "0":
            print("正在关机...")
            break
    print("输入有误,请重新输入")

# ==== 执行结果 ====

"""
请输入数字0进行关机:关机
输入有误,请重新输入
请输入数字0进行关机:关机啊
输入有误,请重新输入
请输入数字0进行关机:0
正在关机...
"""

  这种异常处理机制虽然非常简单,但是并不灵活,我们可以使用更加简单的方式来处理他们。

try except处理异常

  try:代表要检测可能出现异常的代码块

  except:当异常出现后的处理情况

 

  执行流程:

    try中检测的代码块 ---> 如果有异常 ---> 执行except代码块 ---> 执行正常逻辑代码 ---> 程序结束

    try中检测的代码块 ---> 如果没有异常 ---> 执行完毕try中的代码块 ---> 执行正常逻辑代码 ---> 程序结束

# ====  try except 执行流程 有异常的情况 ====

li = [1,2,3]

try:
    print("开始执行我try了...")
    print(li[10])  # 出错点...
    print("继续执行我try...")
except IndexError as e:
    print("有异常执行我except...")

print("正常逻辑代码...")

# ==== 执行结果 ====

"""
开始执行我try了...
有异常执行我except...
正常逻辑代码...
""" 
# ====  try except 执行流程 无异常的情况 ====

li = [1,2,3]

try:
    print("开始执行我try了...")
    print(li[2])
    print("继续执行我try...")
except IndexError as e:
    print("有异常执行我except...")

print("正常逻辑代码...")

# ==== 执行结果 ====

"""
开始执行我try了...
3
继续执行我try...
正常逻辑代码...
""" 
# ==== try except 处理异常 ====

while 1:
    try:  # try检测可能出错的语句,一旦出错立马跳转到except语句块执行代码。
        select = int(input("请输入数字0进行关机:").strip())
        if select == 0:
            print("正在关机...")
            break
        print("输入有误,请重新输入...")
    except ValueError as e:  # 当执行完except的代码块后,程序运行结束,其中e代表的是异常信息。
        print("错误信息是:",e)
        print("输入有误,请重新输入")

# ==== 执行结果 ====

"""
请输入数字0进行关机:1
输入有误,请重新输入...
请输入数字0进行关机:tt
错误信息是: invalid literal for int() with base 10: \'tt\'
输入有误,请重新输入
请输入数字0进行关机:0
正在关机...
"""

多段except捕捉多异常

  我们可以使用try和多段except的语法来检测某一代码块,可以更加方便的应对更多类型的错误,Ps不常用:

# ==== 多段 except 捕捉多异常 ====

while 1:

    li = [1,2,3,4]
    dic = {"name":"Yunya","age":18}

    li_index = input("请输入索引:")
    dic_key = input("请输入键的名称:")

    if li_index.isdigit():
        li_index = int(li_index)

    try:
        print(li[li_index])
        print(dic[dic_key])
    except IndexError as e1:  # 注意,先抛出的错误会直接跳到其处理的except代码块,而try下面的语句将不会被执行。
        print("索引出错啦!")
    except KeyError as e2:
        print("键出错啦!")

# ==== 执行结果 ====

"""
请输入索引:10
请输入键的名称:gender
索引出错啦!
请输入索引:2
请输入键的名称:gender
3
键出错啦!
"""

元组捕捉多异常

  使用多段except捕捉多异常会显得特别麻烦,这个时候我们可以使用(异常类1,异常类2)来捕捉多异常,但是需要注意的是,对比多段except捕捉多异常来说,这种方式的处理逻辑会显得较为复杂(因为只有一段处理逻辑),如下:

# ====  元组捕捉多异常 ====

while 1:

    li = [1,2,3,4]
    dic = {"name":"Yunya","age":18}

    li_index = input("请输入索引:")
    dic_key = input("请输入键的名称:")

    if li_index.isdigit():
        li_index = int(li_index)

    try:
        print(li[li_index])
        print(dic[dic_key])
    except (IndexError,KeyError) as e:  # 使用()的方式可以同时捕捉很多异常。
        print("出错啦!")

# ==== 执行结果 ====

"""
请输入索引:10
请输入键的名称:gender
出错啦!
请输入索引:2
请输入键的名称:gender
3
出错啦!
"""

  可以看到,不管是那种错误都只有一种应对策略,如果我们想要多种应对策略就只能写if判断来判断异常类型再做处理。所以就会显得很麻烦,如下:

# ====  元组捕捉多异常 ====

while 1:

    li = [1,2,3,4]
    dic = {"name":"Yunya","age":18}

    li_index = input("请输入索引:")
    dic_key = input("请输入键的名称:")

    if li_index.isdigit():
        li_index = int(li_index)

    try:
        print(li[li_index])
        print(dic[dic_key])
    except (IndexError,KeyError) as e:
        # 判断异常类型再做出相应的对应策略
        if isinstance(e,IndexError):
            print("索引出错啦!")
        elif isinstance(e,KeyError):
            print("键出错啦!")

# ==== 执行结果 ====

"""
请输入索引:10
请输入键的名称:gender
索引出错啦!
请输入索引:2
请输入键的名称:gender
3
键出错啦!
"""

万能异常Exception

  我们可以捕捉Exception类引发的异常,它是所有异常类的基类。(Exception类的父类则是BaseException类,而BaseException的父类则是object类)

# ====  万能异常Exception ====

while 1:

    li = [1,2,3,4]
    dic = {"name":"Yunya","age":18}

    li_index = input("请输入索引:")
    dic_key = input("请输入键的名称:")

    if li_index.isdigit():
        li_index = int(li_index)

    try:
        print(li[li_index])
        print(dic[dic_key])
    except Exception as e:  #使用 Exception来捕捉所有异常。
        # 判断异常类型再做出相应的对应策略
        if isinstance(e,IndexError):
            print("索引出错啦!")
        elif isinstance(e,KeyError):
            print("键出错啦!")

# ==== 执行结果 ====

"""
请输入索引:10
请输入键的名称:gender
索引出错啦!
请输入索引:2
请输入键的名称:gender
3
键出错啦!
"""

try except else联用

  这种玩法比较少,else代表没有异常发生的情况下执行的代码,执行顺序如下:

  try中检测的代码块 ---> 如果有异常 ---> 终止try中的代码块继续执行 ---> 执行except代码块 ---> 执行正常逻辑代码 ---> 程序结束

  try中检测的代码块 ---> 如果没有异常 ---> 执行完毕try中的代码块 ---> 执行else代码块 ---> 执行正常逻辑代码 ---> 程序结束

# ====  try except else联用 有异常的情况====

li = [1,2,3]

try:
    print("开始执行我try了...")
    print(li[10])  # 出错点...
    print("继续执行我try...")
except IndexError as e:
    print("有异常执行我except...")
else:
    print("没有异常执行我else...")
print("正常逻辑代码...")

# ==== 执行结果 ====

"""
开始执行我try了...
有异常执行我except...
正常逻辑代码...
"""
# ====  try except else联用 无异常的情况====

li = [1,2,3]

try:
    print("开始执行我try了...")
    print(li[2])
    print("继续执行我try...")
except IndexError as e:
    print("有异常执行我except...")
else:
    print("没有异常执行我else...")
print("正常逻辑代码...")

# ==== 执行结果 ====

"""
开始执行我try了...
3
继续执行我try...
没有异常执行我else...
正常逻辑代码...
"""

try except finally联用

  finally代表不论抛异常与否都会执行,因此常被用作关闭系统资源的操作,关于try,except,else,finally他们的优先级如下:

  有异常的情况下:

    try代码块

    终止try代码块继续执行

    except代码块

    finally代码块

    正常逻辑代码

 

  无异常的情况下:

    try代码块

    else代码块

    finally代码块

    正常逻辑代码

# ====  try except else finally 执行流程 有异常的情况 ====

li = [1,2,3]

try:
    print("开始执行我try了...")
    print(li[10])  # 出错点...
    print("继续执行我try...")
except IndexError as e:
    print("有异常执行我except...")
else:
    print("没有异常执行我else...")
finally:
    print("不管有没有异常都执行我finally...")

print("正常逻辑代码...")

# ==== 执行结果 ====

"""
开始执行我try了...
有异常执行我except...
不管有没有异常都执行我finally...
正常逻辑代码...
"""
# ====  try except else finally 执行流程 无异常的情况 ====

li = [1,2,3]

try:
    print("开始执行我try了...")
    print(li[2])
    print("继续执行我try...")
except IndexError as e:
    print("有异常执行我except...")
else:
    print("没有异常执行我else...")
finally:
    print("不管有没有异常都执行我finally...")

print("正常逻辑代码...")

# ==== 执行结果 ====

"""
开始执行我try了...
3
继续执行我try...
没有异常执行我else...
不管有没有异常都执行我finally...
正常逻辑代码...
"""

自定义异常

raise主动抛出异常

  在某些时候我们可能需要主动的去阻止程序的运行,主动的抛出一个异常。可以使用raise来进行操作。这个是一种非常常用的手段。

# ====  raise使用方法  ====

print("----1----")
print("----2----")
print("----3----")
raise Exception("我也不知道是什么类型的异常...")
print("----4----")
print("----5----")
print("----6----")

# ==== 执行结果 ====

"""
----1----
----2----
----3----
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py", line 6, in <module>
    raise Exception("我也不知道是什么类型的异常...")
Exception: 我也不知道是什么类型的异常...

Process finished with exit code 1
"""

自定义异常类

  前面已经说过一切皆对象,异常也来自一个对象。因此我们也可以自己来定制一个对象。注意,自定义异常类必须继承BaseException类。

# ====  自定义异常类  ====

class MyError(BaseException):
    pass

raise MyError("我的异常")



# ==== 执行结果 ====

"""
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py", line 6, in <module>
    raise MyError("我的异常")
__main__.MyError: 我的异常
"""

扩展:断言assert

  断言是一个十分装逼的使用,假设多个函数进行计算,我们已经有了预期的结果只是在做一个算法的设计。如果函数的最后的结果不是我们本来预期的结果那么宁愿让他停止运行也不要让错误继续扩大,在这种情况下就可以使用断言操作,使用断言会抛出一个AssertionError类的异常。

# ====  断言assert  ====

def calculate():
    """假设在做非常复杂的运算"""
    return 3 + 2 * 5


res = calculate()

assert res == 25, ("只有当函数的结果为25时,这条错误才不会被执行。我是提示信息...")  # AssertionError

print("算法测试通过!你真的太厉害了")

# ==== 执行结果 ====

"""
Traceback (most recent call last):
  File "D:/DjangoProject/djangoAdmin/.vscode/one.py", line 10, in <module>
    assert res == 25, ("只有当函数的结果为25时,这条错误才不会被执行。我是提示信息...")  # AssertionError
AssertionError: 只有当函数的结果为25时,这条错误才不会被执行。我是提示信息...
"""

 

以上是关于Python异常处理的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 多处理进程中运行较慢的 OpenCV 代码片段

python常用代码片段总结

使用片段中的处理程序时出现非法状态异常

你如何在 python 中处理 graphql 查询和片段?

Java异常处理机制

java 反射代码片段