Python异常捕获与处理及自定义异常

  1. 相关
    什么是异常?异常就是“不正常”。
    我们的程序的执行过程中发生了一个不正常的事件,这个事件影响了程序的正常运行,此时就是发生了程序异常。
    与在Java中一样的,Python中的异常也是一个对象,当程序发生异常时,程序会抛出响应的异常对象,我们需要去捕获这个异常并解决他,防止异常导致程序停止。
'''
想最快的入门Python吗?请搜索:"泉小朵",来学习Python最快入门教程。
也可以加入我们的Python学习Q群:902936549,送给每一位python的小伙伴教程资料。
'''

2.Python异常
Python官网异常描述

2.1. Python异常相关
官网对于Python内置异常的描述:

In Python, all exceptions must be instances of a class that derives from BaseException.
In a try statement with an except clause that mentions a particular class, that clause also handles any exception classes derived from that class (but not exception classes from which it is derived).
The built-in exception classes can be subclassed to define new exceptions; programmers are encouraged to derive new exceptions from the Exception class or one of its subclasses, and not from BaseException.

在Python内置异常中,所有的异常都应是BaseException的派生类。
在一个try-except(类似java中的try-catch)代码块中,except后会声明一种异常,声明的这种异常能处理所有该异常的子类异常(但是不包括他的父类异常)。
内部异常类可以被继承来定义新的异常类,并且建议程序员来通过继承Exception而不是BaseException来定义一个新的异常类。
1
短短三句话,说明了Python异常的定义、处理办法和自定义异常。我们下面也会从异常处理和自定义异常两方面来讲解。

2.2.Python内置异常结构
首先我们来看一下Python内置异常类的结构,可见Exception其实也是BaseException的一个子类,而Exception是常规异常的基类,我们的自定义异常将会继承这个类。

BaseException # 所有异常的基类
+-- SystemExit # 解释器请求退出
+-- KeyboardInterrupt # 用户中断执行(通常是输入^C)
+-- GeneratorExit # 生成器(generator)发生异常来通知退出
+-- Exception # 常规异常的基类
+-- StopIteration # 迭代器没有更多的值
+-- StopAsyncIteration # 必须通过异步迭代器对象的anext()方法引发以停止迭代
+-- ArithmeticError # 各种算术错误引发的内置异常的基类
| +-- FloatingPointError # 浮点计算错误
| +-- OverflowError # 数值运算结果太大无法表示
| +-- ZeroDivisionError # 除(或取模)零 (所有数据类型)
+-- AssertionError # 当assert语句失败时引发
+-- AttributeError # 属性引用或赋值失败
+-- BufferError # 无法执行与缓冲区相关的操作时引发
+-- EOFError # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发
+-- ImportError # 导入模块/对象失败
| +-- ModuleNotFoundError # 无法找到模块或在在sys.modules中找到None
+-- LookupError # 映射或序列上使用的键或索引无效时引发的异常的基类
| +-- IndexError # 序列中没有此索引(index)
| +-- KeyError # 映射中没有这个键
+-- MemoryError # 内存溢出错误(对于Python 解释器不是致命的)
+-- NameError # 未声明/初始化对象 (没有属性)
| +-- UnboundLocalError # 访问未初始化的本地变量
+-- OSError # 操作系统错误,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,构造函数可能返回子类
| +-- BlockingIOError # 操作将阻塞对象(e.g. socket)设置为非阻塞操作
| +-- ChildProcessError # 在子进程上的操作失败
| +-- ConnectionError # 与连接相关的异常的基类
| | +-- BrokenPipeError # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入
| | +-- ConnectionAbortedError # 连接尝试被对等方中止
| | +-- ConnectionRefusedError # 连接尝试被对等方拒绝
| | +-- ConnectionResetError # 连接由对等方重置
| +-- FileExistsError # 创建已存在的文件或目录
| +-- FileNotFoundError # 请求不存在的文件或目录
| +-- InterruptedError # 系统调用被输入信号中断
| +-- IsADirectoryError # 在目录上请求文件操作(例如 os.remove())
| +-- NotADirectoryError # 在不是目录的事物上请求目录操作(例如 os.listdir())
| +-- PermissionError # 尝试在没有足够访问权限的情况下运行操作
| +-- ProcessLookupError # 给定进程不存在
| +-- TimeoutError # 系统函数在系统级别超时
+-- ReferenceError # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象
+-- RuntimeError # 在检测到不属于任何其他类别的错误时触发
| +-- NotImplementedError # 在用户定义的基类中,抽象方法要求派生类重写该方法或者正在开发的类指示仍然需要添加实际实现
| +-- RecursionError # 解释器检测到超出最大递归深度
+-- SyntaxError # Python 语法错误
| +-- IndentationError # 缩进错误
| +-- TabError # Tab和空格混用
+-- SystemError # 解释器发现内部错误
+-- TypeError # 操作或函数应用于不适当类型的对象
+-- ValueError # 操作或函数接收到具有正确类型但值不合适的参数
| +-- UnicodeError # 发生与Unicode相关的编码或解码错误
| +-- UnicodeDecodeError # Unicode解码错误
| +-- UnicodeEncodeError # Unicode编码错误
| +-- UnicodeTranslateError # Unicode转码错误
+-- Warning # 警告的基类
+-- DeprecationWarning # 有关已弃用功能的警告的基类
+-- PendingDeprecationWarning # 有关不推荐使用功能的警告的基类
+-- RuntimeWarning # 有关可疑的运行时行为的警告的基类
+-- SyntaxWarning # 关于可疑语法警告的基类
+-- UserWarning # 用户代码生成警告的基类
+-- FutureWarning # 有关已弃用功能的警告的基类
+-- ImportWarning # 关于模块导入时可能出错的警告的基类
+-- UnicodeWarning # 与Unicode相关的警告的基类
+-- BytesWarning # 与bytes和bytearray相关的警告的基类
+-- ResourceWarning # 与资源使用相关的警告的基类。被默认警告过滤器忽略。

'''
想最快的入门Python吗?请搜索:"泉小朵",来学习Python最快入门教程。
也可以加入我们的Python学习Q群:902936549,送给每一位python的小伙伴教程资料。
'''
  1. Python处理异常
    在前面我们已经说过了要用try-except来处理异常,这里我们就来对比一下处理与不处理异常的不同之处。

3.1.不处理异常的程序
运行以下代码

i = 1
m = 0

while i < 10:
    print("i=",i)
    print(i / m)
    i += 1

结果输出

/root/PycharmProjects/PyDemo/venv/bin/python /root/PycharmProjects/PyDemo/exception/__init__.py
i= 1
Traceback (most recent call last):
File "/root/PycharmProjects/PyDemo/exception/__init__.py", line 6, in <module>
    print(i / m)
ZeroDivisionError: division by zero

Process finished with exit code 1

结果显示,我们的控制台直接输出异常并且中断了程序,这种情况在程序运行时是非常糟糕的,他导致我们之后的代码都无法运行。

3.2. 处理异常的程序
运行以下代码

i = 1
m = 0

while i < 3:
    print("i=", i)
    try:
        print(i / m)
    except ZeroDivisionError:
        print("除数不能为0")
    i += 1

结果输出

/root/PycharmProjects/PyDemo/venv/bin/python /root/PycharmProjects/PyDemo/exception/__init__.py
i= 1
除数不能为0
i= 2
除数不能为0

Process finished with exit code 0

这里我们用try-except将可能发生异常的代码包围起来,在运行的时候虽然发生了异常,但是我们有处理异常的代码,而且程序能够继续运行,这在程序设计中是非常重要的。

3.3. 异常处理扩展
除了最基本的try-except代码块来处理异常之外,Python也提供了更多的更能,如else、finally等

3.3.1. else的用法
else与except是同等级的,主要是处理没有捕获到异常的情况。

else使用通式

try:
    正常的操作
......................
except:
    发生异常,执行这块代码
......................
else:
    如果没有异常执行这块代码

代码

i = 10
m = 0

while m < 3:
    print("i=", i)
    try:
        print(i / m)
    except ZeroDivisionError:
        print("除数不能为0")
    else:
        print("运算正常,未捕获到异常")
    m += 1

运行结果

/root/PycharmProjects/PyDemo/venv/bin/python /root/PycharmProjects/PyDemo/exception/__init__.py
i= 10
除数不能为0
i= 10
10.0
运算正常,未捕获到异常
i= 10
5.0
运算正常,未捕获到异常

Process finished with exit code 0

在这段代码中,当没有捕获到异常时就执行else下的代码,捕获到异常时执行except下的代码。

3.3.2 finally的用法
finally下的代码,无论异常是否发生都会执行

finally使用通式

try:
    正常的操作
......................
except:
    发生异常,执行这块代码
......................
else:
    如果没有异常执行这块代码
finally:
    退出try时总会执行

代码

i = 10
m = 0

while m < 3:
    print("i=", i)
    try:
        print(i / m)
    except ZeroDivisionError:
        print("除数不能为0")
    else:
        print("运算正常,未捕获到异常")
    finally:
        print("第", m + 1, "次循环")
        m += 1

结果

/root/PycharmProjects/PyDemo/venv/bin/python /root/PycharmProjects/PyDemo/exception/__init__.py
i= 10
除数不能为0
第 1 次循环
i= 10
10.0
运算正常,未捕获到异常
第 2 次循环
i= 10
5.0
运算正常,未捕获到异常
第 3 次循环

Process finished with exit code 0

从结果中我们即可看出,无论是捕获到异常时执行except下代码,还是未捕获到异常执行else下代码时,finally下的代码都被执行了。
finally的在实际应用中是很重要的,比如我们在操作数据库的时候,无论你的操作是否成功,关闭数据库连接都是必须的,这个时候就可以把关闭连接的程序放在finally下,确保操作结束后,数据库连接被关闭。

3.4. try执行原理
当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。

如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。
如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。
如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。
如果最后设置有finally操作,那么最后执行finally下的程序。

'''
想最快的入门Python吗?请搜索:"泉小朵",来学习Python最快入门教程。
也可以加入我们的Python学习Q群:902936549,送给每一位python的小伙伴教程资料。
'''
  1. 主动抛出异常
    在编程过程中,有时候虽然代码没有异常,但是因为设计中有要求,我们也可以主动抛出异常,类似Java中的throw,Python中用raise来主动抛出异常,我们自定义的异常往往需要主动抛出。
    raise使用通式
raise [Exception [, args [, traceback]]]

代码

i = 10
m = 0


def calculate(q, p):
    if m == 0:
        raise Exception("m不能等于0哦")
    print(q / p)


while m < 3:
    try:
        calculate(i, m)
    except Exception as e:
        print(e)
    m += 1

结果

/root/PycharmProjects/PyDemo/venv/bin/python /root/PycharmProjects/PyDemo/exception/__init__.py
m不能等于0哦
10.0
5.0

Process finished with exit code 0

在代码中,我们规定:当除数等于0的时候要抛出一个异常,异常信息是“m不能等于0哦”,然后在执行的时候,如果捕获到异常就把异常信息打印出来。

'''
想最快的入门Python吗?请搜索:"泉小朵",来学习Python最快入门教程。
也可以加入我们的Python学习Q群:902936549,送给每一位python的小伙伴教程资料。
'''

5.Python自定义异常
Python官网自定义异常描述

Programs may name their own exceptions by creating a new exception class (see Classes for more about Python classes). Exceptions should typically be derived from the Exception class, either directly or indirectly.
程序员可以通过创建新的异常类来命名它们自己的异常。异常通常应该直接或间接地从 Exception 类派生。
1
OK,通过官方的文档,我们可以清楚地看出来自定义异常的方法:继承Exception,那么下面我们就来自己定义一个异常。

自定义异常类

自定义异常异常应该是典型的继承自Exception类,通过直接或间接的方式。

一下是直接通过Exception类创建了一个自定义异常,

class CantBeZero(Exception):
    def __init__(self, err="自定义异常提醒您,除数不能为0哦!!!"):
        Exception.__init__(self, err)

代码
在try语句块中,用户自定义的异常后执行except块语句,变量 e 是用于创建CantBeZero类的实例。

i = 10
m = 0


def calculate(q, p):
    if m == 0:
        raise CantBeZero
    print(q / p)


while m < 3:
    try:
        calculate(i, m)
    except CantBeZero as e:
        print(e)
    m += 1

结果

/root/PycharmProjects/PyDemo/venv/bin/python /root/PycharmProjects/PyDemo/exception/__init__.py
自定义异常提醒您,除数不能为0哦!!!
10.0
5.0

Process finished with exit code 0

我们在除数等于0的时候直接抛出了异常,并且被程序捕获到,然后执行了except下的程序。

'''
想最快的入门Python吗?请搜索:"泉小朵",来学习Python最快入门教程。
也可以加入我们的Python学习Q群:902936549,送给每一位python的小伙伴教程资料。
'''
  1. 总结
    异常处理用于处理程序错误之外,还有许多应用的地方。如关闭资源、平台兼容、模块导入等,异常的捕获与处理在程序设计中是非常重要的,我们在前面已经提到,未被捕获处理的异常可能会直接导致程序的停止,这对于生产来说是毁灭性的打击。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容