Python 是解释型语言,运行到哪才检查到哪。所以程序报错不丢人,怎么兜住错误让程序继续跑才是本事。Python 有两类错误:语法错误和异常。
- 语法错误:程序解析时遇到的,如
while True print(1)少了冒号 →SyntaxError; - 异常:语法正确,但运行时报错,如变量没定义 →
NameError。
大多数异常程序没处理,就会以错误信息形式打印并中断。
一、try-except:捕获并处理异常
处理异常不是 "避开错误",而是出错时提供解决方案、不终止程序。
try: result = 3 / 1 print("没有发生异常") except: print("发生异常了") print("End")规则:没异常就跳过 except 继续走;有异常就跳过 try 剩余代码,进 except。
二、捕获指定类型 + 获取异常信息
异常信息格式:冒号前是类型,冒号后是描述。可以按类型分别处理,用as e拿到异常对象:
try: result = 3 / 0 print("发生异常了") except ZeroDivisionError as e: # 指定类型 print(e) # division by zero except (RuntimeError, TypeError, NameError) as e: # 一个 except 管多种 print(e) except: # 通配符兜底 print("Unexpected error") print("End")要点:一个 except 可以把多个异常放元组里;最后一个不带类型的 except 是通配符;匹配不到的异常会向外传递。
三、else:没异常才执行
else放在所有 except 之后,try 里没抛异常才执行。把 "正常结果" 和 "异常处理" 分开,代码更清晰:
try: result = x / y except ZeroDivisionError: print("除数不能为零!") else: print(f"结果是: {result}") # 只有没异常才打印结果四、finally:无论如何都会执行
finally放在最后,无论是否异常都执行,专门做清理(关文件、释放连接、解锁):
try: result = 3 / 0 except NameError as e: print(e) else: print(result) finally: print("finally") # 一定会执行 print("End")注意:finally是 try 结构的一部分,异常没被捕获、程序终止时它也会执行;而放在 try 结构外面的代码,若异常没被兜住就不会执行。
五、主动抛出异常 raise
想主动制造错误、拦截非法输入,用raise 异常类型("描述"):
def int_add(x, y): if isinstance(x, int) and isinstance(y, int): return x + y else: raise TypeError("参数类型错误") print(int_add(1, 2)) # 3 print(int_add("1", "2")) # TypeError: 参数类型错误六、assert 断言
assert 表达式 [, 描述]:表达式为 False 时抛AssertionError,常用于调试。等价于if not 表达式: raise AssertionError:
def int_add(x, y): assert isinstance(x, int) and isinstance(y, int), "参数类型错误" return x + y print(int_add(1, 2)) # 3 print(int_add("1", "2")) # AssertionError: 参数类型错误七、自定义异常
通过继承Exception创建自己的异常:
class MyError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) try: raise MyError(1) # 主动抛出自定义异常 except MyError as e: print("触发自定义异常:", e.value) # 触发自定义异常: 1八、异常的传递
异常层层向外传,直到被某个层捕获或程序终止:
try: try: try: print(1 / 0) # 这里抛 ZeroDivisionError except NameError as e: print("第三层", e) except TypeError as e: print("第二层", e) except Exception as e: # 最终在这里被兜住 print("第一层", type(e), e) # 第一层 <class 'ZeroDivisionError'> division by zero九、with:自动管理资源
with封装了 try-except-finally 范式,自动释放资源,最典型是文件:
# 常规方式:出错时文件没关 try: file = open("test.txt", "w") file.write(a) file.close() finally: print("文件是否关闭:", file.closed) # False 没关掉! # 用 with:出错也自动关 try: with open("test.txt", "w") as f: f.write(a) finally: print("文件是否关闭:", f.closed) # True 自动关了!with的原理:调用对象的__enter__进块、__exit__无论是否异常都会执行清理。它要求对象是上下文管理器(实现了__enter__和__exit__)。
十、常见异常速查
表格
| 异常 | 说明 |
|---|---|
Exception | 所有非系统退出异常的基类(自定义异常都继承它) |
ArithmeticError | 算术错误基类(含ZeroDivisionError等) |
LookupError | 键 / 索引无效基类(含IndexError、KeyError) |
AttributeError | 属性引用或赋值失败 |
IndexError | 序列索引越界 |
KeyError | 字典找不到键 |
NameError | 变量名未找到 |
TypeError | 操作 / 函数应用于类型不当的对象 |
OSError | 系统相关错误(文件未找到、磁盘满等) |
SyntaxError | 语法错误 |
KeyboardInterrupt | 用户按 Ctrl-C 中断 |
老哥收尾
异常处理的核心套路:
try 包住有风险的代码、except 按类型分流(as e 拿信息)、else 管没异常的情况、finally 做必做的清理、raise 主动抛、assert 调试断言、with 自动释放资源。
掌握这套,你的程序就能 "稳如老狗",再也不会一崩到底。下一篇讲模块与包—— 怎么把代码拆分、复用、给别人用。有问题随时回来找老哥。