1)异常层次
Python 有许多内置异常,我们可能时不时会遇到,例如ZeroDivisionError、KeyError、ValueError、TypeError等等。
每个异常都是异常层次结构的一部分 -- 这意味着大多数异常都以某种方式从同一个父类Exception继承。
我们可以通过._subclasses_()打印某些异常类的子类来查看这一点。
另一种方法是使用.__bases__检查异常的父类
这可能很麻烦,所以我创建了一个函数来帮助你自动执行此操作:
一些例子:
2)BaseException 与 Exception
上面我们看到了Exception和BaseException ,并且Exception继承自其父类BaseException。
那么有什么区别呢?
Exception是我们在常规编码中遇到的最常见异常的父类,例如ZeroDivision、ValueError、TypeError、KeyError等 BaseException用来和Exception区别,其他继承自BaseException的异常一般用于特殊情况 BaseException的一些子类包括KeyboardInterrupt,SystemExit等
当我们创建自定义异常时,我们几乎应该从Exception而不是 BaseException 继承,因为Exception意味着它是由于常规编码错误或问题导致的正常错误。
另一方面,BaseException包含其他特殊异常,如KeyboardInterrupt或SystemExit,当我们想要退出 Python 程序时会强制引发这些异常。
假设我们有一个写得很糟糕的 while 循环,要求用户输入:
如果我们希望退出这个 while 循环,我们可以按 Control-C 来强制KeyboardInterrupt结束我们的 Python 程序。
但是如果我们将 BaseException 作为 e 除外,就会发生一些奇怪的事情。
我们现在无法使用 Control-C 强制KeyboardInterrupt结束我们的程序——我们现在只能终止整个终端实例。
重要的是——排除 BaseException 是不好的做法,因为这可能会导致程序出现奇怪的副作用。
3)“except Exception as e”应该放在最后
假设每个 try 块有多个异常,并且每个异常块以某种方式处理不同的异常。
由于排序的原因,我们首先检查是否存在 ZeroDivisionError。
如果有,则执行该except块。 如果没有,我们继续下一个区块 except Exception as e块捕获了之前未捕获的所有其他异常,因此它应该是最后一个
如果我们把except Exception 作为 e 放在第一位,它会处理所有异常,而其他except块将不会执行。
注意 — ZeroDivisionError是 Exception 的子类。这就是为什么我们需要将它放在except Exception as e块之前。
4) except (Exception1, Exception2) as e
但是如果我们想以相同的方式处理一堆异常怎么办?我们是否必须为每个异常重复代码?例如:
我们不必重复那么多代码。
在上面的例子中,以相同方式处理异常的前 3 个except块可以这样压缩为一个except块:
这里,第一个except块捕获ZeroDivisionError、ValueError或KeyError,并以相同的方式处理它们。
5)用消息断言
我们通常使用assert关键字来检查某个条件是否满足。
如果条件为 True,则不发生任何事情 如果条件为 False,则发生 AssertionError
这是一个简单的功能示例——我们要求用户输入,但唯一有效的输入是“A”或“B”。因此,我们使用断言语句来确保这一点。
如果我们输入除 A 或 B 之外的任何内容,我们都会得到 AssertionError:
但是你知道我们可以向 AssertionError 添加自定义消息吗?我们只需要在断言消息后面添加一个字符串(以逗号分隔)
当发生 AssertionError 时,还会显示断言消息。
6)如何忽略断言语句
有一种方法可以忽略所有现有的assert
语句。那就是在运行 Python 脚本时使用 -O 标志。
假设我们有以下带有assert
语句的 Python 脚本。我们使用命令python filename.py正常运行它
这里,我们只是得到一个 AssertionError 并且根本没有到达我们的打印语句。
但是,我们可以使用 -O 标志忽略assert
语句。我们现在使用命令python -O filename.py运行此脚本
注意——这是大写的 O
当我们使用 -O 标志运行 Python 时,特殊变量*debug*变为 False(默认情况下通常为 True)。因此,所有assert
语句都会被忽略。
如果我们有许多assert
语句,但希望运行所有代码而没有任何assert
语句触发 AssertionError,那么这个技巧很有用。
7)try except else 块
我们可能在学习 Python 基础知识时已经学习过 try-except-finally 块。但是您听说过 try-except-else-finally 块吗?
else块位于except块之后。
如果发生异常,则except块运行,但else块不运行 如果没有发生异常,则except块不会运行,但else块会运行。
我们强制执行 ZeroDivisionError。在这种情况下,由于except块运行,所以else块不会运行。无论如何, finally块始终会运行。
现在让我们让try块无错误地运行。现在没有发生任何异常,我们的except块不会运行。因此,我们的else块会运行。
8)finally 块可以在 return 语句之后运行
正常情况下,函数中return语句之后不会发生任何事情。一旦执行return语句,我们就会立即退出函数。
编写一个简单的函数来说明这一点:
这里,print('orange')没有运行,因为它发生在return语句之后。return语句运行后,我们的函数立即停止,并忽略之后发生的一切。
但使用try-except-finally 中的 finally块,我们可以绕过这个限制?
无论如何,finally块内的代码都会运行。即使在函数内的return语句之后也是如此。
这里,我们在 try 块中 print('apple') 并返回 1。但是即使在执行 return 语句之后,我们仍然会像在 finally 块中一样 print('orange')。
如果我们有无论如何都需要运行的代码,这很有用,例如关闭文件或关闭数据库连接(否则可能会导致内存泄漏和其他问题)
9)raise Exception1 from Exception2
在更复杂的应用程序中,我们可能希望引发一系列异常,而不是单个异常。这样,我们就能更好地追踪异常的确切来源和原因。
为此,我们可以使用语法raise Exception1 from Exception2。
下面是一个简单的例子:
这里,我们首先强制引发 ZeroDivisionError。
在我们的except块中,我们从之前引发的 ZeroDivisionError 中引发另一个 ValueError。这样,我们得到了由 ZeroDivisionError 引起的 ValueError。
为了查找和检查异常的原因,我们可以简单地使用.__cause__属性。
🏴☠️宝藏级🏴☠️ 原创公众号『数据STUDIO』内容超级硬核。公众号以Python为核心语言,垂直于数据科学领域,包括可戳👉 Python|MySQL|数据分析|数据可视化|机器学习与数据挖掘|爬虫 等,从入门到进阶!
长按👇关注- 数据STUDIO -设为星标,干货速递