Python异常处理实践:try-except与自定义异常
在Python项目里,运行时错误几乎无法避免:用户输入不合法、文件不存在、网络中断、除数为0,都可能让程序直接中断。异常处理机制的价值不在于彻底消灭错误,而在于错误发生时能识别问题、给出兜底逻辑、释放已占资源,让程序继续稳定运行。本文围绕语法错误与异常的区别、try-except捕获、异常信息获取、else与finally、raise与assert、自定义异常以及with上下文管理器,梳理一套可以直接落到代码里的处理思路。【一、语法错误与异常:发生阶段不同】
Python中的错误主要分两类,虽然都会导致程序无法正常执行,但出现的阶段完全不同。
语法错误(Syntax Error)指代码不符合Python语法规则,在解析阶段就被解释器发现。这时程序还没有开始运行,必须修改代码才能执行。例如if条件后漏写冒号:
if a == 1
print(a)
运行结果为SyntaxError: expected ':'。常见的语法错误包括少写冒号、引号没有闭合、缩进错误、关键字拼写错误等。
异常(Exception)则是语法正确、程序可以运行,但在执行过程中发生错误。例如访问不存在的列表索引:
lst =
print(lst)
列表只有索引0、1、2,访问索引3会触发IndexError: list index out of range。
区分这两者的意义在于:语法错误只能在编码阶段修正,异常则可以在运行阶段被捕获和处理。
【二、try-except基本结构与执行流程】
最基础的异常处理结构是try-except:
try:
# 可能发生异常的代码
except:
# 异常处理代码
它的执行流程分两种情况:
1. try代码块没有异常:执行完try后跳过except,继续往下执行。
2. try代码块发生异常:中断try中剩余代码,转入except执行,然后继续执行后面的程序。
示例:
try:
result = 10 / 0
except:
print("程序出现异常")
print("程序继续执行")
输出为:
程序出现异常
程序继续执行
可以看到,程序没有因为除零错误而整体崩溃。
【三、精准捕获与获取异常信息】
只写except:虽然简单,但它会捕获所有类型的异常,容易把不该吞掉的错误也一并隐藏。实际开发中更推荐精准捕获指定异常:
try:
num = 10 / 0
except ZeroDivisionError:
print("不能除以0")
如果还需要拿到异常对象本身的信息,可以使用as e:
try:
print(10 / 0)
except ZeroDivisionError as e:
print(e)
输出为division by zero。这样既知道异常类型,也能把具体错误信息记录到日志中。
【四、多异常处理与匹配顺序】
同一段代码可能抛出不同异常,可以写多个except分支:
try:
a = int(input())
except ValueError:
print("输入不是数字")
except TypeError:
print("类型错误")
Python会按except的书写顺序依次匹配,命中一个后就不再继续匹配。因此有一个重要原则:通配的except:必须放在最后,否则它会提前捕获所有异常,后面的分支永远不会执行。
【五、else:try成功时执行】
try-except还可以带一个可选的else子句,作用是:try代码块没有发生异常时执行。
try:
result = 10 / 2
except:
print("错误")
else:
print(result)
输出为5。
else的价值在于让职责更清晰:try只放有风险的代码,except处理错误,else处理成功后的结果,避免把正常逻辑也塞进try里而被误捕获。
【六、finally:无论是否异常都会执行】
finally的特点是:不管try中是否发生异常,它都会执行。
try:
# 业务代码
except:
# 异常处理
finally:
# 必须执行的清理代码
典型用途是关闭文件、关闭数据库连接、释放锁以及清理其他资源。例如:
try:
file = open("test.txt")
finally:
file.close()
需要留意的是,这种写法在open本身失败时,file并未成功赋值,finally中直接调用close会再次触发异常。后续章节的with写法可以更好地规避这类问题。
【七、raise主动抛出异常与assert断言】
除了Python自动抛出的异常,开发者也可以用raise主动制造异常,常用于参数校验、业务规则检查和数据验证:
def login(password):
if password != "123456":
raise ValueError("密码错误")
login("111")
调用后抛出ValueError: 密码错误。
assert则用于判断程序是否满足预期条件,格式为assert 条件, 提示信息:
age = 10
assert age >= 18, "年龄不符合要求"
条件不成立时会产生AssertionError。assert适合开发调试阶段检查程序逻辑,不适合当作正式的业务异常处理手段。
【八、自定义异常:让业务错误可读】
Python内置了ValueError、TypeError、IndexError等异常,但业务中还会遇到用户不存在、余额不足、权限不足这类场景,此时可以定义自己的异常类。自定义异常必须继承Exception:
class UserError(Exception):
pass
使用时可以直接在业务函数中抛出并捕获:
class UserNotFoundError(Exception):
pass
def find_user(name):
users = ["admin"]
if name not in users:
raise UserNotFoundError("用户不存在")
try:
find_user("Tom")
except UserNotFoundError as e:
print(e)
自定义异常的意义是让错误类型与业务语义对应,调用方捕获时能一眼看出问题属于哪个环节。
【九、with上下文管理器:自动释放资源】
文件、数据库连接、网络连接等资源在使用后必须释放。传统写法需要手动close,一旦中途出现异常就可能漏掉释放动作。with可以自动管理资源生命周期:
with open("a.txt") as f:
data = f.read()
代码块结束时,Python会自动关闭文件。从机制上看,with等价于try加finally,但写法更简洁,也更不容易遗漏清理步骤。
【十、常见异常类型速查】
ValueError:值错误
TypeError:类型错误
NameError:变量不存在
IndexError:索引越界
KeyError:字典不存在指定键
ZeroDivisionError:除数为0
AttributeError:属性错误
FileNotFoundError:文件不存在
SyntaxError:语法错误
【十一、实际开发中的四条原则】
1. 不要随便捕获所有异常。不推荐写except: pass,这会把错误彻底隐藏,后期排查非常困难。
2. 精准捕获异常。优先写except ValueError,而不是笼统地except:。
3. 资源必须释放。使用with open(),或者用finally保证清理逻辑一定执行。
4. 自定义异常贴近业务。像PasswordError、UserNotFoundError、PermissionError这类命名,能让调用方更快理解错误含义。
【总结】
语法错误属于代码规则问题,异常属于运行过程问题;try-except负责捕获,except as负责取错误信息,else处理成功分支,finally负责资源释放,raise用于主动抛错,assert用于调试检查,自定义异常表达业务语义,with负责自动管理资源。掌握这套机制,是Python从写出可运行代码走向可维护工程代码的关键一步。
Re: Python异常处理实践:try-except与自定义异常
这篇整理得很清楚,尤其是把语法错误和异常按发生阶段区分开,这个点很实用。try-except 的执行流程、else 和 finally 的职责划分也讲得明白。比较认同精准捕获异常,不要一上来就裸 except,否则容易把真正的问题藏起来。finally 那段提到的 open 失败后 file 未赋值、再 close 会二次异常,确实是实际写代码时容易踩的坑,用 with 会更稳妥。raise 和 assert 的定位也说得好,assert 更适合开发和调试,不适合当正式业务异常。自定义异常继承 Exception 之后,业务错误可读性会高很多。整体很适合拿来复习和落地。Re: Python异常处理实践:try-except与自定义异常
总结得很系统,把语法错误和异常按发生阶段拆开这点很关键,很多人一开始确实容易混在一起。try-except 精准捕获、多个 except 的匹配顺序,以及通配 except 放最后,都是实际写代码时很容易踩的坑。else 和 finally 的职责区分也讲得清楚,尤其是 open 失败后 finally 里直接 close 可能再次异常这个细节很实用,也让人更期待后面 with 的写法。自定义异常那段好像还没发完,等后续补全,整体很适合拿来对照项目里的异常处理。Re: Python异常处理实践:try-except与自定义异常
楼主这篇整理得挺清楚,语法错误和异常发生阶段不同这个点很基础,但实际写代码时很多人确实容易混。裸 except 我也觉得要尽量少用,不然出了问题很难定位;用具体异常加 as e 再把信息记到日志里,排查效率会高很多。多异常匹配顺序和 finally 的清理用途也很实用,尤其是 open 失败后 finally 里再调 close 这个坑,确实容易被忽略,所以涉及文件、连接这类资源,with 会更省心。raise 和 assert 的分工也赞同,assert 适合调试断言,不要拿来做正式业务校验。自定义异常那段好像末尾断了,如果能再补一下怎么携带额外字段、怎么按层级设计,应该会更有帮助,期待后续。
页:
[1]