这一节了解一下Python中的异常,它是程序执行过程中发生的错误事件,会中断正常流程。比如除以 0、访问不存在的字典键、打开不存在的文件,Python 都会抛出对应的异常对象。它能把"程序崩溃"变成"可控处理"。它让程序在出错时不会直接终止,而是按你预设的方式恢复、降级或记录。简单总结如下:
场景:
1.防止程序崩溃:捕获错误后程序可以继续运行,而不是直接退出。
2.提供错误恢复:出错时可以重试、回退到默认值、或切换到备用方案。
3.记录诊断信息:在except里记录日志和traceback,方便排查线上问题。
4.定义模块契约:用raise主动抛出异常,告诉调用方"这个条件不满足,无法继续"。
5.释放资源:通过finally或with保证文件、连接、锁等资源一定被释放。
API:
- try:异常捕获入口关键字,用来包裹有可能抛出异常的代码块,监控这段代码运行时是否产生异常。
python
try:
可能报错的代码
- except:捕获指定类型的异常,当try块抛出对应异常时,执行except内逻辑,可以单独捕获某一类异常。
python
except 异常类型:
异常处理逻辑
- except Exception as e:通用异常捕获写法,Exception是大部分内置异常的父类,把异常实例赋值给变量e,用于读取异常信息。
python
except Exception as e:
print(e)
- except (AError,BError):同时捕获多种指定异常,多个异常类型放到元组中,匹配其中任意一种都会进入分支。
python
except (ValueError, ZeroDivisionError):
统一处理这两类错误
- else:try代码块没有抛出任何异常的时候才会执行的分支,有异常则跳过else,适合放依赖try成功之后才执行的代码。
python
try:
正常代码
except:
异常处理
else:
无异常才执行
- finally:无论try里面是否发生异常、无论except有没有捕获到异常,最后一定会执行的代码块,常用于资源释放,关闭文件、连接。
python
try:
业务代码
finally:
资源清理代码
- raise:主动抛出异常,手动触发指定异常,可以携带自定义异常提示信息。
python
raise ValueError("参数不能小于0")
- raise from:链式抛出异常,关联原始异常,保存异常溯源关系,用于包装异常的时候保留底层报错。
python
try:
底层报错
except IOError as err:
raise RuntimeError("上层业务失败") from err
- assert:断言,条件不成立的时候自动抛出AssertionError,多用于开发阶段校验前置条件,生产环境可以通过-O参数关闭断言。
python
assert 条件, "断言失败提示"
- ValueError:值异常,传入参数类型正确但是数值不符合要求时抛出,比如把字符串转数字但内容不是数字。
python
raise ValueError("数字范围非法")
- TypeError:类型异常,操作的数据类型不匹配,比如数字和字符串直接相加、函数传参数量不对。
python
raise TypeError("参数类型错误")
- ZeroDivisionError:除零异常,除法或者取模运算分母为0的时候触发。
python
10 / 0 # 自动抛出 ZeroDivisionError
- IndexError:索引异常,序列(列表、字符串)访问超出有效索引范围。
python
lst = [1,2]
lst[5] # IndexError
- KeyError:字典键异常,访问字典不存在的key时抛出。
python
d = {"a":1}
d["b"] # KeyError
- FileNotFoundError:文件不存在异常,打开一个不存在的文件触发,属于OSError子类。
python
open("not_exist.txt")
- PermissionError:权限异常,文件读写但是没有对应操作系统权限,属于OSError子类。
python
open("/root/xxx.txt","w") # 无权限时报错
- AttributeError:属性异常,对象访问不存在的属性或者方法。
python
obj = 123
obj.test() # AttributeError
- NameError:名字异常,使用了没有定义的变量名。
python
print(unknown_var) # NameError
- SyntaxError:语法异常,代码语法写错,解释器解析阶段报错,无法被try捕获
python
if True # 少冒号,SyntaxError
- BaseException:所有异常的顶层父类,包含Exception和系统退出类异常(KeyboardInterrupt、SystemExit),一般业务不要直接捕获BaseException。
python
except BaseException as e:
pass
- KeyboardInterrupt:键盘中断异常,用户按下Ctrl+C终止程序时抛出,属于BaseException子类。
python
try:
while True:
pass
except KeyboardInterrupt:
print("用户终止程序")
- sys.exc_info():sys模块API,返回当前捕获的异常元组 (异常类型,异常实例,追踪对象),适合在except内部获取完整异常信息。
python
import sys
ty, val, tb = sys.exc_info()
- traceback.print_exc():traceback模块API,直接打印完整异常堆栈信息,比print(e)信息更全,调试用。
python
import traceback
traceback.print_exc()
- traceback.format_exc():traceback模块API,把异常堆栈转为字符串返回,方便日志保存,而不是直接打印。
python
err_str = traceback.format_exc()
- Exception.str():异常实例内置方法,返回异常简短描述,print(e)底层调用这个方法。
python
e = ValueError("test")
print(e.__str__())
- args:异常实例内置属性,是保存异常参数的元组,大部分内置异常第一个元素是提示文本。
python
e = ValueError("参数错了")
print(e.args)
- NotImplementedError:未实现异常,面向对象开发中,基类抽象方法要求子类重写,没有重写就抛出这个异常。
python
def func():
raise NotImplementedError("子类必须实现该方法")
栗子:
python
def div_calc():
while True:
try:
raw_a = input("输入被除数:")
raw_b = input("输入除数:")
a = float(raw_a)
b = float(raw_b)
res = a / b
except ValueError:
print("异常:输入不是合法数字")
except ZeroDivisionError:
print("异常:除数不能为0")
except KeyboardInterrupt:
print("\n收到退出指令,结束计算")
break
else:
print(f"计算结果 = {res:.2f}")
finally:
print("====本次运算一轮结束====\n")
if __name__ == "__main__":
div_calc()
python
import traceback
class RegisterError(Exception):
pass
class PasswordTooShortError(RegisterError):
pass
class UsernameExistError(RegisterError):
pass
def check_username(name: str):
if len(name) < 3:
raise RegisterError("用户名长度至少3位")
if name == "admin":
raise UsernameExistError("admin账号已被占用")
def check_pwd(pwd: str):
if len(pwd) < 6:
raise PasswordTooShortError("密码长度不能小于6位")
def user_register(username, password):
try:
check_username(username)
check_pwd(password)
return "注册成功"
except RegisterError as e:
# 包装一层异常,保留原始异常链
try:
raise RuntimeError("注册校验失败") from e
except RuntimeError as wrap_err:
err_log = traceback.format_exc()
print("【日志记录完整堆栈】\n", err_log)
return f"注册失败:{wrap_err.__cause__}"
if __name__ == "__main__":
print(user_register("ad", "123456"))
print("-"*40)
print(user_register("admin", "123456"))
print("-"*40)
print(user_register("user01", "123"))
print("-"*40)
print(user_register("user02", "12345678"))