python 错误处理
程序异常,就是程序出错了,程序员一般叫做 BUG,写程序不出错是不可能发生的事情,而程序员要做的事情就是及时的捕获错误,修改错误。
最常见的错误 - 除数为 0
在数学中也存在类似的问题,除数不可以为 0。相同的概念在编程中也是存在的。
num1 = 20
num2 = 0
num3 = num1 / num2
print(num3)
运行代码出现下面的错误:
♾️ python 代码:Traceback (most recent call last):
File "D:/gun/2/demo7.py", line 3, in <module>
num3 = num1 / num2
ZeroDivisionError: division by zero
错误内容就是末尾的 ZeroDivisionError: division by zero
,当出现错误程序崩溃,终止运行。错误异常中也提示了错误出现的行数 line 3
在第三行,但查看行号排查错误在很多时候无法直接解决问题,因为出错的地方不在行号那一行,修改 BUG
的效率一般会随着你对 Python
学习的深入逐步增强改善。
try … except 语句
刚才的程序出现错误就终止运行了,如何避免程序被强迫终止,出现问题提示出问题,然后继续运行呢?这就是 try … except
语句使用的场景了。
语法格式:
♾️ python 代码:try:
可能会出错的代码
except 异常对象:
处理异常代码
按照上述语法格式修改上文代码。
♾️ python 代码:num1 = 20
num2 = 0
try:
num3 = num1 / num2
except ZeroDivisionError:
print("除数不可以为 0 ")
此时程序不会报错,当发现除数为 0 会进入异常处理,直接输出除数不能为 0。
try
表示测试代码部分是否存在异常,except
表示捕获异常,前提是出现异常。如果 try
语句中没有任何错误,except 中的代码不会执行。
还有一点需要注意,在 except
后面是异常对象,该异常对象我们设置为 ZeroDivisionError 这是因为已经知道是会出现这个异常,如果在编码过程中不知道会出现哪种异常,依旧会出现错误。
num1 = 20
num2 = "abc"
try:
num3 = num1 / num2
except ZeroDivisionError:
print("除数不可以为 0 ")
上述代码依旧会报错,报错的异常为:
♾️ python 代码:Traceback (most recent call last):
File "D:/gun/2/demo7.py", line 4, in <module>
num3 = num1 / num2
TypeError: unsupported operand type(s) for /: 'int' and 'str'
如果想在 except
后面支持本异常,需要添加上 TypeError
。
num1 = 20
num2 = "abc"
try:
num3 = num1 / num2
except (ZeroDivisionError,TypeError):
print("除数不可以为 0 ")
也可以分开编写:
♾️ python 代码:num1 = 20
num2 = "abc"
try:
num3 = num1 / num2
except ZeroDivisionError:
print("除数不可以为 0 ")
except TypeError:
print("除数类型不对")
该种写法在书写的时候需要预先知道会提示哪种异常,如果异常不清楚那可以省略异常对象,直接使用下述代码即可。
♾️ python 代码:num1 = 20
num2 = "abc"
try:
num3 = num1 / num2
except:
print("除数不可以为 0 ")
try … except … else 语句
在 try … except
语句后面可以增加一个 else
语句,该语句表示的含义可以按照如下描述进行理解,当出现异常的时候执行 except
语句中的代码,当无异常执行 else
语句代码。
num1 = 20
num2 = 1
try:
num3 = num1 / num2
except ZeroDivisionError:
print("除数不可以为 0 ")
except TypeError:
print("除数类型不对")
else:
print("无异常,会被执行")
以上代码无错误,那 else 语句就会被执行到。
异常类型
常见的异常类型
在编写代码的过程中,你需要掌握一些常见的异常类型,熟记它们可以帮助你快速进行错误排查。
- AttributeError 某个对象没有属性
- Exception 通用型异常对象
- FileNotFoundError 找不到文件
- IOError 输入输出异常
- IndexError 索引异常
- KeyError 键异常
- NameError 对象名称异常
- SyntaxError 语法错误
- TypeError 类型错误
- ValueError 值错误
以上错误都属于常见错误,其中重点以 Exception
通用异常对象与 SyntaxError
语法错误为主,它们两个是最常出现的。
很多时候其实直接使用通用异常对象 Exception
就可以了,不需要记住所有的异常类型的。
捕捉多个异常
在上文已经接触过捕捉多个异常的语法格式了,可以在学习一下。
♾️ python 代码:try:
可能出错的代码块
except 异常对象1:
异常处理代码块
except 异常对象2:
异常处理代码块
一个 except 捕获多个异常
Python 也支持使用一个 except 捕获多个异常,具体语法格式如下:
try:
可能出错的代码块
except (异常对象1,异常对象2...):
异常处理代码块
直接抛出异常
捕获到异常之后,可以直接抛出 Python
给内置好的异常信息,例如:
num1 = 20
num2 = 0
try:
num3 = num1 / num2
except ZeroDivisionError as e:
print(e)
except TypeError as e:
print(e)
else:
print("无异常,会被执行")
注意 except
后面异常对象使用 as
关键字起了一个别名叫做 e
,然后直接输出 e
就是 Python
内置好的错误信息了。这里的 e
可以为任意名称,遵循变量命名规则即可。
finally 语句try … except
语句还可以和 finally
语句配合,形成下述语法格式:
try:
可能出错的代码块
except:
代码出错执行的代码块
else:
代码正常执行的代码块
finally:
无论代码是否有异常出现都会执行的的代码块
finally
语法需要与 try
语句配合使用,无论是否有异常出现都会执行该语句内容,具体代码大家可以自行测试即可。
日志模块 logging
logging 模块
在 Python 中为了更好的记录程序错误信息,提供了一个 logging
模块供我们使用,该模块提供了 5 个等级用于标记日志信息的等级。
- DEBUG 等级,使用 logging.debug() 显示
- INFO 等级,记录类的日志
- WARNING 等级,警告级别,存在潜在风险
- ERROR 等级,引发错误
- CRITICAL 等级,引发系统出现问题,最高等级
导入 logging
模块之后,可以使用下述内容设置显示信息的等级。
import logging
logging.basicConfig(level=logging.DEBUG)
五个等级输出函数如下:
♾️ python 代码:import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
输出内容如下:
♾️ python 代码:DEBUG:root:DEBUG
INFO:root:INFO
WARNING:root:WARNING
ERROR:root:ERROR
CRITICAL:root:CRITICAL
上述代码因为设置的等级是 DEBUG
,所以所有的日志信息都会输出,如果设置为 WARNING
,例如下述代码,查看输出内容。
import logging
# 注意看这里的设置
logging.basicConfig(level=logging.WARNING)
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
因为设置了 logging
输出等级是 WARNING
,所以较低等级的 DEBUG
与 INFO
将不再输出,这样可以随着程序开发不断的提高等级,最终提高到 CRITICAL
。
格式化 logging 日志信息
可以在全局进行 logging
信息的格式化,语法格式如下:
logging.basicConfig(level=logging.WARNING,format = "")
在不设置 format
的时候,默认输出的日志信息如下,所以输出内容前面都存在一个 DEBUG:root
: 内容,如果设置 format=""
即可删除原内容。
DEBUG:root:DEBUG
INFO:root:INFO
WARNING:root:WARNING
ERROR:root:ERROR
CRITICAL:root:CRITICAL
设置 format = ""
,代码如下:
import logging
logging.basicConfig(level=logging.WARNING,format= "")
其余内容不需要修改,输出的日志信息,已经没有前面的默认关键词了。
♾️ python 代码:WARNING
ERROR
CRITICAL
对于日志信息的格式化,还可以增加 asctime
,该内容为时间信息,例如下述代码:
import logging
logging.basicConfig(level=logging.WARNING,format= "%(asctime)s")
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
使用该内容运行结果发现,要输出的信息没有了,这是因为你在 format
参数中只传了 asctime
一个内容,如果还需要 logging
输出信息,需要增加 message
,语法如下:
import logging
logging.basicConfig(level=logging.WARNING,format= "%(asctime)s %(message)s")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")
学习过 asctime
与 message
之后,你应该对 format
格式化的语法有了一些基本的认知,它应该是一个 %(参数名)s
这样的结构,如果增加一个 logging
等级参数 levelname
,尝试一下你可以将其拼接到 format
中吗?
程序日志 logging 输出到文件中
程序日志如果都输出在 Python
控制台,导致的结果就是控制台出现大量的调试信息,很多时候可以将日志信息输出到文件中,而且实现非常简单,只需要增加一个参数 filename
即可解决问题。
import logging
logging.basicConfig(filename = "out.txt",level=logging.WARNING,format= "%(asctime)s %(message)s")
执行上述代码之后,会自动在当前根目录(文件与目录可以自己设置)生成一个日志文件。
停用程序日志
使用下述方法停用日志。
♾️ python 代码:logging.disable(level)
如果希望全部停用,只需要直接限制等级到 CRITICAL
即可。
import logging
logging.basicConfig(level=logging.WARNING,format= "%(asctime)s %(message)s")
logging.disable(level=logging.CRITICAL)
logging.debug("DEBUG")
logging.info("INFO")
logging.warning("WARNING")
logging.error("ERROR")
logging.critical("CRITICAL")