C#转Python第4.1篇:当 try-catch 遇上 try-except:异常处理的大不同

在 C# 里写异常处理,try-catch-finally 三件套,捕获类型精确到 InvalidOperationException,连 when 条件都能加。异常体系严谨得像一部法典------SystemExceptionApplicationExceptionCustomException,层级分明。

在 Python 里写异常处理,try-except 两件套搞定,没有 finally(其实有但很少用),捕获类型直接写类名,没有 throw,没有 ExceptionDispatcher。简单得像在纸条上写"出问题了就执行这段"。

当我第一次在 Python 里写 except Exception as e 的时候,我的内心是:"这就完了?我的 catch (Exception ex) 呢?我的 when 呢?我的 ExceptionDispatchInfo 呢?"

后来我才明白,Python 的异常处理哲学是:别把简单的事情搞复杂 。而 C# 的异常处理哲学是:每种错误都值得被精确对待

两种语言的异常处理哲学完全不同------一个追求精确,一个追求简洁。

基础语法对比

C# 版本:

复制代码
try
{
    int result = 10 / int.Parse("0");
    Console.WriteLine(result);
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"除零错误: {ex.Message}");
}
catch (FormatException ex)
{
    Console.WriteLine($"格式错误: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"其他错误: {ex.Message}");
}
finally
{
    Console.WriteLine("不管出不出错都执行");
}

Python 版本:

复制代码
try:
    result = 10 / int("0")
    print(result)
except ZeroDivisionError as e:
    print(f"除零错误: {e}")
except ValueError as e:
    print(f"格式错误: {e}")
except Exception as e:
    print(f"其他错误: {e}")
finally:
    print("不管出不出错都执行")

对比一下:

对比项 C# Python
关键字 catch except
捕获变量 catch (Exception ex) except Exception as e
多类型捕获 `catch (AException BException ex)`
异常过滤器 catch (Exception ex) when (...) except Exception as e: 里手动判断
finally 必须有 可选
throw 必须用 throw 直接 raise
重新抛出 throw;(保留堆栈) raise(保留堆栈)

C# 的 catch 像在签合同,每种异常都有对应的条款;Python 的 except 像在贴纸条,"出问题了就执行这段"。

没有 finally?Python 有更优雅的方案

C# 的 finally 用来做资源清理------关闭文件、释放连接、解锁。Python 也有 finally,但更常用的是 with 语句:

C# 版本:

复制代码
StreamReader reader = null;
try
{
    reader = new StreamReader("file.txt");
    string content = reader.ReadToEnd();
    Console.WriteLine(content);
}
finally
{
    reader?.Dispose();
}

Python 版本:

复制代码
with open("file.txt") as f:
    content = f.read()
    print(content)
# 离开 with 块自动关闭,不需要 finally

with 语句自动调用 __exit__ 方法来清理资源,比 finally 更简洁。C# 其实也有类似的 using 语句:

复制代码
using (var reader = new StreamReader("file.txt"))
{
    string content = reader.ReadToEnd();
    Console.WriteLine(content);
}
// 自动 Dispose

设计哲学几乎一样------用语法糖来保证资源清理 ,只是 Python 叫 with,C# 叫 using

异常捕获:精确 vs 宽松

C# 的异常捕获非常精确,连 when 条件都能加:

复制代码
try
{
    await httpClient.GetAsync(url);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
    Console.WriteLine("页面不存在");
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
    Console.WriteLine("未授权");
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"请求失败: {ex.StatusCode}");
}

Python 的异常捕获没有 when,但可以用 if 判断:

复制代码
try:
    response = requests.get(url)
    response.raise_for_status()
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 404:
        print("页面不存在")
    elif e.response.status_code == 401:
        print("未授权")
    else:
        print(f"请求失败: {e.response.status_code}")

C# 用 whencatch 里过滤,Python 用 ifexcept 里过滤。效果一样,但 C# 的 when 更"声明式",Python 的 if 更"命令式"。

多类型捕获

C# 可以用 | 在一个 catch 里捕获多种异常:

复制代码
try
{
    // ...
}
catch (InvalidOperationException | ArgumentException ex)
{
    Console.WriteLine($"参数或状态错误: {ex.Message}");
}

Python 也可以:

复制代码
try:
    # ...
except (ValueError, TypeError) as e:
    print(f"值或类型错误: {e}")

几乎一模一样,都是"这两种异常我用同一种方式处理"。

异常的层级结构

C# 的异常层级:

复制代码
System.Exception
├── System.SystemException
│   ├── System.DivideByZeroException
│   ├── System.NullReferenceException
│   ├── System.IndexOutOfRangeException
│   └── ...
├── System.ApplicationException
└── 自定义异常

Python 的异常层级:

复制代码
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
├── Exception
│   ├── ValueError
│   ├── TypeError
│   ├── KeyError
│   ├── IndexError
│   ├── FileNotFoundError
│   └── ...
└── 自定义异常

关键区别:

特性 C# Python
基类 System.Exception Exception(更上面还有 BaseException
需要继承吗 建议继承 Exception 可以继承任意类
编译检查 捕获未声明的异常会警告 不检查,运行时才知道
checked/unchecked checked 关键字 没有,全是 unchecked

Python 的异常不需要显式声明,函数可以抛出任何异常,编译器不会告诉你。这是 Python 的"信任程序员"哲学------你觉得需要检查就检查,不检查也行。

raise vs throw

C# 抛异常必须用 throw

复制代码
throw new ArgumentNullException(nameof(name));
throw;  // 重新抛出当前异常

Python 抛异常用 raise

复制代码
raise ValueError("name 不能为空")
raise  # 重新抛出当前异常

还有一个区别:C# 的 throw 会重置堆栈跟踪,throw; 不会。Python 的 raise 始终保留堆栈跟踪。

上下文管理器:with 语句

Python 独有的 with 语句,是异常处理的最佳拍档:

复制代码
class FileManager:
    def __init__(self, filename):
        self.filename = filename

    def __enter__(self):
        self.file = open(self.filename, 'r')
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        if exc_type is not None:
            print(f"发生错误: {exc_val}")
        return False  # 不抑制异常

with FileManager("file.txt") as f:
    content = f.read()

__exit__ 方法接收三个参数:

  • exc_type:异常类型(没有异常时为 None

  • exc_val:异常值

  • exc_tb:异常堆栈

返回 True 会抑制异常(吞掉),返回 False 会继续抛出。

C# 的 IDisposableusing 实现类似的功能,但没有这么灵活------using 只负责调用 Dispose(),不能在 Dispose 里判断是否有异常。

异常链:Exception Dispatch Info vs Exception Chaining

C# 保留原始异常的堆栈:

复制代码
try
{
    // ...
}
catch (Exception ex)
{
    throw new ApplicationException("处理失败", ex);  // 保留内部异常
}

// 或者用 ExceptionDispatchInfo 重新抛出,完全保留堆栈
ExceptionDispatchInfo.Capture(ex).Throw();

Python 的异常链:

复制代码
try:
    # ...
except Exception as e:
    raise RuntimeError("处理失败") from e  # 保留原始异常

# 或者直接 raise,Python 自动链式异常
try:
    # ...
except Exception as e:
    raise  # Python 3 自动添加 __context__

Python 3 的 raise 会自动保留 __context__(之前的异常),from e 会设置 __cause__(明确的因果关系)。

迁移指南:C# 开发者最容易犯的错

  1. 忘记 as 关键字 :Python 的 except Exception as e 不是 except Exception e

  2. 用错异常类型 :Python 的 IndexError 不是 IndexOutOfRangeException

  3. 没有 checked 异常:Python 不会在编译时检查你是否捕获了所有异常

  4. **滥用 except Exception**:和 C# 的 catch (Exception) 一样,太宽泛了

  5. **忽略 else**:Python 的 try-except-elseelse 在没有异常时执行,C# 没有这个

坑点提醒

bare except ------捕获一切异常,包括 KeyboardInterrupt

复制代码
try:
    # ...
except:  # 不要这样写!
    pass

这会捕获 SystemExitKeyboardInterrupt,导致你连 Ctrl+C 都用不了。永远指定异常类型。

异常吞噬 ------except 里什么都不做:

复制代码
try:
    # ...
except Exception:
    pass  # 异常被吞掉了,出 bug 都不知道

至少要 printlog 一下异常信息。

资源泄漏 ------不用 with

复制代码
f = open("file.txt")
content = f.read()
# 忘记 f.close(),文件句柄泄漏

永远用 with 语句管理资源。

真实案例 :我有个同事从 C# 转 Python,写了一个数据处理脚本。脚本里用 try-except Exception: pass 吞掉了所有异常,跑了三天才发现数据处理全错了------异常被吞掉,错误数据静默写入数据库。后来改成 except Exception as e: logging.error(e) 才发现问题。

try-except-else-finally:完整流程

Python 的异常处理有四个部分,比 C# 多一个 else

复制代码
try:
    result = 10 / int(input("输入一个数: "))
except ZeroDivisionError:
    print("不能除以零")
except ValueError:
    print("请输入有效数字")
else:
    # 没有异常时执行
    print(f"结果是: {result}")
finally:
    # 无论如何都执行
    print("计算完成")

C# 没有 else,你得把正常逻辑放在 try 块里:

复制代码
try
{
    int num = int.Parse(Console.ReadLine());
    int result = 10 / num;
    Console.WriteLine($"结果是: {result}");  // 这里如果出异常也会被捕获
}
catch (DivideByZeroException)
{
    Console.WriteLine("不能除以零");
}
catch (FormatException)
{
    Console.WriteLine("请输入有效数字");
}
finally
{
    Console.WriteLine("计算完成");
}

Python 的 else 让"正常逻辑"和"异常处理"分离得更清楚------else 里的代码不会被 try 块的异常影响,只有 else 自己抛出的异常才会被 except 捕获。

Python 3.14 新特性

PEP 758:不带圆括号的 except 和 except* 表达式

Python 3.14 允许在捕获多个异常且不使用 as 子句时省略圆括号:

复制代码
# Python 3.13 及之前
try:
    connect_to_server()
except (TimeoutError, ConnectionRefusedError):  # 必须有圆括号
    print('网络连接失败')

# Python 3.14
try:
    connect_to_server()
except TimeoutError, ConnectionRefusedError:  # 可以省略圆括号
    print('网络连接失败')

注意 :这个简化只适用于不使用 as 子句的情况:

复制代码
# 还是需要圆括号
try:
    connect_to_server()
except (TimeoutError, ConnectionRefusedError) as e:  # 有 as 必须用圆括号
    print(f'网络连接失败: {e}')

对比 C# 的异常捕获

特性 C# Python 3.14
多类型捕获 `catch (AException BException ex)`
圆括号 不需要 省略圆括号(无as时)
异常过滤器 when (condition) if 在 except 块内判断
重新抛出 throw; raise

改进的错误消息

Python 3.14 还改进了错误消息,帮助开发者更快定位问题:

复制代码
# 关键字拼写错误
whille True:  # 语法错误,建议使用 'while'
    pass

# else 后的 elif
if x > 0:
    print("正数")
else:
    print("非正数")
elif x < 0:  # 语法错误:else 块后不能有 elif
    print("负数")

一句话总结

C# 的异常处理像写法律条文------每种情况都有明确的条款;Python 的异常处理像贴便利贴------简单直接,出问题了再处理。

下一篇咱们来聊聊自定义异常------C# 的 CustomException : Exception vs Python 的自定义异常类,两种语言的"异常哲学"又有啥不同。


📦 示例代码:C# 转 Python 全系列配套练习代码(含 48 章示例)

💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

相关推荐
数据知道17 分钟前
Java 安全审计实战:SSRF、反序列化、SpEL 注入
java·开发语言·安全·网络安全
陈年老古董18 分钟前
矿物分类数据处理:缺失值填充方法详解
开发语言·python·机器学习·项目
晚风醉蝶22 分钟前
1-18-基数排序-RadixSort
python·算法·排序算法·基数排序
今天AI了吗31 分钟前
从“金鱼脑”到“大象记忆”:AI Agent 短期记忆与长期记忆的存储与检索全解
数据库·人工智能·python·sql·rust
whcyhhh1 小时前
头歌实践教学平台:数据科学与大数据技术导论(十八4)
大数据·开发语言·python
编码者卢布1 小时前
【Azure Developer】通过 API 获取 Azure VM 信息实践指南 Part 2(Azure China)
python·flask·azure
65岁退休Coder1 小时前
LangGraph v1.2.9 核心概念 & 流程控制
后端·python·langchain
IPdodo_1 小时前
2026年AI 数据采集代理 IP 选型:成功率、并发、轮换与成本评估
前端·网络·人工智能·chrome·python·http·网络调试
z落落1 小时前
C# Modbus-RTU 串口读写设备+CRC16+邮件告警
开发语言·c#