Python基础之异常处理

程序在运行过程中,难免会遇到错误,有的是编写人员疏忽造成的语法错误,有的是程序内部隐含逻辑问题造成的数据错误,还有的是程序运行时与系统的规则冲突造成的系统错误,等等。如果出错就不会执行出错语句后面的代码,导致程序异常中断,因此为了提高程序健壮性,容错性,对容易发生错误的语句需要捕获异常对异常处理,保证程序的连续性提高稳定性。

def get_num(l:list, n1:int, n2:int):
    print('执行get_num函数')
    num1 = l[n1]
    num2 = l[n2]
    res = num1/num2
    return res
    


def func_1():
    print('执行func_1函数')

def func_2():
    print('执行func_2函数')

get_num([1,2,3,4],5)
func_1()
func_2()

上述代码在调用函数get_num时会抛出indexError的异常,导致程序中断 

def func_1():
    x = 10
    y = 0
    b = x/y
    print(b)
    print("做完除法后需要执行的代码")

Traceback (most recent call last):
  File "/Users/fujinjie/PythonProjects/develop_test/buyer_app_autotest/unit_test.py", line 30, in <module>
    r = func_1()
  File "/Users/fujinjie/PythonProjects/develop_test/buyer_app_autotest/unit_test.py", line 26, in func_1
    b = x/y
ZeroDivisionError: division by zero

由于y = 0 ,即除数为0,程序抛出:ZeroDivisionError: division by zero 的异常,b = x/y后面的代码并没有被继续执行,因为出错后程序是并不知道如何处理所以直接中断后续,并抛出错误告知用户,假设在我们自动化测试中,可能要执数百条用例,如果某一句子代码执行出现异常,则自动化程序就会异常中断无法继续执行测试。

当一个程序发生异常时,代表该程序在执行时出现了非正常的情况,无法再执行下去。默认情况下,程序是要终止的。如果要避免程序退出,可以使用捕获异常的方式获取这个异常的名称,再通过其他的逻辑代码让程序继续运行,这种根据异常做出的逻辑处理叫作异常处理。异常处理不仅仅能够管理正常的流程运行,还能够在程序出错时对程序进行必要的处理。大大提高了程序的健壮性和人机交互的友好性。

一、try...except...异常处理机制

def func_1():
    x = 10
    y = 0
    try:
        b = x/y
        print(b)
    except:  # except捕获所有类型的异常,即只要有异常就捕获,不区分异常类型
        print("出现错误异常后,执行这里")
    print("做完除法后需要执行的代码")

#出现错误异常后,执行这里
 做完除法后需要执行的代码

上面是通过try...except 优化后的函数,把会出错的代码放入try: 语句下作为try的子句,当try下面的代码出错时,程序会执行except:下面的代码,并执行程序后续代码,如果try: 下面的代码没有出错,则跳过except: 直接执行后续代码,程序继续运行。

在这里通过except我们捕获try代码块中的错误和异常,相当于告诉程序,当try下面语句出错时,该如何处理,上面我们直接做了一个字符串的输出: "出现错误异常后,执行这里"

二、except + 异常类型 捕获指定类型的异常

def func_1():
    x = 10
    y = 0
    try:
        b = x/y
        print(b)
    except ZeroDivisionError as e:  # as 为别名,当一个变量名称太长时可取别名代替简化
        print(f"出错啦,错误信息:{e}")
    print("做完除法后需要执行的代码")

出错啦,错误信息:division by zero
做完除法后需要执行的代码

基本语法构成

try:
    可能产生异常的代码块
except [ (Error1, Error2, ... ) [as e] ]:
    处理异常的代码块1
except [ (Error3, Error4, ... ) [as e] ]:
    处理异常的代码块2
except [Exception]:
    处理其它异常

该格式中,[] 括起来的部分可以使用,也可以省略。其中各部分的含义如下:

  • (Error1, Error2,...) 、(Error3, Error4,...):其中,Error1、Error2、Error3 和 Error4 都是具体的异常类型。显然,一个 except 块可以同时处理多种异常。
  • [as e]:作为可选参数,表示给异常类型起一个别名 e,这样做的好处是方便在 except 块中调用异常类型(后续会用到)。
  • [Exception]:作为可选参数,可以代指程序可能发生的所有异常情况,其通常用在最后一个 except 块。

try except 语句的执行流程如下:

  1. 首先执行 try 中的代码块,如果执行过程中出现异常,系统会自动生成一个异常类型,并将该异常提交给 Python 解释器,此过程称为。

    捕获异常

  2. 当 Python 解释器收到异常对象时,会寻找能处理该异常对象的 except 块,如果找到合适的 except 块,则把该异常对象交给该 except 块处理,这个过程被称为处理异常。如果 Python 解释器找不到处理异常的 except 块,则程序运行终止,Python 解释器也将退出。

很多时间我们并不知道try下面包裹的子句会出现何种类型的异常,对于这种情况我们一般交给 Exception来处理,即表示可以接收任何类型的异常

 

try:
    #...
except Exception:
    #...

一个简的例子

try:
    num_1 = int(input("输入一个整数:"))
    num_2 = int(input("输入另一个整数:"))
    print( num_1/num_2 )
except ValueError:
    print("数值错误:程序只能接收整数参数")
except ArithmeticError:
    print("算术错误")
except Exception:
    print("未知异常")

该程序中,根据用户输入num_1 和 num_2 值的不同,可能会导致 ValueError、ArithmeticError 异常:

  1. 如果用户输入的 num_1 或者 num_2 是其他字符,而不是数字,会发生 ValueError 异常,try 块会捕获到该类型异常,同时 Python 解释器会调用第一个 except 块处理异常;
  2. 如果用户输入的 num_1 和 num_2 是数字,但 num_2 的值为 0,由于在进行除法运算时除数不能为 0,因此会发生 ArithmeticError 异常,try 块会捕获该异常,同时 Python 解释器会调用第二个 except 块处理异常;
  3. 当然,程序运行过程中,还可能由于其他因素出现异常,try 块都可以捕获,同时 Python 会调用最后一个 except 块来处理。

通过traceback.format_exc()返回异常栈(返回是数据类型是字符串)

 

import traceback

def func_1():
    x = 10
    y = 0
    try:
        b = x/y
        print(b)
    except ZeroDivisionError as e: # as 为别名,当一个变量名称太长时可取别名代替简化
        ei = traceback.format_exc()
        print(f"出错啦,错误信息:{e},{ei}")
    print("做完除法后需要执行的代码")

出错啦,错误信息:division by zero,Traceback (most recent call last):
  File "C:\Users\37210\Desktop\Jcmall_study\test_b\b_3.py", line 8, in func_1
    b = x/y
ZeroDivisionError: division by zero

做完除法后需要执行的代码

 常见异常举例

IOError 输入/输出异常;基本上是无法打开文件
TypeError 传入对象类型与要求的不符合
AttributeError 无法访问该对象,一般对象不存在,比如foo.x,但是foo没有属性x
ImportError  无法引入模块或包;基本上是路径问题或名称错误
IndentationError  语法错误(的子类) ;代码没有正确对齐
IndexError  下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError  试图访问字典里不存在的键
NameError  使用一个还未被赋予对象的变量
SyntaxError Python代码非法,一般是语法错误

except 后面可以指定捕获的错误类型,错误类型指定后,如果try:下面的代码抛出的错误类型非该指定的类型,则不会被捕获,程序仍旧会异常终止; 

 

def func_1():
    x = 10
    y = 0
    try:
        b = x/y
        print(b)
    except ValueError as e:  # 这里对ValueError的异常进行捕获
        print(f"出错啦,错误信息:{e}")
    print("做完除法后需要执行的代码")


Traceback (most recent call last):
  File "/Users/fujinjie/PythonProjects/develop_test/buyer_app_autotest/unit_test.py", line 33, in <module>
    r = func_1()
  File "/Users/fujinjie/PythonProjects/develop_test/buyer_app_autotest/unit_test.py", line 27, in func_1
    b = x/y
ZeroDivisionError: division by zero

三、except 捕获多个指定的错误类型 

def func_1():
    x = 10
    y = 0
    try:
        b = x/y
        print(b)
    except (TypeError,ValueError,ZeroDivisionError) as e:  # 这里对									  ZeroDivisionError的异常进行捕获
        print(f"出错啦,错误信息:{e}")
    print("做完除法后需要执行的代码")

出错啦,错误信息:division by zero
做完除法后需要执行的代码

def func_1():
    x = 10
    y = "0"    # 此处y是一个str类型
    try:
        b = x/y
        print(b)
    except (TypeError,ValueError,ZeroDivisionError) as e:  # 这里对												TypeError的异常进行捕获
        print(f"出错啦,错误信息:{e}")
    print("做完除法后需要执行的代码")

出错啦,错误信息:unsupported operand type(s) for /: 'int' and 'str'
做完除法后需要执行的代码

四、try...except...finally

Python异常处理机制还提供了一个 finally 语句,通常用来为 try 块中的程序做扫尾清理工作

注意:finally 只要求和 try 搭配使用,而至于该结构中是否包含 except ,对于 finally 不是必须的

finally 语句的功能是:无论 try 块是否发生异常,最终都要进入 finally 语句,并执行其中的代码块。

def func_1():
    x = 10
    y = 0
    try:
        b = x/y
        print(b)
    finally:
        print("无论try中代码是不出现异常,finally中代码始终会被执行")
    print("继续执行模块中其他代码")



def func_2():
    x = 10
    y = 1
    try:
        b = x/y
        print(b)
    finally:
        print("无论try中代码是不出现异常,finally中代码始终会被执行")
    print("继续执行模块中其他代码")

比较上面两个函数 func_1与func_2,func_1中的try代码块会出现异常,func_2中的try代码块未出现异常,但两个函数的finally包裹的子句都得到了执行

五、try...except...else

在原本的

try except

结构的基础上,Python异常处理机制还提供了一个 else 块,也就是原有 try except 语句的基础上再添加一个 else 块,即

try:
	#...
except:
	#...
else:
	#...

使用 else 包裹的代码,只有当 try 块没有捕获到任何异常时,才会得到执行;反之,如果 try 块捕获到异常,即便调用对应的 except 处理完异常,else 块中的代码也不会得到执行。

注意:else 必须和 try except 搭配使用

 

try:
    result = 20 / int(input('请输入除数:'))
    print(result)
except ValueError:
    print('必须输入整数')
except ArithmeticError:
    print('算术错误,除数不能为 0')
else:
    print('没有出现异常')
print("继续执行")

try: 下面的代码未发生异常,则执行else: 下面的代码,如果发生异常则执行except: 下面的代码,因此esle: 代码块的执行与否,取决于try: 是否报异常; 

def func_1():
    x = 10
    y = 1
    try:
        b = x/y
        print(b)
    except:  # except捕获所有类型的异常,即只要有异常就捕获,不区分异常类型
        print("出现错误异常后,执行这里")
    else:
        print("没有异常会执行这里的代码")
    finally:
        print("finally中代码始终会被执行")
    print("做完除法后需要执行的代码")

总结一个

Python异常处理语法结构如下

try:
    #业务实现代码
except Exception1 as e:
    #捕获到Exception1异常后执行此代码
    ...
except Exception2 as e:
    #捕获到Exception2异常后执行此代码
    ...
#可以有多个 except
...
else:
    #try中未出现异常是执行此处代码,有异常时不会执行
finally :
    #不管try中代码是否出现异常,此处代码始终会执行
    ...

注:finally必须跟try连用,跟except与else无连用关系;else必须跟try...except...连用

即整个结构try是必须存在的

  • 如果没有 try 块,则不能有后面的 except 块、else 块和 finally 块。但是也不能只使用 try 块,要么使用 try except 结构,要么使用 try finally 结构;
  • except 块、else 块、finally 块都是可选的,当然也可以同时出现;
  • 可以有多个 except 块,多个 except 块必须位于 try 块之后,finally 块必须位于所有的 except 块之后。
  • 要使用 else 块,其前面必须包含 try 和 except。

异常处理执行过程

 

练习:

1、下面代码存在keyError异常,请通过try...except捕获并处理该异常,当错误时print输出“输入的key不存在,请检查key”

goods_inf = {'Id': 10001, 'goods': 'JBL小音响', 'price': 501}
def get_goods_inf(goods:dict, key:str):
    res = goods[key]
    return res

get_goods_inf(goods=goods_inf, key='id')

2、请优化下面函数,当除数为0时捕获异常并告知用户除数不能为0,当输入的是非数字时捕获异常并提示用户输入的是非数字字符 

def add_num():
    num1 = int(input('请输入被除数:'))
    num2 = int(input('请输入除数:'))
    res = num1/num2
    return res

 

3、基于add_num函数,请通过try...except...else优化,当未捕获到异常是函数返回res,当捕获到异常时函数一律返回”输入错误,请检查输入并把这个异常类型打印出来“

4、基于add_num函数,请通过finally进行优化,函数始终会通过print输出’add_num函数执行完毕’

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值