ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

程序崩了不要慌!Python 异常处理从 try-except 到自定义异常,老哥给你兜底

程序崩了不要慌!Python 异常处理从 try-except 到自定义异常,老哥给你兜底

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 自动释放资源。

掌握这套,你的程序就能 "稳如老狗",再也不会一崩到底。下一篇讲模块与包—— 怎么把代码拆分、复用、给别人用。有问题随时回来找老哥。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进