一、异常基础概念 exception
什么是错误?由于逻辑或者语法等导致一个程序无法正常执行的问题,一些错误无法预知

什么是异常? 没有发生期望或者应该发生的状态,不能按照程序的正常流程来走发生的故障
当异常发生时,程序不会再继续执行下去,而转去调用此函数的地方,
待处理此错误并恢复为正常的状态
错误是错的这件事,异常是发生错误的表现出来的状态

异常的作用:
通知上层调用者有错误产生需要处理,用作信号,是处理错误的一套机制

异常的发送者-------->错误的接收者(处理者)

python中的异常类型不能自己定义,只能用python提供的类型

二、try语句的俩种语法:

1.try - except语句的语法:

try:
    #可能触发异常的语句放在这里
except 错误类型1  [as 变量1]    #[]里面的可以省略,是对错误类型起别名
    #异常处理语句1
except 错误类型2  [as 变量2]   # []里面的可以省略,是对错误类型起别名
# 异常处理语句2
except 错误类型3  [as 变量3]   # []里面的可以省略,是对错误类型起别名
# 异常处理语句3
#...
except:
    #异常处理语句other
else:
    #未发生异常的语句
finally:
    #最终语句

作用:
尝试捕获可能发生的异常,如果发生异常将程序转为正常状态继续执行
注:
except语句至少有一个,else语句可有可无
示例:

def div_apple(n):
    """此示例用分苹果来是以捕获异常"""
    print("%d个苹果你想分给几个人?" % n)
    s = input("请输入人数:") #输入的不是一个整数串 比如1000->1ooo
    cnt = int(s)              #此处可能会引起valueErro的错误,程序中断
    result = n /cnt           #此处可能触发ZeroDivisionErro
    print("每个人分了",result,"个苹果")
    
#发生错误后,会把对应的错误向上层抛,比如这里会抛向这个函数的调用者
#所以我们将函数调用放到我们的try语句中
#如果发生异常,try接收到相应的错误类型,和except中的错误类型比较
#如果错误类型匹配,那么就执行相应错误的处理语句
#如果这里没有定义该类型的错误,将继续向上抛,抛给模块,...,直至抛给操作系统,程序挂掉

try:
    div_apple(10)
except ValueError:     #ValueErro一种错误类型,出现异常情况才会来执行这里
    print("发生了值错误,已转为正常状态")
except ZeroDivisionError:
    print("除数为0的异常已经捕获并且处理")
#except是自上而下的进行类型匹配

print("程序正常退出")

执行结果:当错将1000输成1ooo,触发ValueErro异常
10个苹果你想分给几个人?
请输入人数:1ooo
发生了值错误,已转为正常状态
程序正常退出

执行结果:输入0,触发ZeroDivisionError的错误
10个苹果你想分给几个人?
请输入人数:0
除数为0的异常已经捕获并且处理
程序正常退出

也可以将俩句except写在一起:

try:
    div_apple(10)
except (ValueError,ZeroDivisionError):   
#以上俩个类型的错误都会用相同的方法处理
    print("发生异常,已转为正常状态")

else语句:没有发生异常的情况下才会执行这里

try:
    div_apple(10)
except (ValueError,ZeroDivisionError):   #出现异常情况才会执行这里
    print("发生异常,已转为正常状态")
except:
    print("除了以上两种异常,其他的异常都在我这里就能处理")
else:#没异常才会执行else的语句
    print("没有发生什么异常才会执行这里")
#except是自上而下的进行类型匹配

执行结果:没有异常情况
10个苹果你想分给几个人?
请输入人数:5
每个人分了 2.0 个苹果
没有发生什么异常才会执行这里
程序正常退出

finally语句:无论是否有异常发生,都会执行

try:
    div_apple(10)
except (ValueError,ZeroDivisionError):   #出现异常情况才会执行这里
    print("发生异常,已转为正常状态")
except:
    print("除了以上两种异常,其他的异常都在我这里就能处理")
else:#没异常才会执行else的语句
    print("没有发生什么异常才会执行这里")
#except是自上而下的进行类型匹配
finally:
    print("无论发生异常与否,我都会被执行到")

 执行结果:有异常情况
 10个苹果你想分给几个人?
请输入人数:0
发生异常,已转为正常状态
无论发生异常与否,我都会被执行到
程序正常退出

 执行结果:无异常情况
 10个苹果你想分给几个人?
请输入人数:4
每个人分了 2.5 个苹果
没有发生什么异常才会执行这里
无论发生异常与否,我都会被执行到
程序正常退出

练习:写一个函数,get_score()来获取用户输入的学习成绩,0~1,如果输入出现错误
则,此函数返回0,如果正确,则返回相应的成绩

#第二种方法:可以在函数内部进行异常捕获处理
def get_score():
    s = input("请输入您的成绩(0~100):  ")
    try:
        num = int(s)
    except ValueError:
        return 0
    if num>= 0 and num<=100:
        return num
    return 0

#第一种方法:可以在调用者这里进行异常处理(同上)

执行结果:
请输入您的成绩(0~100):  oooo
0

try-except语句语法说明:
1.except子句至少有一个,可以有多个
2.else子句最多只能有一个,也可以省略
3.finally子句最多只能有一个,可以省略

有的时候我们希望看到程序自己告诉我们哪里错误,而不是直接崩溃掉,
我们可以这样来设计:

def get_score():
    s = input("请输入您的成绩(0~100):  ")
    num = int(s)
    if num>= 0 and num<=100:
        return num
    return 0

#可以将程序出错的原因打印出来
try:
    print(get_score())
except ValueError as err:
    print("程序出错在这里: ",err)
   
执行结果:
请输入您的成绩(0~100):  1ooo
程序出错在这里:  invalid literal for int() with base 10: '1ooo'

2.try - finally语句:

语法:

try:
    #可能触发异常的语句
finally:
    #最终语

语法说明:
finally子句不可以省略
一定不存在except子句
作用:
通常用try-finally语句来做触发异常时 必须要处理的事,无论异常是否发生,finally一定会执行
(比如,家里着火了,无论如何一定要关闭煤气罐和电闸 -_-,就算不着火,出门关闸是个好习惯 - _ - )
注:
try-finally语句不会改变程序的(正常/异常)状态

示例:

#厨房做饭为例
#打开一定要打开天然气,
# 期间不管发生什么事还是什么都不发生,都得关闭天然气

def cooke():
    print("打开天然气")
    try:
        count = int(input("输入鸡蛋的个数"))
        print("完成煎鸡蛋!共煎%d个鸡蛋" % count)
    finally:
        print("关闭天然气")
#不管鸡蛋煎没煎成,一定要关闭天然气

cooke()

执行结果:
打开天然气
输入鸡蛋的个数dasssd
关闭天然气
Traceback (most recent call last):
    count = int(input("输入鸡蛋的个数"))
ValueError: invalid literal for int() with base 10: 'dasssd'

#从运行结果看,即使出错了,还是最终将天然气关闭了

try finally语句可以和 try except嵌套使用:
规则:从内层往外层抛,看哪一层可以解决

def cooke():
    print("打开天然气")
    try:
        try:
            count = int(input("输入鸡蛋的个数"))
            print("完成煎鸡蛋!共煎%d个鸡蛋" % count)
        finally:
            print("关闭天然气")
    except ValueError:
        print("内层的try异常自己没能处理,抛到了我这里,处理完毕,程序正常")
        
cooke()

执行结果:
打开天然气
输入鸡蛋的个数sfdsfsd
关闭天然气
内层的try异常自己没能处理,抛到了我这里,处理完毕,程序正常

执行过程:

python异常处理机制结构详解 python异常处理机制_异常机制

3.raise:语句
作用:
触发一个错误,让程序进入异常状态,即发送发出一个错误信号
语法:
raise 异常类型

raise 异常对象

示例:捕获异常的类型

#一般我们处理异常都通过return 返回值结合if else语句来处理错误
#这里示例用raise语句来处理错误,主动抛出错误
#raise 扔,抛   我们一般需要将raise抛出的错误进行捕获并且处理

def my_raise(n):
    #假设n必须是 0~100之间的数
    print("begin处理....")
    if n>100 or n<0:  #传来的是无效值,我们需要告诉调用者
        raise ValueError
    print("end处理...")

try:
    my_raise(1000)
except ValueError:
    print("raise抛出的错误我已经捕获到,并且已经处理,程序恢复正常")

print("程序正常退出")

执行结果:
begin处理....
raise抛出的错误我已经捕获到,并且已经处理,程序恢复正常
程序正常退出

示例:捕获异常类型的对象,即可查看具体错误

def my_raise(n):
    #假设n必须是 0~100之间的数
    print("begin处理....")
    if n>100 or n<0:  #传来的是无效值,我们需要告诉调用者
    
        raise ValueError("你输入的参数:%d不合法" % n)
    #VlaueError()相当于创建了这个类型的对象,
    # 然后被捕获,即为e,"你输入的参数:%d不合法" % n  为对象内容
    
    print("end处理...")

try:
    my_raise(1000)
except ValueError as e:
    print("raise抛出的错误我已经捕获到,并且已经处理,程序恢复正常")
    print("错误的信息通过对象返回回来内容为:",e)
print("程序正常退出")

执行结果:
begin处理....
raise抛出的错误我已经捕获到,并且已经处理,程序恢复正常
错误的信息通过对象返回回来内容为: 你输入的参数:1000不合法
程序正常退出

4.assert 语句(断言语句)
语法:
assert 真值表达式,错误数据(通常是字符串)
作用:
当真值表达式为False时,用错误数据创建一个AssertionError类型的错误,并进入异常状态
等同于:
if 真值表达式 == False:
raise AssertionError(错误数据)

示例:

def get_age():
    a = input("请输入年龄: ")
    a = int (a)
    assert a < 140,"年龄不可能大于140!!!"
    assert a >=0,"年龄不可能出现负数!!!"
    return a

age = get_age()
print("您输入的年龄是: ",age)

执行结果:
请输入年龄: 999
Traceback (most recent call last):
    assert a < 140,"年龄不可能大于140!!!"
AssertionError: 年龄不可能大于140!!!

示例:既然断言是种错误,当然可以捕获处理啦

def get_age():
    a = input("请输入年龄: ")
    a = int (a)
    assert a < 140,"年龄不可能大于140!!!"
    assert a >=0,"年龄不可能出现负数!!!"
    return a

try:
    age = get_age()
except AssertionError as erro:
    print("发生了断言错误,并且错误对象为: ",erro)
    age = 0   #可以进行一个默认的纠正,改为0

print("您输入的年龄是: ",age)
执行结果:
请输入年龄: 10000
发生了断言错误,并且错误对象为:  年龄不可能大于140!!!
您输入的年龄是:  0

三、为什么需要异常处理机制
在程序调用层数较深的时候,向主调函数传递错误信息需要层层的return 返回比较麻烦