错误和异常
常见的两种错误:句法错误、异常
1、句法错误
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
2、异常
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
- 错误信息的最后一行说明程序遇到了什么类型的错误。异常有不同的类型,而类型名称会作为错误信息的一部分中打印出来
错误信息开头用堆栈回溯形式展示发生异常的语境。
3、异常的处理
try 语句的工作原理如下:
- 执行 try 子句 (try 和 except 关键字之间的(多行)语句)。
- 如果没有触发异常,则跳过 except 子句,try 语句执行完毕。
- 如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。 如果异常的类型与 except 关键字后指定的异常相匹配,则会执行 except 子句,然后跳到 try/except 代码块之后继续执行。
- 如果发生的异常与 except 子句 中指定的异常不匹配,则它会被传递到外部的 try 语句中;如果没有找到处理程序,则它是一个 未处理异常 且执行将终止并输出如上所示的消息。
- try 语句可以有多个 except 子句 来为不同的异常指定处理程序
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
try ... except 语句具有可选的 else 子句,它适用于 try 子句 没有引发异常但又必须要执行的代码
使用 else 子句比向 try 子句添加额外的代码要好,可以避免意外捕获非 try … except 语句保护的代码触发的异常。
import sys
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
- 异常处理程序不仅会处理在 try 子句 中发生的异常,还会处理在 try 子句 中调用(包括间接调用)的函数
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as err:
... print('Handling run-time error:', err)
...
Handling run-time error: division by zero
焦腾飞 3-29 21:52:55
4、触发异常
raise 语句支持强制触发指定的异常,如:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere
>>>
raise 判断是否触发了异常,没有处理该异常:
>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: HiThere
5、异常链
- raise 语句支持可选的 from 子句,该子句用于启用链式异常
def func():
raise ConnectionError
try:
func()
except ConnectionError as exc:
raise RuntimeError('failed to open database') from exc
预期:
C:\Python38\python.exe C:/Users/X21201/Desktop/python/tiaoce.py
Traceback (most recent call last):
File "C:/Users/X21201/Desktop/python/tiaoce.py", line 5, in <module>
func()
File "C:/Users/X21201/Desktop/python/tiaoce.py", line 2, in func
raise ConnectionError
ConnectionError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:/Users/X21201/Desktop/python/tiaoce.py", line 7, in <module>
raise RuntimeError('failed to open database') from exc
RuntimeError: failed to open database
- 可以通过使用 from None 这样的写法来禁用
try:
open('database.sqlite')
except OSError:
raise RuntimeError from None
Traceback (most recent call last):
File "C:/Users/X21201/Desktop/python/tiaoce.py", line 6, in <module>
raise RuntimeError from None
RuntimeError
6、用户自定义异常
- 通常会创建新的异常类来命名自己的异常,不论是以直接还是间接的方式,异常都应从 Exception 类派生
- 大多数异常命名都以 “Error” 结尾,类似标准异常的命名。
- 许多标准模块都需要自定义异常,以报告由其定义的函数中出现的错误。
7、定义清理操作
try 语句还有一个可选子句 finally,用于定义:在所有情况下都必须要执行的清理操作, finally子句是try语句执行结束前的最后执行。不论try语句是否触发异常,都会执行finally。 在实际应用程序中,finally 子句对于释放外部资源(例如文件或者网络连接)非常有用,无论是否成功使用资源。
try:
raise KeyboardInterrupt
finally:
print('goodbye world')
Traceback (most recent call last):
File "C:/Users/X21201/Desktop/python/tiaoce.py", line 3, in <module>
raise KeyboardInterrupt
KeyboardInterrupt
goodbye world
- 如果try子句期间触发了某异常,则执行expect语句处理异常,如果没except子句处理异常,则finally子句执行后重新触发异常
- except、else子句执行期间触发的异常,该异常也会在finally子句执行后被重新触发
- 如果 finally 子句中包含 break、continue 或 return 等语句,异常将不会被重新引发
- 如果执行 try 语句时遇到 break,、continue 或 return 语句,则 finally 子句在执行 break、continue 或 return 语句之前执行
- 如果 finally 子句中包含 return 语句,则返回值来自 finally 子句的某个 return 语句的返回值,而不是来自 try 子句的 return 语句的返回值
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print('y不能等于0哦')
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'
8、预定义的清理操作
with 语句支持以及时、正确的清理的方式使用文件对象
- 不正确的用法:简单应用没问题,但是对于较大的应用程序来说可能会出问题
for line in open("myfile.txt"):
print(line, end="")
- 正确用法:语句执行完毕后,即使在处理行时遇到问题,都会关闭文件 f
with open("myfile.txt") as f:
for line in f:
print(line, end="")
|