目录
- 一、认识异常
-
- [1.1 为什么要使用异常处理机制?](#1.1 为什么要使用异常处理机制?)
- [1.2 语法错误](#1.2 语法错误)
- [1.3 异常错误](#1.3 异常错误)
- [1.4 如何解读错误信息](#1.4 如何解读错误信息)
- 二、异常处理
-
- [2.1 异常的捕获](#2.1 异常的捕获)
- [2.2 Python内置异常](#2.2 Python内置异常)
- [2.3 捕获多个异常](#2.3 捕获多个异常)
- [2.4 raise语句与as子句](#2.4 raise语句与as子句)
- [2.5 使用traceback查看异常](#2.5 使用traceback查看异常)
- [2.6 try...except...else语句](#2.6 try…except…else语句)
- [2.7 try...except...finally语句--捕获异常](#2.7 try…except…finally语句--捕获异常)
- [2.8 语句嵌套和捕获](#2.8 语句嵌套和捕获)
- [2.9 总结](#2.9 总结)
- 三、程序调试
-
- [3.1 使用print(插桩)](#3.1 使用print(插桩))
- [3.2 assert语句--应用断言调试程序](#3.2 assert语句--应用断言调试程序)
- [3.3 使用logging](#3.3 使用logging)
- 四、练习
在程序运行过程中,难免会遇到各种各样的问题,有些问题是开发人员疏忽造成的,有些问题是用户操作失误造成的,还有一些问题是在程序运行过程中无法预测的原因导致的,如写入文件时硬盘空间不足,从网络抓取数据时网络掉线等。Python 内置了一套异常处理机制,帮助开发人员妥善处理各种异常,避免程序因为这些问题而终止运行。本章将讲解如何在 python 程序中处理异常和进行程序调试。
学习重点:
- 认识异常
- 使用 try 语句捕获异常
- 自定义异常
- 正确调试程序
一、认识异常
1.1 为什么要使用异常处理机制?
在程序运行的过程中,或多或少会遇到各种错误。如果发生了错误,最简单的应对方法是设计返回一个错误提示,这样能够知道出错的原因。例如,当调用函数 open() 打开文件时,定义出错时返回 -1。但是在复杂的程序设计中,如果完全依赖这种返回信息,开发人员就必须额外编写大量的检验代码,规定各种错误的返回值,以便能够识别错误,显然这是一种很笨拙的方法。Python 内置了一套异常处理机制,它能够降低程序开发的复杂度。当预测某些代码可能会出错时,使用 try 语句块来运行这些代码。
- 如果发生了错误,则不会再继续执行,直接跳转至 except 语句块进行错误处理。
- 如果没有错误发生,则 except 语句块不会被执行。
- 如果发生了不同类型的错误,可以设计不同的 except 语句块来分别处理不同的错误。
使用 try...except 语句组合来捕获错误,还可以跨越多层调用。例如,假设在函数 main() 中调用函数 foo(),在函数 foo() 中调用函数 bar(),结果调用函数 bar() 时出错了,这时如果在 main() 函数中能够捕获到错误,然后立即进行处理,这样就不需要在每个可能出错的函数中去捕获错误。只要在合适的层次上捕获错误就可以了,从而减少错误跟踪的麻烦。
1.2 语法错误
在编写 Python 代码的过程中,常见的错误有两种类型:语法错误和异常错误。下面先介绍语法错误,语法错误就是程序的写法不符合编程语言的规则,对于语法错误,在编写程序的过程中应该努力避免,在程序调试中消除。Python 常见的语法错误如下:
- 拼写错误: 关键字拼写错误会提示 SyntaxError 错误(语法错误),变量名、函数名拼写错误会提示 NameError 错误等。
- 不符合语法规范: 例如,多加或缺少括号、冒号等符号,以及表达式书写错误等。
- 缩写错误: Python 以缩进作为代码块的主要标志。在同一个程序或者项目中,应该保持相同的缩进风格。
1.3 异常错误
异常就是在程序执行过程中,发生的超出预期的事件,在异常事件发生时,将会影响程序的正常执行。一般情况下,当 Python 程序无法正常处理时,就会发生一个异常。出现异常的原因:
- 在程序设计过程中,由于疏忽或者考虑不周造成的错误。这时可以在调试或运行过程中根据异常提示信息,找到出错的位置和代码,并对错误代码进行分析、测试和排错。
- 有些异常是不可避免的,如读写超出权限、内存溢出、传入的参数类型不一致等。对于这类异常,可以主动对异常进行捕获,并妥善进行处理,防止程序意外终止。
- 主动抛出一个异常。在程序设计中,用户也可以主动抛出异常,提示用户注意特定场景下程序会无法继续执行。
当 Python 脚本发生异常时,需要捕获异常,并妥善处理。如果异常未被处理,程序将会终止运行,因此为程序添加异常处理,能使程序更健壮。
1.4 如何解读错误信息
出错并不可怕,可怕的是不知道如何排除错误。正确解读错误信息是排除错误的第一步。【示例】下面结合示例介绍如何跟踪异常,请先阅读如下示例代码:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 11:37
# @Author : AmoXiang
# @File : test.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def a(s): # 自定义函数a
return 10 / int(s) # 数学运算
def b(s): # 自定义函数b
return a(s) * 2 # 调用函数a
def c(): # 自定义函数c
b('0') # 调用函数b
c() # 调用函数c
如果错误没有被捕获,它就会一直往上抛出异常,最后被 Python 解释器捕获,打印错误信息,然后退出程序。执行本示例程序,结果如下图所示:
分析:
- 第1行,提示用户这是错误的跟踪信息
- 第2、3行,调用函数 c() 出错,出错位置在文件 test.py 第20行
- 第4、5行,调用函数
b('0')
出错,出错位置在文件 test.py 第17行 - 第6、7行,在文件 test.py 第13行,执行 return a(s) * 2 代码时出错
- 第8、9行,在文件 test.py 第9行,执行 return 10 / int(s) 代码时出错
- 第10行,打印错误信息,根据错误类型 ZeroDivisionError,可以判断 int(s) 本身没有出错,但是 int(s) 返回0,在计算 10/0 时出错,除数不能够为0。至此,找到错误的源头并进行预防。
二、异常处理
当发生异常时,需要对异常进行捕获,然后进行妥善处理。Python 提供了多个异常处理语句,方便开发者使用。
2.1 异常的捕获
在 Python 中,提供了 try...except 语句捕获并处理异常。在使用时,把可能产生异常的代码放在 try 语句块中,把处理结果放在 except 语句块中,这样,当 try 语句块中的代码出现错误时,就会执行 except 语句块中的代码,如果 try 语句块中的代码没有错误,那么 except 语句块将不会执行。具体的语法格式如下:
python
try:
block1
except [ExceptionName [as alias]]:
block2
参数说明:
1.block1: 表示可能出现错误的代码块
2.ExceptionName [as alias]: 可选参数,用于指定要捕获的异常。
其中,ExceptionName表示要捕获的异常名称,如果在其右侧加上as alias,则表示为当前的异常指定一个别名,通过该别名,可以记录
异常的具体内容。
3.说明: 在使用try...except语句捕获异常时,如果在except后面不指定异常名称,则表示捕获全部异常。
4.block2: 表示进行异常处理的代码块。在这里可以输出固定的提示信息,也可以通过别名输出异常的具体内容,
同样可以忽略异常什么都不做或者进行补救。
5.说明: 使用try...except语句捕获异常后,当程序出错时,输出错误信息后,程序会继续执行。
示例:
python
# -*- coding: utf-8 -*-
# @Time : 2024-10-25 9:18
# @Author : AmoXiang
# @File: 3.异常捕获.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def foo():
try:
print('before')
c = 1 / 0
print('after')
except:
print('catch u')
print('finished')
foo()
print('==== end ====')
上例执行到 c = 1/0 时产生异常并抛出,由于使用了 try...except 语句块则捕捉到了这个异常,异常生成位置之后语句将不再执行,转而执行对应的 except 部分的语句,最后执行 try...except 语句块之外的语句。捕获指定类型的异常:
python
# -*- coding: utf-8 -*-
# @Time : 2024-10-25 9:26
# @Author : AmoXiang
# @File: 4.捕获指定异常.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def foo():
try:
print('before')
c = 1 / 0
print('after')
# except ArithmeticError: # 指定捕获的类型
except ZeroDivisionError: # 指定捕获的类型
print('catch u')
print('finished')
foo()
'''
before
catch u
finished
==== end ====
'''
print('==== end ====')
使用 Exception 类型可以捕获所有常规类型:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 12:02
# @Author : AmoXiang
# @File : 4-1.捕获指定的异常.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
try:
f = open('a.txt', 'r') # 打开并不存在的文件
except Exception as e: # 捕获IOError类型异常
print('错误编号: %s,错误信息: %s' % (e.errno, e.strerror)) # 显示错误信息
当发生异常时,在 try 语句块中异常发生点后的剩余语句永远不会被执行,解释器将寻找最近的 except 语句进行处理,如果没有找到合适的 except 语句,那么异常就会向上传递。如果在上一层中也没找到合适的 except 语句,该异常会继续向上传递直到找到合适的处理器。如果到达顶层仍然没有找到合适的处理器,那么就认为这个异常是未处理的,Python 解释器就会抛出异常,同时终止程序运行。【示例】 下面示例设计了一个多层嵌套的异常处理结构,演示异常传递的过程。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 12:42
# @Author : AmoXiang
# @File : 5.多层嵌套的异常处理结构.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
try:
try:
try:
f = open('a.txt', 'r') # 打开并不存在的文件
except NameError as e: # 捕获未声明的变量的异常
print('NameError') # 显示错误信息
except IndexError as e: # 捕获索引超出列表范围的异常
print('IndexError') # 显示错误信息
except IOError as e: # 捕获输入/输出的异常
print('IOError') # 显示错误信息
# 最后输出结果是什么呢?
2.2 Python内置异常
在 Python 中,异常也是一种类型,所有的异常对象都继承自 BaseException 基类,所以使用 except 语句不但可以捕获该类型的错误,还可以捕获所有子类型的错误。用户自定义的异常并不直接继承 BaseException,所有的异常类都是从 Exception 继承,且都在 exceptions 模块中定义。Python 自动将所有异常名称放在内建命名空间中,所以程序不必导入 exceptions 模块即可用异常。Python 内置了很多异常,可以向用户准确反馈出错信息。Python 内置异常类型说明如下所示,其中缩进排版的层级表示异常之间的继承关系。
python
BaseException # 所有异常的基类
|---SystemExit # 解释器请求退出
|---KeyboardInterrupt # 用户中断执行(通常是输入^C)
|---GeneratorExit # 生成器(generator)发生异常来通知退出
|---Exception # 常规异常的基类
|---StopIteration # 迭代器没有更多的值
|---StopAsyncIteration # 必须通过异步迭代器对象的__anext__()方法引发以停止迭代
|---ArithmeticError # 各种算术错误引发的内置异常的基类
|---FloatingPointError # 浮点计算错误
|---OverflowError # 数值运算结果太大无法表示
|---ZeroDivisionError # 除(或取模)零(所有数据类型)
|---AssertionError # 当assert语句失败时引发
|---AttributeError # 属性引用或赋值失败
|---BufferError # 无法执行与缓冲区相关的操作时引发
|---EOFError # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发
|---ImportError # 导入模块/对象失败
|---ModuleNotFoundError # 无法找到模块或在sys.modules中找到None
|---LookupError # 映射或序列上使用的键或索引无效时引发的异常的基类
|---IndexError # 序列中没有此索引(index)
|---KeyError # 映射中没有这个键
|---MemoryError # 内存溢出错误(对于Python 解释器不是致命的)
|---NameError # 未声明/初始化对象(没有属性)
|---UnboundLocalError # 访问未初始化的本地变量
|---OSError # 操作系统错误,EnvironmentError、IOError、WindowsError、socket.error、select.error和mmap.error已合并到OSError中,构造函数可能返回子类
|---BlockingIOError # 操作将阻塞对象(e.g. socket)设置为非阻塞操作
|---ChildProcessError # 在子进程上的操作失败
|---ConnectionError # 与连接相关的异常的基类
|---BrokenPipeError # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入
|---ConnectionAbortedError # 连接尝试被对等方中止
|---ConnectionRefusedError # 连接尝试被对等方拒绝
|---ConnectionResetError # 连接由对等方重置
|---FileExistsError # 创建已存在的文件或目录
|---FileNotFoundError # 请求不存在的文件或目录
|---InterruptedError # 系统调用被输入信号中断
|---IsADirectoryError # 在目录上请求文件操作(如os.remove())
|---NotADirectoryError # 在不是目录的事物上请求目录操作(例如 os.listdir())
|---PermissionError # 尝试在没有足够访问权限的情况下运行操作
|---ProcessLookupError # 给定进程不存在
|---TimeoutError # 系统函数在系统级别超时
|---ReferenceError # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象
|---RuntimeError # 在检测到不属于任何其他类别的错误时触发
|---NotImplementedError # 在用户定义的基类中,抽象方法要求派生类重写该方法或者正在开发的类指示仍然需要添加实际实现
|---RecursionError # 解释器检测到超出最大递归深度
|---SyntaxError # Python语法错误
|---IndentationError # 缩进错误
|---TabError # Tab和空格混用
|---SystemError # 解释器发现内部错误
|---TypeError # 操作或函数应用于不适当类型的对象
|---ValueError # 操作或函数接收到具有正确类型,但值不合适的参数
|---UnicodeError # 发生与Unicode相关的编码或解码的错误
|---UnicodeDecodeError # Unicode解码错误
|---UnicodeEncodeError # Unicode编码错误
|---UnicodeTranslateError # Unicode转码错误
|---Warning # 警告的基类
|---DeprecationWarning # 有关已弃用功能的警告的基类
|---PendingDeprecationWarning # 有关不推荐使用功能的警告的基类
|---RuntimeWarning # 有关可疑的运行时行为的警告的基类
|---SyntaxWarning # 关于可疑语法警告的基类
|---UserWarning # 用户代码生成警告的基类
|---FutureWarning # 有关已弃用功能的警告的基类
|---ImportWarning # 关于模块导入时可能出错的警告的基类
|---UnicodeWarning # 与Unicode相关的警告的基类
|---BytesWarning # 与bytes和bytearray相关的警告的基类
|---ResourceWarning # 与资源使用相关的警告的基类。被默认警告过滤器忽略。
BaseException: 所有内建异常类的基类是 BaseException。
SystemExit: sys.exit() 函数引发的异常,异常不捕获处理,就直接交给 Python 解释器,解释器退出。示例:
python
# 捕获这个异常
import sys
try:
print('before')
sys.exit(1)
print('after')
except SystemExit: # 换成Exception能否捕获
print('SysExit')
'''
before
SysExit
outer
'''
print('outer') # 是否执行?
如果 except 语句捕获了该异常,则继续向后面执行,如果没有捕获住该异常 SystemExit,解释器直接退出程序,注意捕获前后程序退出状态码的变化,捕获的状态码 ⇒ exit code 0,未捕获的状态码 ⇒ exit code 1
KeyboardInterrupt: 对应的捕获用户中断行为 Ctrl + C。
python
import time
try:
while True:
time.sleep(1)
print('running')
except KeyboardInterrupt:
print("Ctrl + c")
print('=' * 30)
运行结果如下图所示:
Exception: Exception 是所有内建的、非系统退出的异常的基类,自定义异常类应该继承自它。
SyntaxError: SyntaxError 语法错误,Python 将这种错误也归到异常类下面的 Exception 下的子类,但是这种错误是不可捕获的:
python
def a():
try:
0a = 5
except:
pass
a()
ArithmeticError: 所有算术计算引发的异常,其子类有除零异常等
LookupError: 使用映射的键或序列的索引无效时引发的异常的基类:IndexError、KeyError
自定义异常类型: 在 Python 中,异常也是一种类型,捕获异常就是获取它的一个实例。用户可以自定义异常类型,以适应个性化开发的需要,自定义异常类型应该直接或者间接继承自 Exception 类。【示例】: 自定义异常类型,设置基类为 Exception,方便在异常触发时输出更多的信息。raise 语句的使用方法见 2.4 raise语句与as子句 小节。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 13:33
# @Author : AmoXiang
# @File : 11.自定义异常类型.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
class MyError(BaseException): # 自定义错误类型
def __init__(self, msg): # 重构类型构造函数
self.msg = msg
def __str__(self): # 重构类型标识函数
return self.msg
try:
raise MyError("自定义错误信息") # 主动抛出自定义错误
except MyError as e:
print(e.args) # 显示错误信息
2.3 捕获多个异常
捕获多个异常有两种方式,具体说明如下:
-
在一个 except 子句中包含多个异常,多个异常以元组的形式进行设置。语法格式如下:
pythontry: 语句块 # 可能产生异常的代码 except (<异常名1>, <异常名2>, <异常名3>,...) [as 别名]: 语句块 # 对异常进行处理的代码
-
使用多个 except 子句处理多个异常,多个异常之间存在优先级。语法格式如下:
pythontry: 语句块 # 可能产生异常的代码 except <异常名1> [as 别名1]: 语句块 # 对异常进行处理的代码 except <异常名2> [as 别名2]: 语句块 # 对异常进行处理的代码 except <异常名3> [as 别名3] 语句块 # 对异常进行处理的代码 ....
当使用多个 except 子句时,这种异常处理语法的解析过程如下:
- 捕获是从上到下依次比较,如果匹配,则执行匹配的 except 语句块
- 如果被一个 except 语句捕获,其他 except 语句就不会再次捕获了
- 如果没有任何一个 except 语句捕获到这个异常,则该异常向外抛出
- except:称为缺省捕获,缺省捕获必须是最后一个捕获语句
捕获的原则从小到大,从具体到宽泛。 示例1:
python
# -*- coding: utf-8 -*-
# @Time : 2024-10-25 9:52
# @Author : AmoXiang
# @File: 5.多种捕获.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
class MyException(Exception):
pass
try:
a = 1 / 0
raise MyException()
open('t')
sys.exit(1)
except ZeroDivisionError:
print('zero')
except ArithmeticError:
print('arith')
except MyException: # 捕获自定义异常
print('catch u')
except Exception:
print('exception')
except: # 写在最后,缺省捕获
print('error')
'''
zero
====end====
'''
print('====end====')
示例2:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 13:09
# @Author : AmoXiang
# @File : 5.多种异常捕获示例2.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
str1 = 'hello world' # 字符串
try:
int(str1) # 传入非法的值
except IndexError as e: # 捕获IndexError异常
print(e.__str__)
except KeyError as e: # 捕获KeyError异常
print(e.__str__)
except ValueError as e: # 捕获ValueError异常
print(e.__str__)
# '<method-wrapper '__str__' of ValueError object at 0x00000179C5C5E6E0>'
示例3: 使用 raise 语句针对不同情况抛出不同异常。对登录信息进行分项验证,哪项不合法,就使用 raise 语句抛出 ValueError 异常,并且应用 try...except 语句处理异常,代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 13:08
# @Author : AmoXiang
# @File : 10.raise语句.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
try:
username = input('请输入用户名(不超过16个字符): ')
pwd = input('请输入密码(只能由字母和数字组成): ')
if len(username) > 16: # 如果用户超过16个字符
raise ValueError('用户名输入不合法,不能超过16个字符!') # 抛出ValueError异常
if not pwd.isalnum():
raise ValueError('密码输入不合法,不能包括除了数字和字母以外的字符!') # 抛出ValueError异常
except ValueError as e:
print(e)
2.4 raise语句与as子句
使用 raise 语句可以主动抛出一个异常,语法格式如下:
python
raise [Exception [, args [, Traceback]]]
# 1.Exception表示异常的类型,如: ZeroDivisionError
# 2.args是一个异常参数,该参数是可选的,默认为None
# 3.traceback 表示跟踪异常的回溯对象,也是可选参数
使用 raise 语句可以确保程序根据开发人员的设计逻辑进行运行,如果偏离了轨道,可以主动抛出异常,结束程序的运行。先看一个例子:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 13:08
# @Author : AmoXiang
# @File : 10.raise语句.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def test(num):
try:
if type(num) != int: # 如果为非数字的值,则抛出TypeError错误
raise TypeError('参数不是数字')
if num <= 0: # 如果为非正整数,则抛出ValueError错误
raise ValueError('参数为大于0的整数')
print(num) # 打印数字
# 被抛出的异常,应该是异常类的实例,如何获得这个对象呢?使用 as 子句:
except Exception as e:
print(e) # 打印错误信息
test("1") # 参数不是数字
test(0) # 参数为大于0的整数
test(2) # 2
raise 真的什么类型都能抛出吗?
python
# -*- coding: utf-8 -*-
# @Time : 2024-10-25 9:58
# @Author : AmoXiang
# @File: 6.as子句.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
# raise 能抛出什么样的异常?
class A: pass
try:
# 1/0
raise 1
# raise "abc"
# raise A
# raise A()
# raise {}
except Exception as e: # 写在最后,缺省捕获
print(type(e), e) # 抛出TypeError类型异常实例
'''
<class 'TypeError'> exceptions must derive from BaseException
====end====
'''
print('====end====')
raise 语句:
- raise 后要求应该是 BaseException 类的子类或实例,如果是类,将被无参实例化。自定义应该是 Exception 子类
- raise 后什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛类型异常。这种方式较少用,它用在 except 中
2.5 使用traceback查看异常
Python 能够通过 traceback 对象跟踪异常,记录程序发生异常时有关函数调用的堆栈信息。具体用法格式如下:
python
import traceback # 导入traceback模块
try:
代码块
except:
print(traceback.print_exc()) # 打印回溯信息
使用 traceback 对象之前,需要导入 traceback 模块。调用 traceback 对象的 print_exc() 方法可以在控制台打印详细的错误信息。如果希望获取错误信息,可以使用 print_exc() 对象的 format_exc() 方法,它会以格式化字符串的形式返回错误信息,与 print_exc() 方法打印的信息完全相同。【示例1】设计一个简单的异常处理代码段。
python
try:
1 / 0 # 制造错误
except ZeroDivisionError as e: # 捕获异常
print(e) # 打印异常信息
上面错误信息无法跟踪异常发生的位置,在哪个文件、哪个函数、哪一行代码出现异常。【示例2】针对示例1,下面使用 traceback 模块来跟踪异常。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 13:37
# @Author : AmoXiang
# @File : 12.traceback使用.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
import traceback # 导入traceback模块
try:
1 / 0 # 制造错误
except ZeroDivisionError as e: # 捕获异常
traceback.print_exc() # 打印traceback对象信息
运行程序,输出结果如下:
这样就可以帮助用户在程序中回溯到出错点的位置。print_exc() 方法可以把错误信息直接保存到外部文件中。语法格式如下:
python
traceback.print_exc(file=open('文件名', '模式', encoding='字符编码'))
【示例3】以示例2为基础,修改最后一行代码,打开或创建一个名为 error.log 的文件,以追加形式填入错误信息。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 13:37
# @Author : AmoXiang
# @File : 12.traceback使用.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
import traceback # 导入traceback模块
try:
1 / 0 # 制造错误
except ZeroDivisionError as e: # 捕获异常
# 把错误信息保存到当前目录下log.log文件中
traceback.print_exc(file=open('error.log', mode='a', encoding='utf-8'))
2.6 try...except...else语句
try...except...else 语句也是异常处理结构,它是在 try...except 语句的基础上再添加一个 else 子句,用于指定当 try 语句块中没有发现异常时要执行的语句块。当 try 语句中发现异常时,该语句块中的内容将不被执行。语法格式如下:
python
try:
block1
except [ExceptionName [as alias]]:
block2
else:
block3
参数说明:
# 1.block1: 表示可能出现错误的代码块
# 2.ExceptionName [as alias]: 可选参数,用于指定要捕获的异常。其中,ExceptionName表示要捕获的异常名称,
# 如果在其右侧加上as alias则表示为当前的异常指定一个别名,通过该别名,可以记录异常的具体内容。
# 3.block2: 表示进行异常处理的代码块。在这里可以输出固定的提示信息,也可以通过别名输出异常的具体内容。
# 4.block3: 没有产生异常时执行的语句块
【示例1】捕获异常并在没有异常时给出提示。在执行除法运算时,捕获异常,并且实现当程序没有抛出异常时,输出运算结果,代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 18:51
# @Author : AmoXiang
# @File : 13.else子句.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def division():
num1 = int(input('请输入被除数: ')) # 用户输入提示,并记录
num2 = int(input('请输入除数: '))
return num1 // num2 # 执行除法运算
if __name__ == '__main__':
try: # 捕获异常
result = division() # 调用函数
except ZeroDivisionError: # 处理异常
'''
请输入被除数: 5
请输入除数: 0
出错了:除数不能为0!
'''
print('\n出错了: 除数不能为0!')
except ValueError as e: # 处理ValueError异常
print('输入错误: ', e) # 输出错误原因
else: # 没有抛出异常时执行
'''
请输入被除数: 20
请输入除数: 5
结果为: 4
'''
print('结果为: ', result)
【示例2】在编写学生信息管理系统时,如果想要对输入的信息进行整体验证,可以使用 try 语句捕获异常,当出现转换异常时,输出提示信息,否则说明正确,输出结果,代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 18:51
# @Author : AmoXiang
# @File : 13.else子句.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
while True:
try: # 捕获异常
english = int(input('请输入英语成绩: '))
python = int(input('请输入Python成绩: '))
c = int(input('请输入C语言成绩: '))
except: # 处理异常
print('输入无效,不是整型数据,请重新录入信息!')
continue # 继续循环
else: # 没有抛出异常时执行
print('英语: ', english, '\tPython: ', python, '\tC语言: ', c)
break # 跳出循环
'''
请输入英语成绩: 12.6
输入无效,不是整型数据,请重新录入信息!
请输入英语成绩: 100
请输入Python成绩: 99
请输入C语言成绩: 100
英语: 100 Python: 99 C语言: 100
'''
2.7 try...except...finally语句--捕获异常
完整的异常处理语句应该包含 finally 代码块,通常情况下,无论程序中有无异常产生,finally 代码块中的代码都会被执行。其基本格式如下:
python
try:
block1
except [ExceptionName [as alias]]:
block2
finally:
block3
对于 try...except...finally 语句的理解并不复杂,它只是比 try...except 语句多了一个 finally 语句,如果程序中有一些在任何情形中都必须执行的代码,那么就可以将它们放在 finally 语句的区块中。ps: 使用 except 子句是为了允许处理异常。无论是否引发了异常,使用 finally 子句都可以执行清理代码。如果分配了有限的资源(如打开文件),则应将释放这些资源的代码放置在 finally 块中。
【示例1】在捕获异常时应用 finally 子句输出指定的提示信息。在执行除法运算时,实现当 division() 函数在执行时无论是否抛出异常,都输出文字 "执行了除法运算"
,代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 19:00
# @Author : AmoXiang
# @File : 14.try...except...finally.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def division():
num1 = int(input('请输入被除数: ')) # 用户输入提示,并记录
num2 = int(input('请输入除数: '))
return num1 // num2 # 执行除法运算
if __name__ == '__main__':
try: # 捕获异常
result = division() # 调用函数
except ZeroDivisionError: # 处理异常
print('\n出错了: 除数不能为0!')
except ValueError as e: # 处理ValueError异常
print('输入错误: ', e) # 输出错误原因
else: # 没有抛出异常时执行
print('结果为: ', result)
finally: # 无论是否抛出异常都执行
print('\n执行了除法运算!')
'''
正常情况:
请输入被除数: 365
请输入除数: 12
结果为: 30
执行了除法运算!
异常情况:
请输入被除数: 10
请输入除数: 0
出错了: 除数不能为0!
执行了除法运算!
'''
【示例2】在连接数据库时应用 try...except...finally 子句。在连接数据时,需要应用 try...except 语句捕获异常,并且无论是否抛出异常,都需要关闭数据库连接,这时就需要应用 try...except...finally 语句实现。在下面的代码,需要在 else 子句中,再嵌套一个 try...except...finally 处理创建数据表时可能出现的异常,代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 19:00
# @Author : AmoXiang
# @File : 14.try...except...finally.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
import sqlite3 # 导入sqlite3模块
conn = None
try:
# 连接到SQLite数据库
# 数据库文件是amo.db,如果文件不存在,会自动在当前目录创建
conn = sqlite3.connect('amo.db')
except Exception as e: # 处理异常
print(e)
else:
cursor = None
try:
cursor = conn.cursor() # 创建一个Cursor
# 执行一条SQL语句,创建user表
cursor.execute('create table user(id int(10) primary key,name varchar(20))')
except Exception as e1: # 处理异常
print(e1)
finally: # 无论是否抛出异常都执行
if cursor:
cursor.close() # 关闭游标
finally: # 无论是否抛出异常都执行
if conn:
conn.close() # 关闭Connection
注意上例中的 conn 与 cursor 的作用域,解决的办法是在外部定义。finally 中一般放置资源的清理、释放工作的语句。也可以在 finally 中再次捕捉异常:
python
try:
f = open('test.txt')
except Exception as e:
print('{}'.format(e))
finally:
print('清理工作')
try:
f.close()
except Exception as e:
print(e)
'''
[Errno 2] No such file or directory: 'test.txt'
清理工作
name 'f' is not defined
'''
小结:
- 一般在 finally 子句中可以设计善后处理工作。例如,关闭已经打开的文件、断开数据库连接、释放系统资源,或者保存文件,避免数据丢失等。
- try 语句必须跟随一个 except 子句,或者跟随一个 finally 子句,也可以同时跟随
- else 子句是可选的,但是如果设计 else 子句,则必须至少设计一个 except 子句
- try、except、else、finally 这4个关键字的位置顺序是固定的,不可随意调换
2.8 语句嵌套和捕获
异常语句内部可以嵌入到 try 块、except 块、finally 块中,异常在内部产生后,如果没有捕获到,就会继续向外部抛出,如果外部也没能捕获,将继续再向外部抛出,直至异常代码所在线程,导致线程崩溃。finally 中有 return、break 语句,则异常就不会继续向外抛出。示例:
python
# -*- coding: utf-8 -*-
# @Time : 2024-10-25 10:07
# @Author : AmoXiang
# @File: 8.语句嵌套与捕获.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
try:
try:
1 / 0
except KeyError as e:
print(1, e)
finally:
print(2, 'inner fin')
except FileNotFoundError as e:
print(3, e)
finally:
print(4, 'outer fin')
'''
2 inner fin
4 outer fin
Traceback (most recent call last):
File "D:\Code\dream\PythonStudy\base\day08-异常处理\8.语句嵌套与捕获.py", line 10, in <module>
1 / 0
ZeroDivisionError: division by zero
'''
抑制异常抛出的示例:
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 19:21
# @Author : AmoXiang
# @File : 15.return抑制异常的抛出.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def test():
try:
raise ValueError("发生异常")
except Exception as e:
print("捕获异常:", e)
raise # 原本这里抛出异常
finally:
print("进入 finally")
return "finally 的 return 覆盖了异常" # 这个 return 会抑制上面 raise 抛出的异常
result = test()
print("函数返回值:", result)
2.9 总结
python
try:
<语句> #运行别的代码
except <异常类>:
<语句> # 捕获某种类型的异常
except <异常类> as <变量名>:
<语句> # 捕获某种类型的异常并获得对象
else:
<语句> #如果没有异常发生
finally:
<语句> #退出try时总会执行
如果 try 中语句执行时发生异常,搜索 except 子句,并执行第一个匹配该异常的 except 子句,如果 try 中语句执行时发生异常,却没有匹配的 except 子句,异常将被递交到外层的 try,如果外层不处理这个异常,异常将继续向外层传递。如果都不处理该异常,则会传递到最外层,如果还没有处理,就终止异常所在的线程,如果在 try 执行时没有发生异常,如有 else 子句,可执行 else 子句中的语句,无论 try 中是否发生异常, finally 子句最终都会执行。
三、程序调试
在程序开发过程中,免不了会出现一些错误,有语法方面的,也有逻辑方面的。对于语法方面的比较好检测,因为程序会直接停止,并且给出错误提示。而对于逻辑错误就不太容易发现了,因为程序可能会一直执行下去,但结果是错误的,我们需要知道出错状态下,相关变量的动态变化情况,哪些值是正确的,哪些值是错误的,以此分析出错的具体原因。所以作为一名程序员,掌握一定的程序调试方法,可以说是一项必备技能,本小节简单介绍各种调试工具,帮助用户快速修复错误。
3.1 使用print(插桩)
使用 print() 函数把可能有问题的变量打印出来,然后诊断变量的值是否符合逻辑。这是最简单、最直接的调试方法。【示例】在本示例中,为了方便排错,在变量n赋值之后临时插入一行代码:print(>>> n=%d%n),打印出n的值,看能否找出具体出错的原因。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 19:33
# @Author : AmoXiang
# @File : 16.print调试程序.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def foo(s):
n = int(s)
print('>>> n = %d' % n)
return 10 / n
def main():
foo('0')
main()
执行程序后,在输出中查找变量n的打印值。
python
>>> n = 0
Traceback (most recent call last):
File "E:\projects\pyCode2025\base\day08-异常处理\16.print调试程序.py", line 18, in <module>
main()
File "E:\projects\pyCode2025\base\day08-异常处理\16.print调试程序.py", line 15, in main
foo('0')
File "E:\projects\pyCode2025\base\day08-异常处理\16.print调试程序.py", line 11, in foo
return 10 / n
~~~^~~
ZeroDivisionError: division by zero
使用 print() 方法的最大缺点是:临时插入与程序无关的代码,后期发布时还得删掉它,如果脚本到处都是 print,后期清理就比较麻烦。在实际工作调试中,在哪里进行 print 调试,是有技巧的,这个就需要不断地积累经验,有时候 print 调试比断点调试来得更方便直接,特别是在有多线程等并发程序下。
3.2 assert语句--应用断言调试程序
assert 的中文意思是断言,它一般用于对程序某个时刻必须满足的条件进行验证。assert 语句的基本语法如下:
python
assert expression [,reason]
参数说明:
# 1.expression: 条件表达式,如果该表达式的值为真时,什么都不做,如果为假时,则抛出AssertionError异常。
# 2.reason: 可选参数,用于对判断条件进行描述,为了让他人更好地知道出现了什么问题
# 相当于
if not expression:
raise AssertionError(reason)
# 凡是可以使用 print 来辅助查看的地方,都可以使用断言(assert)来替代
针对上一小节示例,可以使用以下方式进行调试。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 19:36
# @Author : AmoXiang
# @File : 17.assert调试程序.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
main()
运行结果如下图所示:
assert 会检测表达式 n != 0 是否为 True,如果为 False,则诊断出错,assert 语句就会抛出 AssertionError 异常。提示:启动 Python 解释器时,可以使用 -O 参数关闭 assert,关闭之后,可以把所有的 assert 语句当成 pass 语句忽略掉。
3.3 使用logging
logging(日志)模块用于跟踪程序的运行状态。它把程序的运行状态划分为不同的级别,按严重程度递增排序说明如下:
python
DEBUG: 调试状态
INFO: 正常运行状态
WARNING: 警告状态.在运行时遇到意外问题,如磁盘空间不足等,但是程序将会正常运行
ERROR: 错误状态.在运行时遇到严重的问题,程序已不能执行部分功能了
CRITICAL: 严重错误状态。在运行时遇到严重的异常,表明程序已不能继续运行了
默认等级为 WARNING,这意味着仅在这个级别或以上的事件发生时才会反馈信息。用户也可以调整响应级别。logging 提供了一组日志函数:debug()、info()、warning()、error() 和 critical()。在代码中可以调用这些函数,当相应级别的事件发生时,将会执行该级别或以上级别的日志函数。日志函数被执行时,将会把事件发生的相关信息打印到控制台,或者写入到指定的文件中,方便开发人员在调试时进行参考。【示例1】本示例演示将日志信息打印在控制台上。
python
import logging # 导入日志模块
logging.debug('debug信息')
logging.warning('只有这个会输出......')
logging.info('info信息')
由于默认设置的等级是 warning,所以只有 warning 的信息被输出到控制台上。打印信息如下:
【示例2】本示例使用 logging.basicConfig() 方法设置日志信息的格式和日志函数响应级别。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 21:27
# @Author : AmoXiang
# @File : 18.logging调试程序.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
import logging # 导入日志模块
logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.DEBUG)
logging.debug('debug信息')
logging.info('info信息')
logging.warning('warning信息')
logging.error('error信息')
logging.critical('critial信息')
由于在 logging.basicConfig 中设置 level 的值为 logging.DEBUG,所以所有 debug、info、warning、error、critical 级别的日志信息都会打印到控制台上。打印信息如下:
【示例3】本示例简单演示如何使用 logging 把日志信息输出到外部文件中。
python
# -*- coding: utf-8 -*-
# @Time : 2025-05-08 21:27
# @Author : AmoXiang
# @File : 18.logging调试程序.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
import logging # 导入logging模块
# 配置记录文件和记录信息的格式
logging.basicConfig(filename='test.log',
format='[%(asctime)s-%(filename)s-%(levelname)s:%(message)s]', level=logging.DEBUG, filemode='a',
encoding='utf-8',
datefmt='%Y-%m-%d%I:%M:%S %p')
s = '0'
n = int(s)
logging.info('n = %d' % n) # 保存n的值到logging
print(10 / n)
'''
在上面示例中,使用logging.basicConfig函数设置要保存信息的日志文件,以及日志信息的输出格式、日志时间格式、事件级别等。
具体参数说明如下:
1.filename:指定文件名
2.format:设置日志信息的显示格式。本例设置的格式分别为时间+当前文件名+事件级别+输出的信息。
3.level:事件级别,低于设置级别的日志信息将不会被保存到日志文件中。
4.filemode:日志文件打开模式,'a'表示在文件内容尾部追加日志信息,'w'表示重新写入日志信息,即覆盖之前保存的日志信息。
5.datefmt:设置日志的日期时间格式。
'''
程序运行结果如下图所示:
在当前目录中,可以看到新建的 test.log 文件,打开可以看到已经保存的日志信息如下:
四、练习
1.一段代码运行后出现 IndentationError 错误提示,是什么错误呢?
python
A.变量不存在
B.语法错误
C.缩进错误
D.索引超出范围
2.在Python中,提供什么语句捕获并处理异常?
python
A.try......except
B.try......catch
C.try......eatch
D.try......for
3.在使用中,我们一般把可能产生异常的语句放在哪里?
python
A.except语句块中
B.try语句块中
C.catch语句块中
D.函数结构中
4.如果分配了有限的资源,则应将释放这些资源的代码放置在()代码块中。
python
A.finally
B.except
C.else
D.def
5.如果try语句块中的代码没有错误,那么()语句块将不会被执行。
python
A.except
B.else
C.for
D.def
6.如果expression表达式中值为真时,什么都不做,如果为假时,抛出()异常。
python
A.SyntaxError
B.AssertionError
C.NameError
D.TypeError
7.如果某个函数或方法可能会产生异常,但不想在当前函数或者方法中处理这个异常,可以使用什么语句在函数或方法中抛出异常?
python
A.except
B.finally
C.raise
D.catch
8.用于指定try语句块中没有发现异常时要执行的语句块是什么语句 ?
python
A.finally
B.except
C.if
D.else
9.Python上下文管理语句为()?
python
A.except
B.else
C.for
D.with
10.Python提供了()语句来进行调试。
python
A.assert
B.except
C.expression
D.if
11.当我们写出不符合python语法的代码时,在解析时会报(),并且会显示出错的那一行,并用小箭头指明最早探测到错误的位置。
python
A.SyntaxError
B.ZeroDivisionError
C.AttributeError
D.IOError
12.如果传入对象类型与要求的不符合,会报什么异常?
python
A.ZeroDivisionError
B.IOError
C.TypeError
D.NameError
13.在使用try...except语句捕获异常时,如果在except后面不指定异常名称,则表示()
python
A.随机捕获异常
B.捕获指定异常
C.捕获全部异常
D.不捕获异常
14.Python内建异常类的基类是?
python
A.Object
B.BaseException
C.type
D.KeyError
15.下面一段代码运行后会输出什么?
python
1 s1 = 'hello'
2 try:
3 int(s1)
4 except Exception as e:
5 print(e)
A.Error: can't find file or read data
B.invalid literal for int() with base 10: 'hello'
C.SyntaxError: Missing parentheses in call to 'print'
D.Written content in the file successfully
16.下面一段代码运行后,会有什么结果?
python
1 s1 = 'hello'
2 try:
3 int(s1)
4 except IndexError as e:
5 print (e)
A.未捕获到异常,程序直接报错
B.没有找到文件或读取文件失败
C.ZeroDivisionError
D.Error: can't find file or read data
17.下面一段代码运行后,会产生什么结果?
python
1 #!/usr/bin/python3
2 try:
3 fh = open("testfile", "r")
4 fh.write("This is my test file for exception handling!!")
5 except IOError:
6 print ("Error: can\'t find file or read data")
7 else:
8 print ("Written content in the file successfully")
A.语法错误,无法运行
B.This is my test file for exception handling!!
C.Error: can't find file or read data
D."Written content in the file successfully"
18.运行下面的代码,会输出什么结果?
python
1 #!/usr/bin/python3
2 def functionName( level ):
3 if level <1:
4 raise Exception(level)
5 return level
6 try:
7 l=functionName(-10)
8 print ("level=",l)
9 except Exception as e:
10 print ("error in level argument",e.args[0])
A.error in level argument -10
B."error in level argument",e.args[0]
C.level
D."level=",l
19.下面代码运行后,会出现什么结果?
python
1 #!/usr/bin/python3
2 try:
3 fh = open("testfile", "w")
4 fh.write("This is my test file for exception handling!!")
5 except IOError:
6 print ("Error: can\'t find file or read data")
7 else:
8 print ("Written content in the file successfully")
9 fh.close()
A.Written content in the file successfully
B.This is my test file for exception handling!!
C.Error: can\'t find file or read data
D.testfile", "w"
20.运行下面这句代码,会引发什么异常?
python
10 * (1/0)
A.ZeroDivisionError
B.SyntaxError
C.AttributeError
D.IOError
21.属性引用或赋值失败的情况下引发的异常是什么?
python
A.AttributeError
B.IOError
C.KeyError
D.IndexError
22.下面一段代码运行后,会引发什么异常?
python
'2' + 2
A.IOError
B.TypeError
C.ZeroDivisionError
D.NameError
23.如果想要捕捉特定的异常类型并输出异常提示,下面的代码有没有错误?
python
1. try:
2. f = open('myfile.txt')
3. s = f.readline()
4. i = int(s.strip())
5. except OSError as err:
6. print("OS error: {0}".format(err))
7. except ValueError:
8. print("Could not convert data to an integer.")
9. except:
10. print("Unexpected error:", sys.exc_info()[0])
A.没有错误
B.第一行去掉:
C.第七行去掉
D.第九行第十行去掉
24.小地瓜写了一段代码,看看有没有错误吧!
python
1 try:
2 f = open('temp.txt','w')
3 print(f.write('到底写进去了没有呢?'))
4 sum = 1 + '1'
5 else OSError as reason:
6 print('不存在这个文件' + str(reason))
7 except TypeError as reason:
9 print('类型错误' + str(reason))
10 finally:
11 f.close()
A.3行f.write改为f.write:
B.5行else改为except
C.7行except改为else
D.没有错误
25.小明最近在学Python,他写了一段代码,想最后无论什么异常与否,都输出一句小明最帅,看看他写的有没有问题吧!
python
1 s1 = 'hello'
2 try:
3 int(s1)
4 except IndexError as e:
5 print(e)
6 except KeyError as e:
7 print(e)
8 except ValueError as e:
9 print(e)
10 else:
11 print('1')
12 else:
13 print('小明最帅')
A.9行e改成小明最帅
B.10行else改成finally
C.12行else改成finally
D.没有错误
26.小明自己自定义了一个异常,看看他有没有错误吧!
python
1 class hexinException(BaseException):
2 def __init__(self,msg):
3 self.msg=msg
4 def __str__(self):
5 return self.msg
6 try:
7 raise hexinException('类型错误')
8 except hexinException as e:
9 print(e)
A.4行self改成msg
B.7行raise改成else
C.9行e改成hexinException
D.没有错误
27.小明写了一组代码,输入任意两个字符串,输出这两个字符串相加结果,如果输入错误,报错重新输入,看看他写的对么?
python
1 while True:
2 try:
3 a = int(input('Plase input a number: '))
4 b = int(input('Plase input a number: '))
5 print(ab)
6 except Exception as e:
7 print('Error happend, please do agin')
8 print(e)
9 else:
10 print('No error happend')
A.2行try:改成try
B.5行ab改成a+b
C.9行else改成finally
D.没有错误
至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习Python语言的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!

好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请
点赞
、评论
、收藏
一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了
关注
我哦!