卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

  • 函数、作用域与模块

  • 面向对象

  • 进阶语法与语言机制

    • 可迭代对象和迭代器有什么区别?
    • 生成器是什么?yield为什么能节省内存?
    • Python装饰器的原理是什么?
    • Python上下文管理器是什么?with如何自动释放资源?
    • Python异常体系是什么?try、except、else和finally怎么执行?
      • 简要回答
      • 详细回答
      • 知识拓展
    • Python浅拷贝和深拷贝有什么区别?
    • Python类型标注有什么用?常见类型应该怎么写?
    • dataclass、namedtuple和普通类有什么区别?
    • Python如何管理内存?引用计数和垃圾回收是什么?

# Python异常体系是什么?try、except、else、finally怎么执行?

面试官问:"Python的异常体系是什么?try、except、else、finally的执行顺序是怎样的?"

很多人会答"用try捕获异常,except处理异常",但这只是基础用法。真正要讲清楚的是:Python异常继承自BaseException,常规异常继承自Exception;else在没有异常时执行,finally无论是否异常都执行,且finally会在return之前执行。

# 简要回答

Python异常体系采用继承结构,顶层是BaseException,主要分支:

  • SystemExit、KeyboardInterrupt、GeneratorExit:系统级异常,通常不应该捕获。
  • Exception:所有常规异常的基类,业务代码应该捕获这个分支。

try-except-else-finally的执行顺序:

  1. 执行try代码块。
  2. 如果没有异常,跳过except,执行else(如果有)。
  3. 如果有异常且被except捕获,执行对应的except块,不执行else。
  4. 无论是否异常,最后执行finally(如果有)。
  5. 关键:finally在return之前执行,如果finally中也有return,会覆盖之前的返回值。

捕获异常的推荐做法:

  • 只捕获预期的具体异常类型,不用裸except。
  • 不捕获BaseException,会拦截KeyboardInterrupt导致程序无法中断。
  • 捕获后要么处理,要么记录日志后重新抛出,不能静默吞掉。

# 详细回答

# Python异常继承结构

BaseException
 ├── SystemExit          # sys.exit()触发
 ├── KeyboardInterrupt   # Ctrl+C触发
 ├── GeneratorExit       # 生成器关闭
 └── Exception           # 常规异常基类
      ├── StopIteration  # 迭代结束
      ├── ArithmeticError
      │    ├── ZeroDivisionError
      │    └── OverflowError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      ├── ValueError
      ├── TypeError
      ├── AttributeError
      └── ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

业务代码通常只捕获Exception及其子类,让SystemExit和KeyboardInterrupt能正常传播。

# try-except基本用法

捕获具体异常类型:

try:
    result = 10 / 0
except ZeroDivisionError:
    print("除数不能为0")
# 输出: 除数不能为0
1
2
3
4
5

捕获多种异常:

try:
    value = int("abc")
except (ValueError, TypeError) as e:
    print(f"转换失败: {e}")
# 输出: 转换失败: invalid literal for int() with base 10: 'abc'
1
2
3
4
5

捕获异常并获取实例:

try:
    raise ValueError("参数错误")
except ValueError as e:
    print(f"捕获异常: {e}")
    print(f"异常类型: {type(e)}")
1
2
3
4
5

# else子句的执行时机

else只在try块没有抛出异常时执行:

def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("除数为0")
    else:
        print(f"计算成功: {result}")
        return result

divide(10, 2)   # 输出: 计算成功: 5.0
divide(10, 0)   # 输出: 除数为0
1
2
3
4
5
6
7
8
9
10
11

else的作用是把只可能在try块中出错的代码和成功后的处理逻辑分开,让异常处理更清晰。

# finally子句一定会执行

无论是否异常,finally都会执行:

def read_file(filename):
    try:
        f = open(filename)
        content = f.read()
        return content
    except FileNotFoundError:
        print("文件不存在")
        return None
    finally:
        print("清理资源")

read_file("data.txt")
# finally一定会执行,无论函数如何退出
1
2
3
4
5
6
7
8
9
10
11
12
13

即使try或except中有return,finally也会在return之前执行。

# finally会在return之前执行

这是容易出错的地方:

def test():
    try:
        return "try"
    finally:
        print("finally执行")

result = test()
# 输出: finally执行
print(result)  # try
1
2
3
4
5
6
7
8
9

执行顺序:计算return的值 → 执行finally → 真正返回。

如果finally中也有return,会覆盖之前的返回值:

def test():
    try:
        return "try"
    finally:
        return "finally"

print(test())  # finally
1
2
3
4
5
6
7

不推荐在finally中使用return,会让控制流难以理解。

try-except-else-finally执行流程

# 异常链与上下文

重新抛出异常时,保留原始异常信息:

try:
    result = 10 / 0
except ZeroDivisionError as e:
    raise ValueError("计算失败") from e
1
2
3
4

使用raise ... from ...会设置异常的__cause__属性,保留完整的异常链:

Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  ...
ValueError: 计算失败
1
2
3
4
5
6
7
8
9

不使用from时,Python会自动设置__context__属性,但不会显式标注因果关系。

# 自定义异常

继承Exception定义业务异常:

class InvalidAgeError(ValueError):
    """年龄无效异常"""
    pass

def set_age(age):
    if age < 0 or age > 150:
        raise InvalidAgeError(f"年龄必须在0-150之间,当前: {age}")
    return age

try:
    set_age(-5)
except InvalidAgeError as e:
    print(e)
1
2
3
4
5
6
7
8
9
10
11
12
13

自定义异常应该:

  • 继承自合适的内置异常(ValueError、TypeError等)或直接继承Exception。
  • 类名以Error结尾。
  • 提供清晰的错误信息。

# 裸except的问题

不指定异常类型会捕获所有异常,包括系统异常:

try:
    # 某些操作
    pass
except:  # 不推荐
    print("出错了")
1
2
3
4
5

问题:

  • 会捕获KeyboardInterrupt,导致Ctrl+C无法中断程序。
  • 会捕获SystemExit,导致sys.exit()失效。
  • 掩盖了拼写错误等编程错误。

应该改为:

try:
    pass
except Exception as e:  # 只捕获常规异常
    print(f"出错了: {e}")
1
2
3
4

# 知识拓展

Q1: 什么时候用else,什么时候直接写在try后面?

如果某段代码只有在try块成功后才应该执行,且这段代码本身可能抛出异常,应该用else:

try:
    data = load_data()  # 可能失败
except IOError:
    print("加载失败")
else:
    process_data(data)  # 只在加载成功后执行,但也可能抛出异常
1
2
3
4
5
6

如果process_data()也放在try中,无法区分异常来自load_data还是process_data。

Q2: finally中可以抑制异常吗?

可以,但不推荐。如果finally中执行return,异常会被静默吞掉:

def test():
    try:
        raise ValueError("错误")
    finally:
        return "正常返回"

print(test())  # 正常返回,异常消失
1
2
3
4
5
6
7

这会让调试非常困难,应该避免在finally中使用return。

Q3: 捕获异常后应该怎么处理?

三种常见做法:

  1. 完全处理:修复问题,不再抛出。
  2. 记录并重新抛出:记录日志后用raise(不带参数)重新抛出原异常。
  3. 转换异常:捕获底层异常,抛出更高层的业务异常,用raise ... from ...保留异常链。

不应该静默吞掉异常,除非确实不需要处理。

Q4: Python 3.11的异常组是什么?

Python 3.11+支持ExceptionGroup,可以同时抛出多个异常:

raise ExceptionGroup("多个错误", [
    ValueError("值错误"),
    TypeError("类型错误")
])
1
2
3
4

配合except*语法分别处理每种异常。适用于并发场景,需要同时报告多个任务的失败。

Q5: 异常处理会影响性能吗?

会,但只在异常真正抛出时。创建try块的开销很小,但抛出和捕获异常的开销较大(需要构建traceback)。因此:

  • 不要用异常做常规控制流(如用StopIteration控制循环)。
  • 预期会频繁发生的情况应该用条件判断,不用异常。
  • 异常应该用于真正的异常情况。
Last Updated: 10/13/2026, 5:57:00 PM

← Python上下文管理器是什么?with如何自动释放资源? Python浅拷贝和深拷贝有什么区别? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪