Python-进阶-错误和异常

这里的错误和异常不是特指语法错误,而是指程序在运行时发生的错误。即使语句或表达式在语法上是正确的,当尝试执行它时也可能会导致错误。这个错误可能是由于输入数据的问题,运行环境的问题,还可能是参数配置的问题等等,
随着代码编写的多了,应用环境的变化,各种各样的奇奇怪怪问题都会出现,我们会开始意识到,错误处理的重要性。毕竟比运行中断更可怕的,任务以错误的方式运行完了。 随着处理的问题情况增加,我们必须要开始有意识的对代码进行一些错误处理。来增强代码的鲁棒性/异常识别能力。

try except

基础版本使用 try except 我们更多的可能是类似if else的用法,重点关注try的代码能不能成功运行,但是实际上except 是可以识别不同的异常类型的,对应的我们也可以进行不同的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys

try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err: # 文件读写错误
print("OS error:", err)
except ValueError: # 值转换错误
print("Could not convert data to an integer.")
except Exception as err: # 捕获其他的异常
print(f"Unexpected {err=}, {type(err)=}")
raise

python的标准异常类型:

异常名称 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

raise(引发异常)

raise 语句允许程序员强制发生指定的异常。 raise 的唯一参数指示要引发的异常。这必须是异常实例或异常类(从 BaseException 派生的类,例如 Exception 或其子类之一)。如果传递异常类,它将通过不带参数调用其构造函数来隐式实例化:例如:

1
2
raise ValueError() # 不传递参数的异常类,会进行隐式实例化
raise NameError('HiThere') # 传递参数将异常类进行实例化。

定义清理操作(final)

try 语句还有另一个可选子句,旨在定义在所有情况下都必须执行的清理操作。如果存在 finally 子句,则 finally 子句将作为 try 语句完成之前的最后一个任务执行。无论 try 语句是否产生异常, finally 子句都会运行。在实际应用程序中, finally 子句对于释放外部资源(例如文件或网络连接)非常有用,无论资源的使用是否成功。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
finally:
print("executing finally clause")

>>> divide(2, 1)
result is 2.0
executing finally clause

>>> divide(2, 0)
division by zero!
executing finally clause

>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

以下几点讨论发生异常时更复杂的情况:

  • 如果在执行 try 子句期间发生异常,则该异常可以由 except 子句处理。如果 except 子句未处理异常,则在执行 finally 子句后将重新引发异常。
  • 执行 except 或 else 子句期间可能会发生异常。同样,在执行 finally 子句后会重新引发异常。
  • 如果 finally 子句执行 break 、 continue 或 return 语句,则不会重新引发异常。
  • 如果 try 语句到达 break 、 continue 或 return 语句,则 finally 子句将在之前执行到 break 、 continue 或 return 语句的执行。
  • 如果 finally 子句包含 return 语句,则返回值将是 finally 子句的 return 语句中的值,而不是值来自 try 子句的 return 语句。

丰富异常

当创建异常以便引发时,通常使用描述已发生的错误的信息对其进行初始化。在某些情况下,在捕获异常后添加信息很有用。为此,异常有一个方法 add_note(note) ,它接受一个字符串并将其添加到异常的注释列表中。标准回溯渲染包括异常之后的所有注释(按添加顺序)

1
2
3
4
5
6
7
8
9
10
11
12
try:
raise TypeError('bad type')
except Exception as e:
e.add_note('Add some information')
e.add_note('Add some more information')
raise

Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: bad type
Add some information
Add some more information

-------------本文结束感谢您的阅读-------------