异常处理
异常就是运行期检测到的错误。计算机语言针对可能出现的错误定义了异常类型,某种错误引发对应的异常时,异常处理程序将被启动,从而恢复程序的正常运行。
1. Python 标准异常总结
- BaseException:所有异常的 基类
- Exception:常规异常的 基类
- StandardError:所有的内建标准异常的基类
- ArithmeticError:所有数值计算异常的基类
- FloatingPointError:浮点计算异常
- OverflowError:数值运算超出最大限制
- ZeroDivisionError:除数为零
- AssertionError:断言语句(assert)失败
- AttributeError:尝试访问未知的对象属性
- EOFError:没有内建输入,到达EOF标记
- EnvironmentError:操作系统异常的基类
- IOError:输入/输出操作失败
- OSError:操作系统产生的异常(例如打开一个不存在的文件)
- WindowsError:系统调用失败
- ImportError:导入模块失败的时候
- KeyboardInterrupt:用户中断执行
- LookupError:无效数据查询的基类
- IndexError:索引超出序列的范围
- KeyError:字典中查找一个不存在的关键字
- MemoryError:内存溢出(可通过删除对象释放内存)
- NameError:尝试访问一个不存在的变量
- UnboundLocalError:访问未初始化的本地变量
- ReferenceError:弱引用试图访问已经垃圾回收了的对象
- RuntimeError:一般的运行时异常
- NotImplementedError:尚未实现的方法
- SyntaxError:语法错误导致的异常
- IndentationError:缩进错误导致的异常
- TabError:Tab和空格混用
- SystemError:一般的解释器系统异常
- TypeError:不同类型间的无效操作
- ValueError:传入无效的参数
- UnicodeError:Unicode相关的异常
- UnicodeDecodeError:Unicode解码时的异常
- UnicodeEncodeError:Unicode编码错误导致的异常
- UnicodeTranslateError:Unicode转换错误导致的异常
异常体系内部有层次关系,Python异常体系中的部分关系如下所示:
2. Python标准警告总结
- Warning:警告的基类
- DeprecationWarning:关于被弃用的特征的警告
- FutureWarning:关于构造将来语义会有改变的警告
- UserWarning:用户代码生成的警告
- PendingDeprecationWarning:关于特性将会被废弃的警告
- RuntimeWarning:可疑的运行时行为(runtime behavior)的警告
- SyntaxWarning:可疑语法的警告
- ImportWarning:用于在导入模块过程中触发的警告
- UnicodeWarning:与Unicode相关的警告
- BytesWarning:与字节或字节码相关的警告
- ResourceWarning:与资源使用相关的警告
3. try - except 语句
try:
检测范围
except Exception[as reason]:
出现异常后的处理代码
try 语句按照如下方式工作:
- 首先,执行
try
子句(在关键字try
和关键字except
之间的语句) - 如果没有异常发生,忽略
except
子句,try
子句执行后结束。 - 如果在执行
try
子句的过程中发生了异常,那么try
子句余下的部分将被忽略。如果异常的类型和except
之后的名称相符,那么对应的except
子句将被执行。最后执行try - except
语句之后的代码。 - 如果一个异常没有与任何的
except
匹配,那么这个异常将会传递给上层的try
中。
【例子】
try:
f = open('test.txt')
print(f.read())
f.close()
except OSError:
print('打开文件出错')
# 打开文件出错
[root@10 python]# ls test.txt
ls: 无法访问'test.txt': 没有那个文件或目录
[root@10 python]# py try-except.py
打开文件出错
[root@10 python]# echo -n "测试" > test.txt
[root@10 python]# py try-except.py
测试
[root@10 python]#
这段代码的作用是尝试打开名为"test.txt"的文件,如果打开成功,则读取文件内容并打印出来,最后关闭文件。如果打开文件出错(比如文件不存在或无法访问),则会捕获 OSError
异常并打印出"打开文件出错"。
解释一下代码的执行过程:
- 首先,尝试打开名为"test.txt"的文件,如果文件存在且可以被访问,则执行下面的代码块;否则跳到
except
语句。 - 打开文件后,使用
f.read()
方法读取文件内容,并通过print()
函数打印出来。 - 最后,使用
f.close()
关闭文件,确保资源被正确释放。 - 如果在
try
代码块中发生了OSError
异常(比如文件无法打开),那么程序会跳到except
语句,并执行其中的代码,打印出"打开文件出错"。
注意:使用 try-except
结构可以捕获指定类型的异常,并在出现异常时执行特定的错误处理代码,避免程序崩溃。
【例子】
try:
f = open('test.txt')
print(f.read())
f.close()
except OSError as error:
print('打开文件出错\n原因是:' + str(error))
# 打开文件出错
# 原因是:[Errno 2] No such file or directory: 'test.txt'
[root@10 python]# rm -f test.txt
[root@10 python]# py try-except_2.py
打开文件出错
原因是:[Errno 2] No such file or directory: 'test.txt'
[root@10 python]#
这段代码与之前的代码相似,但在 except
块中做了一些修改。现在,代码会捕获 OSError
异常,并将异常信息打印出来。
解释一下代码的执行过程:
- 首先,尝试打开名为"test.txt"的文件,如果文件存在且可以被访问,则执行下面的代码块;否则跳到
except
语句。 - 打开文件后,使用
f.read()
方法读取文件内容,并通过print()
函数打印出来。 - 最后,使用
f.close()
关闭文件,确保资源被正确释放。 - 如果在
try
代码块中发生了OSError
异常(比如文件无法打开),那么程序会跳到except
语句,并执行其中的代码。 -
as error
表示将捕获的异常对象赋值给变量error
。 - 在
except
块中,使用print()
函数将错误提示信息打印出来,包括捕获到的异常信息。
这样,即可打印出出错的原因,帮助调试程序或提供错误信息。
3.1 一个try
语句可能包含多个except
子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。**
【例子】
try:
int("abc")
s = 1 + '1'
f = open('test.txt')
print(f.read())
f.close()
except OSError as error:
print('打开文件出错\n原因是:' + str(error))
except TypeError as error:
print('类型出错\n原因是:' + str(error))
except ValueError as error:
print('数值出错\n原因是:' + str(error))
# 数值出错
# 原因是:invalid literal for int() with base 10: 'abc'
[root@10 python]# ls test.txt
ls: 无法访问'test.txt': 没有那个文件或目录
[root@10 python]# py try-except_3.py
数值出错
原因是:invalid literal for int() with base 10: 'abc'
[root@10 python]# sed -i.bak 's|abc|123|' try-except_3.py
[root@10 python]# sed -n '/int(.....)/'p try-except_3.py
int("123")
[root@10 python]# py try-except_3.py
类型出错
原因是:unsupported operand type(s) for +: 'int' and 'str'
[root@10 python]# sed -i.bak2 "s#'1'#1#" try-except_3.py
[root@10 python]# sed -n '/s =/'p try-except_3.py
s = 1 + 1
[root@10 python]# py try-except_3.py
打开文件出错
原因是:[Errno 2] No such file or directory: 'test.txt'
[root@10 python]# echo -n "测试" > test.txt
[root@10 python]# py try-except_3.py
测试
[root@10 python]#
这段代码中使用了多个 except
块来捕获不同类型的异常,并打印出对应的错误信息。
解释一下代码的执行过程:
- 首先,尝试将字符串 "abc" 转换为整数,由于该字符串不能被正确转换,抛出了
ValueError
异常。这时,程序会跳到第一个except
块中。 - 在第一个
except
块中,使用print()
函数打印出错误提示信息,包括捕获的异常信息。在这里,打印出了“数值出错\n原因是:invalid literal for int() with base 10: 'abc'”。 - 接下来,由于前面的语句发生了异常,程序不会继续执行剩余的代码。
- 注意,第二个和第三个
except
块不会执行,因为这里抛出的异常是ValueError
,而不是OSError
或TypeError
。
使用多个 except
块可以根据不同的异常类型,进行不同的错误处理。这样能够更精确地捕获和处理异常,提高代码的健壮性。
3.2 try-except-else 语句
【例子】
dict1 = {'a': 1, 'b': 2, 'v': 22}
try:
x = dict1['y']
except LookupError:
print('查询错误')
except KeyError:
print('键错误')
else:
print(x)
# 查询错误
[root@10 python]# py try-except-else.py
查询错误
[root@10 python]# vi try-except-else.py
[root@10 python]# cat try-except-else.py
dict1 = {'a': 1, 'b': 2, 'v': 22}
try:
x = dict1['y']
except KeyError:
print('键错误')
except LookupError:
print('查询错误')
else:
print(x)
[root@10 python]# py try-except-else.py
键错误
[root@10 python]# sed -i.bak "s/'y'/'a'/" try-except-else.py
[root@10 python]# sed -n '/a/'p try-except-else.py
dict1 = {'a': 1, 'b': 2, 'v': 22}
x = dict1['a']
[root@10 python]# py try-except-else.py
1
[root@10 python]#
这段代码中使用了 try-except-else
结构来处理字典的查询操作。
解释一下代码的执行过程:
- 首先,尝试从
dict1
字典中查询键'y'
对应的值,由于该键不存在,抛出了KeyError
异常。这时,程序会跳到第一个except
块中。 - 在第一个
except
块中,使用print()
函数打印出错误提示信息,即"查询错误"。 - 由于发生了异常,程序不会继续执行
else
块中的代码。 - 注意,这里使用了
LookupError
作为第一个except
块的异常类型,它是KeyError
的父类,因此可以捕获KeyError
异常。 - 所以,使用多个except代码块时,必须坚持对其规范排序,要从最具针对性的异常到最通用的异常。
通过 try-except-else
结构,我们可以在 try
块中尝试执行某个操作,如果发生异常,则执行特定的异常处理代码(如 except
块),如果没有发生异常,则执行其他的代码(如 else
块)。这样可以使代码更加灵活和健壮。
3.3 一个 except
子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。
【例子】
try:
int("abc")
s = 1 + '1'
f = open('test.txt')
print(f.read())
f.close()
except (OSError, TypeError, ValueError) as error:
print('出错了!\n原因是:' + str(error))
# 出错了
# 原因是:invalid literal for int() with base 10: 'abc'
这段代码中使用了一个 except
块来捕获多个异常类型,并打印出对应的错误信息。
解释一下代码的执行过程:
- 首先,尝试将字符串 "abc" 转换为整数,由于该字符串不能被正确转换,抛出了
ValueError
异常。 - 接着,由于前面的语句发生了异常,程序不会继续执行剩余的代码。
- 在
except
块中,使用(OSError, TypeError, ValueError)
表示同时捕获这三种异常类型。然后,将捕获的异常对象赋值给变量error
。 - 在
except
块中,使用print()
函数打印出错误提示信息,包括捕获的异常信息。在这里,打印出了“出错了!\n原因是:invalid literal for int() with base 10: 'abc'”。
通过使用括号((OSError, TypeError, ValueError)
)将多个异常类型放在一起,可以在单个 except
块中捕获多个异常。这样简化了代码,并减少了重复的代码判断。
元组内的异常类型顺序对结果没有影响。
try
语句块中的语句段顺序对结果有影响,谁在前并当发生异常时,就先输出谁并终止代码执行。
感谢你的指正,我会更加准确地回答你的问题。非常抱歉之前的回答有误。谢谢!
4. try - except - finally 语句
try:
检测范围
except Exception[as reason]:
出现异常后的处理代码
finally:
无论如何都会被执行的代码
不管try
子句里面有没有发生异常,finally
子句都会执行。
【例子】如果一个异常在try
子句里被抛出,而又没有任何的except
把它截住,那么这个异常会在finally
子句执行后被抛出。
def divide(x, y):
try:
result = x / y
print("result is", result)
except ZeroDivisionError:
print("division by zero!")
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
# TypeError: unsupported operand type(s) for /: 'str' and 'str'
[root@10 python]# py try-except-finally.py
result is 2.0
executing finally clause
division by zero!
executing finally clause
executing finally clause
Traceback (most recent call last):
File "/root/python/try-except-finally.py", line 17, in <module>
divide("2", "1")
File "/root/python/try-except-finally.py", line 3, in divide
result = x / y
TypeError: unsupported operand type(s) for /: 'str' and 'str'
[root@10 python]#
这段代码中,divide
函数用于执行两个数的除法操作。它使用了try-except-finally
语句块来处理可能出现的异常情况。
当传入的两个参数x
和y
都是数值类型时,try
块中的除法操作将正常执行,结果将存储在result
变量中,并通过print语句打印出来。然后,无论是否有异常发生,finally
块中的代码都会执行,打印出"executing finally clause"的消息。
当传入的y
为0时,会发生ZeroDivisionError
异常,except
块中的代码将执行,打印出"division by zero!"的消息。然后,无论是否有异常发生,finally
块中的代码仍然会执行,打印出"executing finally clause"的消息。
当传入的参数x和y都是字符串类型时,由于字符串不支持除法操作,将抛出TypeError异常。在这种情况下,except
块中的代码不会执行,而是直接跳过并执行finally
块中的代码,打印出"executing finally clause"的消息,并输出错误信息。
5. try - except - else 语句
如果在try
子句执行时没有发生异常,Python将执行else
语句后的语句。
try:
检测范围
except:
出现异常后的处理代码
else:
如果没有异常执行这块代码
使用except
而不带任何异常类型,这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息,因为它捕获所有的异常。
try:
检测范围
except(Exception1[, Exception2[,...ExceptionN]]]):
发生以上多个异常中的一个,执行这块代码
else:
如果没有异常执行这块代码
【例子】
try:
fh = open("testfile.txt", "w")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError:
print("Error: 没有找到文件或读取文件失败")
else:
print("内容写入文件成功")
fh.close()
# 内容写入文件成功
这段代码是一个文件操作的示例,让我解释一下它的执行流程:
首先,代码尝试打开文件 "testfile.txt" 并以写入模式打开。如果文件不存在,将创建一个新文件。这部分逻辑被放在
try
块中。如果文件打开成功,代码会执行
fh.write("这是一个测试文件,用于测试异常!!")
来向文件写入内容。如果文件打开或写入过程中发生了输入/输出(IO)错误,例如文件无法找到或无法读取,那么程序会跳转到
except IOError
块中,并打印 "Error: 没有找到文件或读取文件失败"。如果文件打开和写入都成功完成且未发生任何错误,那么程序会执行
else
块中的代码,打印 "内容写入文件成功",然后关闭文件。
总结起来,这段代码尝试打开文件并写入内容,如果在这个过程中发生了IO错误,则会捕获并处理异常。如果没有发生错误,代码会执行相应的成功逻辑,并在最后关闭文件。
注意:else
语句的存在必须以except
语句的存在为前提,在没有except
语句的try
语句中使用else
语句,会引发语法错误。
6. raise语句
Python 使用raise
语句抛出一个指定的异常。
【例子】
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
# An exception flew by!
这段代码演示了如何使用 raise
语句主动引发一个异常,并使用 try-except
结构捕获并处理该异常。让我解释一下它的执行流程:
在
try
块中,使用raise
语句引发了一个NameError
异常,异常的错误信息为'HiThere'
。因为在
except NameError
中指定了捕获NameError
类型的异常,所以代码会跳转到该except
块中。在
except
块中,会打印出字符串 "An exception flew by!"。
总结起来,这段代码通过 raise
语句主动引发了一个 NameError
异常,并在 try-except
结构中捕获和处理了该异常。当异常被引发时,代码会跳转到与异常类型匹配的 except
块中执行相应的逻辑。