01. Python异常机制
1.1 异常的概念
1.1.1 语法错误
- 语法错误又称为解析错误,是学习Python时最常见的错误。
- 在IDE中,这类错误会被标记出来:
- 在程序运行时,Python解释器会复现错误的代码行,并用小箭头
^
指向行里检测到的第一个错误。- 如下图中,在
print("Hello World")
的p下方出现了小箭头。 - 说明在p前后出现了语法错误(这里就是
while True
语句后缺少了冒号:
与缩进)。
- 如下图中,在
1.1.2 异常
- 即使语句或表达式使用了正确的语法,执行时仍可能触发错误。
- 执行时检测到的错误称为异常,异常会导致程序运行终止,并且解释器会报出错误信息。
```python
除数为0异常
10 * (1 / 0) Traceback (most recent call last): File “
“, line 1, in ZeroDivisionError: division by zero
标识符未定义异常
4 + spam * 3 Traceback (most recent call last): File “
“, line 1, in NameError: name ‘spam’ is not defined
类型计算异常
‘2’ + 2 Traceback (most recent call last): File “
“, line 1, in TypeError: can only concatenate str (not “int”) to str ``` 1.1.3 错误信息解读
- 对于整个报错信息而言,最有用的实际上是最后一行信息。
- 错误信息的最后一行说明程序遇到了什么类型的错误。
- 异常有不同的类型,而类型名称会作为错误信息的一部分中打印出来:ZeroDivisionError、NameError、TypeError、……。
- 作为异常类型打印的字符串是发生的内置异常的名称。
- 对于所有内置异常都是如此,但对于用户定义的异常则不一定如此(虽然这种规范很有用)。标准的异常类型是内置的标识符(不是保留关键字)。
-
1.2 Python异常体系与结构层次
大部分可见到的异常都是Exception的子类。
底部的Warning只是一个警告,不会报错,也不会终止程序的运行。
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
02. 异常处理
2.1 异常处理的基本概念
2.1.1 Python异常处理介绍
所谓异常处理,不是说把这个异常捕获到了,然后去改正这个错误,而是如果程序运行过程中出现了这个错误,Python程序需要用怎样的预备方案去应对。
比如一家发电厂因为一些原因停止发电了(程序运行出现异常导致程序运行中止),但是为了给负责的区域内供电保证正常的运行,发电厂一般都会有备用发电机,当主要发电机不工作时,可以采用备用发电机正常进行发电工作,这就类似于程序中的异常处理。
2.1.2 Python异常处理流程
Python解释器在正常情况下会逐行解释并执行相应的程序代码。
- 但是在执行过程中,如果遇到了错误,就会抛出这个错误对应的异常对象。
接着Python解释器会检测该异常是否被特定的结构所捕获。
Python中捕获异常有三种格式:
try-except
、try-except-finally
、try-except-else
。2.2.1 try-except捕获异常
try-except异常捕获的语法格式:
try:
可能出现异常的代码
except 异常类型1 as 变量名1:
预备方案1
except 异常类型2 as 变量名2:
预备方案2
……
except 异常类型n as 变量名n:
预备方案n
示例:有一个长度为4的列表
nums = [19, 27, 33, 56]
,试图打印索引位4的元素会报错。nums = [19, 27, 33, 56]
value = nums[4]
print(value)
"""
运行报错:
Traceback (most recent call last):
File "D:\Project\Python\BaseProject\Day03\global_variable_test.py", line 9, in <module>
value = nums[4]
IndexError: list index out of range
"""
- 此时,就可以捕获这个异常,并给出一个预备方法,如:若出现了下标越界异常,则打印这个列表最后一个元素。
try:
nums = [19, 27, 33, 56]
value = nums[4]
except IndexError as err:
print(err)
value = nums[-1]
print(value)
"""
运行结果:
list index out of range
56
"""
值得注意的是,写在try中的代码,如果出现异常并被捕获,则异常之后的代码也不会被执行。
try:
nums = [19, 27, 33, 56]
value = nums[4] # 这里会出现异常
ele = nums[0] # 这行代码不会被执行
except IndexError as err:
print(err)
value = nums[-1]
print(value)
print(ele) # 报错:NameError: name 'ele' is not defined
2.2.2 try-except-finally捕获异常
try-except-finally应用场景:
- try-except-finally结构中的try-except与2.2.1中没有区别,finally一般用来让程序与外面资源切断联系。
- 比如,对数据库的操作,不管try-except是否捕捉到了异常,与数据库的连接都要关闭。
try-except-finally异常捕获的语法格式:
try:
可能出现异常的代码
except 异常类型 as 变量名:
预备方案
finally:
不管是否出现异常都执行的操作
示例:用PyMySQL创建一个数据库连接,然后在这个过程中不管是否出现异常,数据库连接都应该被关闭。 ```python import pymysql
connect = None try:
# 简历数据库连接对象
connect = pymysql.Connect(
host='localhost',
port=3306,
user='root',
password='123456',
database='mysql_learning',
charset='utf8mb4'
)
print(connect)
except Exception as e: print(e) finally:
# 只要数据库连接不为空,那么不管try中的程序是否报错,connect都应该被关闭
if connect is not None:
connect.close()
<a name="NNTwQ"></a>
#### 2.2.3 try-except-else捕获异常
- else的运用位置概述:
- 之前的else都是声明在`if-else`、`if-elif-else`语句中的,当if和elif中所有的条件判断语句都为False时,会执行else中的语句。
- 此外,else还可以是`while-else`和`for-else`,当循环正常退出时(while循环的条件为False、for循环遍历完序列中所有元素时)就会执行else中的语句。
```python
s = ""
for i in "ABC":
s += i.lower()
else:
print(s)
"""
程序运行结果:abc
"""
i = 15
while i >= 10:
i -= 1
if i == 12:
break
else:
print(i)
"""
没有运行结果:程序是被break中止的,并非正常退出的,故else不执行。
"""
当else运用到try-except中时,是指try中的所有语句没有出现异常时,执行else中的语句。
try:
num1 = 10
num2 = 20
num3 = num1 + num2
except Exception as e:
print(e)
else:
print(num3)
"""
运行结果:30
try中的语句没有出现任何异常
故执行else中的print(num3)语句
"""
示例:输入一个整数,求它和10的差,只要录入的不是整数,就要一直录入下去,直到是整数为止,然后输出计算结果。
while True:
try:
num = int(input("请输入一个整数:"))
except ValueError as e:
print(e)
else:
result = num - 10
print(f"{result=}")
break
2.3 finally执行先于return
在函数中使用try-catch-finally结构时,finally的执行优先级会高于return语句。
- 示例:定义一个div函数,传入两个数,计算这两个数的商并写入到./result.txt文件中。若写入成功,则返回商,否则返回0。
```python
def div(num1, num2) -> int:
try:
except ZeroDivisionError as e:file = open("./result.txt", "w", encoding="utf-8")
result = num1 / num2 # 当num2为0时,会出现ZeroDivisionError异常。
file.write(f'{result}')
return result
finally:print("分母num2的值不能为0")
file.write(f"{0}")
return 0
print("程序执行完成,文件result.txt将被关闭")
file.close()
print(div(10, 2))
- 在第15行调用div函数处打断点,可以查看到整个函数的执行流程。
- 可以发现,当try中的代码运行到第6行的`return result`时,会直接跳到finally中执行finally结构中的语句。
- 当finally结构中的所有语句都执行完成后,会再跳回到第六行,然后将结果返回。
- 以上说明finally的执行优先级先于return语句。
![20220905_215741_Trim.gif](https://cdn.nlark.com/yuque/0/2022/gif/2692415/1662386508715-d03fcb3e-bae2-4bfb-8aaf-17955421ec5c.gif#averageHue=%23f2f3fc&clientId=ufae3a633-a831-4&from=drop&height=535&id=u0c685c90&originHeight=688&originWidth=1028&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1699867&status=done&style=none&taskId=u9211a9d2-2d46-465d-bf18-ffb65e0f770&title=&width=800)
<a name="KD51g"></a>
## 03. 抛出异常
- 抛出异常语法格式:`raise 异常类型(异常信息)`
- 示例:输入一个整型数字,当这个数字大于100时,抛出ValueError异常,异常信息为`num=value过大`。
```python
num = int(input("输入一个数字:"))
if num >= 100:
raise ValueError(f"{num=}过大")