8. 错误和异常
语法错误与异常分流、try/except/else/finally、raise 与异常链、with 协议、ExceptionGroup——对齐 Promise/finally 但要理解修复与再抛。
8.1 语法错误 vs 异常
- SyntaxError:解析失败(漏冒号、缩进错误等)。
- Exception:运行期失败(
TypeError、ValueError、KeyError…)。
try:
{}["missing"]
except KeyError as err:
print("找不到键:", err)8.2 try / except / else / finally
- 执行
try。 - 出现异常 → 匹配
except。 - 无异常 →
else。 - 几乎总会跑
finally(除非进程被杀、os._exit等)。
while True:
try:
x = int(input("请输入数字: "))
except ValueError:
print("不是合法整数,重试")
else:
break # 只有成功解析才跳出def read_int_header(path: str) -> int:
f = None
try:
f = open(path, encoding="utf-8")
return int(f.readline())
except OSError:
print("打不开文件")
raise # 重新抛出:交给上层处理
except ValueError:
print("首行不是整数")
return 0
finally:
if f is not None:
f.close()文件更常见写
with open(...) as f:;这里故意展示finally手动清理的模式。
8.3 raise 与 from(异常链)
raise ≈ 前端的 throw:主动抛错,交给 try/except(类似 try/catch)或继续向外冒泡。except 里单独写 raise ≈ throw err,原样再抛。
try:
open("配置文件.ini", encoding="utf-8")
except OSError as exc:
raise RuntimeError("无法加载配置") from excraise ... from exc ≈ throw new Error("无法加载配置", { cause: exc })(把底层错误挂到 cause,栈/日志里能串起来)。raise ... from None ≈ 故意不传 cause,对外只暴露上层文案(排障信息会变少)。
8.4 自定义异常
class AuthError(Exception):
"""认证失败"""
def login(token: str | None) -> None:
if not token:
raise AuthError("缺少 token")8.5 with 与上下文管理器
with open("test.txt", encoding="utf-8") as f:
print(f.read())上下文协议也用于锁、数据库连接等;等价于「无论成功失败都走退出逻辑」。
8.6 ExceptionGroup 与 except*
def task() -> None:
raise ExceptionGroup(
"批量失败",
[OSError("磁盘"), ValueError("参数")],
)
try:
task()
except* OSError as eg:
print("OS 问题:", eg.exceptions)
except* ValueError as eg:
print("参数问题:", eg.exceptions)8.7 finally 与 return(隐蔽坑)
在 Python 里,若 finally 里执行了 return,会取代 try(以及 except)里的返回值
def confusing() -> int:
try:
return 1
finally:
return 2 # 会覆盖 try 的 return —— 尽量避免
print(confusing()) # 28.8 最佳实践
- 精确捕获:能处理再捕获;不要
except Exception: pass除非真有全局兜底策略。 - 资源:
with优于手写close。 - 日志:
logging.exception("msg")在except块里会自动附加栈。 - 业务语义:自定义异常类型让上层
switch/match更容易。