python异常处理

1、什么是异常

定义:异常就是程序运行时发生错误
构成:追踪信息、错误代码、异常名称、提示信息

2、python程序中常见的异常种类

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

3、异常处理

python解释器检测到错误,触发异常(也可以是程序员自己触发异常),程序员在程序中编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关),
,如果捕获成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理。

python解释器去执行程序,检测到了一个错误时,触发异常,异常触发后如果是没有被处理的情况下,程序就会在当前异常的地方终止,后面的代码不会再运行。所以为了让我们的
程序能够长期稳定的持续的运行,就必须提供一种异常处理机制来增强你程序的健壮性与容错性,从而能够有效的提升用户体验,维持业务的稳定性

这也就是我们对程序进行异常处理的意义

4、异常处理方式

基础语法
单分支
多分支
万能异常
try...else语句
try...finally语句
抛出异常(主动触发异常)
自定义异常
断言

# 1、使用if判断的方式
# num = input(">>>>>>:")
#
# if num.isdigit():
#     int(num) # 这里是程序的主体,其余的都属于异常处理范畴
# elif num.isspace():
#     print("您输入的是空格,请重新输入!")
# elif len(num) == 0:
#     print("输入为空,请输入正确的数值!")
# else:
#     print("不知道哪错了,请您联系客服!")

# 如下不通过逻辑判断进行异常处理会导致程序终止报错
# int(num)

# print("程序执行到了我这里@#@")

# 2、使用特定的语法结构的方式
"""
基本语法:
try:
    被检测的代码块
except 异常类型:
    try中一旦检测到异常,就执行当前位置的逻辑

单分支:
需要明确的是单分支只能用来处理指定的情况,如果未捕获到异常,则程序依旧会终止报错
try:
    order
except NameError as e: # 这里可以使用except与as+变量名的方式搭配使用,打印变量名会直接输出报错信息
    print(e)  # name 'order' is not defined
    
多分支:
mul_except = {"广东":["广州", "深圳", "东莞", "惠州"], "江苏":["南京", "苏州", "徐州", "连云港"], "湖北": ["武汉", "宜昌"]}
# try:
#     p_name = input(">>>>>>")
#     # 打印省级行政单位下辖的市级单位列表
#     print(mul_except[p_name])
#     c_index = input(">>>>>>")
#     # 通过序列下标打印具体某一市级行政单位名称
#     print(mul_except[p_name][int(c_index)])
# except ValueError:
#     print("列表中没有该省的城市信息")
# except IndexError:
#     print("您要获取的元素下标超出序列范围")

# 多分支的方式处理异常使得代码更加的人性化,用户体验会更好

# 万能异常:
在python的异常中,有一个万能异常:Exception,他可以捕获任何异常,但同时它也是一把双刃剑,使用的时候要尽可能谨慎
其使用场景如下:
无论程序出现什么异常,我们都可以丢弃,或者使用同一个逻辑去处理时,一个Exception就足够了
如果是不同的异常类型我们都需要做不同的处理逻辑,那么此时就要用到多分支
在日常的实践当中我们更多的其实是多分支+万能异常这种处理方式,首先通过多分支优先处理一些重要的、已知的异常,一些无关紧
要的、未知的异常则通过万能异常来进行捕获,需要注意的是在联合使用时,万能异常一定要放在最后,否则就没有意义了

# try...else语句
try:
    print("此处代码被完整执行")
except IndexError as e:
    print(e)
else:
    # 该语句通常用来完成一些说明工作
    print("try代码块没有触发任何异常,执行此处")

# try...finally语句
def finally_usage():
    try:
        print("此处代码被完整执行")
        print("此处代码触发")
        return True
    except Exception as e:
        print(e)
        return False
    finally:
        # 该语句通常可用来完整一些收尾工作
        print("不管try语句中的代码是否报错,都会执行finally分支中的代码")

# 主动触发异常
try:
    raise TypeError('类型错误')
except Exception as e:
    print(e)
    
# 自定义异常
当python现有的异常类型无法覆盖我们出现的业务场景时,我们可以自己定义特殊类型的异常,继承Exception类即可
class OrderIdNotExist(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return self.msg

order_id_list = [13666, 13999, 13888, 13777]
def customize_except(order_id):
    if order_id not in order_id_list:
        raise OrderIdNotExist("订单ID不存在")
        
try:
    customise_except(333999)
except OrderIdNotExist as e:
    print(e)

# 断言
assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达式为假。可以理解assert断言语句为raise-if-not,用来测试表达式
其返回值为假,就会触发异常
assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题,格式如下:

assert expression[, arguments]
assert 表达式[, 参数]

assert 2==7
order_id = 1000000088
order_id_list = [1000000081, 1000000082, 1000000083, 1000000084, 1000000085]
assert order_id in order_id_list


# 异常处理小结:
try:
    # 可能发生异常的代码(也就是我们常说的业务代码)
except 异常类型1 as 变量名:
    print(变量名) # 变量名存储的是具体的错误信息
except 异常类型2 as 变量名:
    print(变量名) # 变量名存储的是具体的错误信息
except Exception as 变量名:
    print(变量名) # 变量名存储的是具体的错误信息
else:
    print('如果以上代码没有发生异常以及异常处理工作就执行这里的代码')
    print('一般情况下else中的代码用来下结论')
    # logging模块
finally:
    print('不管代码是否有异常都会执行,且在函数中遇到return仍然会执行')
    print('一般情况下用于这个函数中资源的回收')

"""
# 基本语法示例
# try:
#     order
# except:
#     print("程序遇到异常了")

# 单分支示例

# order

# try:
#     order
# except NameError as e:
#     print("程序遇到变量异常")
#     print(e)
#
# print("程序执行到了我这里@#

# 多分支示例

# mul_except = {"广东": ["广州", "深圳", "东莞", "惠州"], "江苏": ["南京", "苏州", "徐州", "连云港"], "湖北": ["武汉", "宜昌"]}
#
# try:
#     p_name = input(">>>>>>")
#     # 打印省级行政单位下辖的市级单位列表
#     print(mul_except[p_name])
#     c_index = input(">>>>>>")
#     # 通过序列下标打印具体某一市级行政单位名称
#     print(mul_except[p_name][int(c_index)])
# except ValueError:
#     print("列表中没有该省的城市信息")
# except IndexError:
#     print("您要获取的元素下标超出序列范围")

# 万能异常示例(将上一例子添加尾部万能异常代码块,并构造缩进异常即可)

# try...else语句示例
# try:
#     print("此处代码被完整执行")
#     print("此处代码触发:NameError")
#     order
# except IndexError as e:
#     print(e)
# else:
#     # 该语句通常用来完成一些说明工作
#     print("try代码块没有触发任何异常,执行此处")

# try...finally语句示例

# def finally_usage():
#     try:
#         # print("此处代码被完整执行")
#         print("此处代码触发:NameError")
#         print(order)
#         return True
#     except Exception as e:
#         print(e)
#         return False
#     finally:
#         # 该语句通常可用来完整一些收尾工作
#         print("不管try语句中的代码是否报错,都会执行finally分支中的代码")
#
# finally_usage()

# 抛出异常示例(主动触发异常):
# raise TypeError('类型错误')

# try:
#     raise TypeError('类型错误')
# except Exception as e:
#     print(e)


# 自定义异常
# 自定义异常,只需自定义异常类继承父类Exception。在自定义异常类中,重写父类init方法
# class OrderIdNotExist(Exception):
#     # 自定义异常类型的初始化
#     def __init__(self, msg):
#         self.msg = msg
#
#     # 返回异常类对象的说明信息
#     def __str__(self):
#         return self.msg
#
#
# def customize_except(order_id):
#     order_id_list = [13666, 13999, 13888, 13777]
#     if order_id not in order_id_list:
#         raise OrderIdNotExist("订单ID不存在")
#
#
# # customize_except(333999)
#
# try:
#     customize_except(333999)
# except OrderIdNotExist as e:
#     print(e)

# 断言示例
# 格式: assert 条件表达式
# assert 2==7
# order_id = 1000000088
# order_id_list = [1000000081, 1000000082, 1000000083, 1000000084, 1000000085]
# assert order_id in order_id_list

知识点补充

  1. raise 语句可以自定义报错信息
  2. raise后的语句是不会被执行了,因为已经抛出异常,控制流将会跳到异常捕捉模块。
  3. except 语句可以一个except后带多个异常,也可以用多个语句捕捉多个异常,分别做不同处理。
  4. except语句捕捉的异常如果没有发生,那么except里的语句块是不被执行的。而是执行else里的语句
  5. 在上面语句中try/except/else/finally所出现的顺序必须是try–>except X–>except–>else–>finally,即所有的except必须在else和finally之前,else(如果有的话)必须在finally之前,而except X必须在except之前。否则会出现语法错误。
    6.else和finally都是可选的.
  6. 在上面的完整语句中,else语句的存在必须以except X或者except语句为前提,如果在没有except语句的try block中使用else语句会引发语法错误。

总结:使用特殊格式异常处理相对于使用if逻辑判断处理的异常的优势
try...except这种异常处理机制就是取代if那种逻辑判断方式,从而在不牺牲可读性的前提下增强健壮性和容错性,并且异常处理中为每一个异常定制了异常类型,对于同一种异常,一个except就可以捕捉到,可以同时处理多段代码的异常,减少了代码,增强了可读性

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容