C#转Python第2.3篇:上周帮同事 review 代码,他一行 Python 让我愣了 5 秒
上周帮同事 review 代码,他甩过来一行 Python:[x * 2 for x in nums if x > 0]。我愣了 5 秒——这不就是我用 LINQ 写了三年的Where().Select()吗?一行顶三行,Python 玩家果然任性。
不过等我看到后面的需求——分组、聚合、延迟执行——我又默默打开了 C# 的 LINQ。两种写法各有各的杀手锏,C# 的 LINQ 像是"流水线",Python 的推导式像是"压缩包"。
基础语法对比
C# 版本:
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 筛选 var evens = numbers.Where(x => x % 2 == 0).ToList(); // 转换 var doubled = numbers.Select(x => x * 2).ToList(); // 排序 var sorted = numbers.OrderByDescending(x => x).ToList(); // 聚合 var sum = numbers.Sum(); var avg = numbers.Average(); // 链式调用 var result = numbers .Where(x => x > 3) .Select(x => x * 2) .OrderBy(x => x) .ToList(); // C# 12 集合表达式(更简洁) int[] nums = [1, 2, 3, 4, 5]; List<int> list = [1, 2, 3, 4, 5]; IEnumerable<int> more = [.. nums, 6, 7, 8]; // 展开运算符Python 版本:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 筛选 evens = [x for x in numbers if x % 2 == 0] # 转换 doubled = [x * 2 for x in numbers] # 排序 sorted_list = sorted(numbers, reverse=True) # 聚合 total = sum(numbers) avg = sum(numbers) / len(numbers) # 链式操作 result = [x * 2 for x in numbers if x > 3] result = sorted([x * 2 for x in numbers if x > 3])对比一下:
| 操作 | C# LINQ | Python 列表推导式 |
|---|---|---|
| 筛选 | list.Where(x => x > 0) | [x for x in list if x > 0] |
| 转换 | list.Select(x => x * 2) | [x * 2 for x in list] |
| 排序 | list.OrderBy(x => x) | sorted(list) |
| 聚合 | list.Sum() | sum(list) |
| 链式 | .Where().Select().ToList() | [... for x in list if ...] |
C# 的 LINQ 是"方法链",Python 的推导式是"表达式压缩"。
复杂操作对比
多条件筛选
C# 版本:
var users = GetUsers(); // 多条件筛选 var result = users .Where(u => u.Age >= 18) .Where(u => u.IsActive) .Where(u => u.Score > 80) .ToList(); // 或者用 && 连接 var result2 = users .Where(u => u.Age >= 18 && u.IsActive && u.Score > 80) .ToList();Python 版本:
users = get_users() # 多条件筛选 result = [u for u in users if u["age"] >= 18 and u["is_active"] and u["score"] > 80] # 或者分行写 result = [ u for u in users if u["age"] >= 18 and u["is_active"] and u["score"] > 80 ]嵌套循环
C# 版本:
// 笛卡尔积 var colors = new[] { "红", "蓝", "绿" }; var sizes = new[] { "S", "M", "L" }; var combinations = colors .SelectMany(c => sizes, (c, s) => new { Color = c, Size = s }) .ToList(); // 或者用嵌套 Select var combinations2 = colors .SelectMany(c => sizes.Select(s => new { Color = c, Size = s })) .ToList();Python 版本:
# 笛卡尔积 colors = ["红", "蓝", "绿"] sizes = ["S", "M", "L"] combinations = [(c, s) for c in colors for s in sizes] # 等价于 combinations = [] for c in colors: for s in sizes: combinations.append((c, s))Python 的嵌套循环更直观,C# 需要SelectMany。
字典推导
C# 版本:
var users = new List<User> { /* ... */ }; // 转换为字典 var userDict = users.ToDictionary(u => u.Id, u => u.Name); // 用 LINQ 生成字典 var priceDict = products .Where(p => p.IsActive) .ToDictionary(p => p.Id, p => p.Price);Python 版本:
users = [{"id": 1, "name": "张三"}, ...] # 字典推导 user_dict = {u["id"]: u["name"] for u in users} # 带条件的字典推导 price_dict = { p["id"]: p["price"] for p in products if p["is_active"] }集合推导
C# 版本:
var numbers = new List<int> { 1, 2, 2, 3, 3, 3, 4 }; // 用 HashSet 去重 var unique = new HashSet<int>(numbers); // 用 LINQ Distinct var distinct = numbers.Distinct().ToList();Python 版本:
numbers = [1, 2, 2, 3, 3, 3, 4] # 集合推导(自动去重) unique = {x for x in numbers} # {1, 2, 3, 4} # 列表去重(保持顺序) distinct = list(dict.fromkeys(numbers)) # [1, 2, 3, 4]性能对比
C# 版本:
// LINQ 延迟执行 var query = numbers.Where(x => x > 0); // 不会立即执行 var result = query.ToList(); // 这里才执行 // 可以利用延迟执行优化 var result = numbers .Where(x => x > 0) // 延迟 .Select(x => x * 2) // 延迟 .Take(10) // 延迟 .ToList(); // 执行,只处理前 10 个Python 版本:
# 列表推导式立即执行 result = [x * 2 for x in numbers if x > 0] # 立即创建整个列表 # 生成器表达式延迟执行 result = (x * 2 for x in numbers if x > 0) # 不会立即创建列表 # 用 next() 取第一个 first = next(x * 2 for x in numbers if x > 0) # 或者用生成器配合函数 result = list(islice( (x * 2 for x in numbers if x > 0), 10 ))C# 的 LINQ 天然支持延迟执行,Python 需要用生成器表达式。
可读性对比
C# 版本:
// 复杂的链式调用 var result = orders .Where(o => o.Status == OrderStatus.Completed) .Where(o => o.OrderDate >= DateTime.Now.AddDays(-30)) .SelectMany(o => o.Items) .GroupBy(i => i.ProductId) .Select(g => new { ProductId = g.Key, TotalQuantity = g.Sum(i => i.Quantity), TotalAmount = g.Sum(i => i.Quantity * i.Price) }) .OrderByDescending(x => x.TotalAmount) .Take(10) .ToList();Python 版本:
# 等价的 Python 写法 result = [ { "product_id": product_id, "total_quantity": sum(i["quantity"] for i in items), "total_amount": sum(i["quantity"] * i["price"] for i in items) } for product_id, items in groupby( [ i for o in orders if o["status"] == "completed" and o["order_date"] >= datetime.now() - timedelta(days=30) for i in o["items"] ], key=lambda i: i["product_id"] ) ] # 或者用 Pandas(更清晰) import pandas as pd df = pd.DataFrame(orders) result = ( df[df["status"] == "completed"] .query("order_date >= @thirty_days_ago") .explode("items") .groupby("product_id") .agg({"quantity": "sum", "amount": "sum"}) .nlargest(10, "amount") )C# 的 LINQ 链更清晰,Python 的复杂推导式可读性较差。
设计哲学
C# 的 LINQ 是"方法链模式"——每个操作返回新的序列,可以链式调用,适合复杂的数据处理管道。
Python 的推导式是"表达式压缩"——用一行代码表达复杂的逻辑,适合简单的转换和筛选。
C# 的 LINQ 像是"流水线",每个环节清晰可见; Python 的推导式像是"压缩包",解压后才能看清内部。
坑点提醒
Python 推导式的变量泄漏——变量会泄漏到外部作用域:
x = 10 result = [x for x in range(5)] print(x) # 4!不是 10 了 # 避免方法:Python 3.0+ 的推导式有自己的作用域 # 但在旧版本中,推导式变量会泄漏过度嵌套的推导式——可读性灾难:
# 不好:嵌套太深 result = [ f(x, y) for x in range(10) for y in range(10) if g(x, y) for f in [h1, h2, h3] if condition(f, x, y) ] # 好:拆分成多步 filtered = [(x, y) for x in range(10) for y in range(10) if g(x, y)] result = [f(x, y) for x, y in filtered for f in [h1, h2, h3] if condition(f, x, y)]C# 的 LINQ 方法命名——英语不好会很痛苦:
// 需要记住很多方法名 // Where, Select, SelectMany, OrderBy, ThenBy // GroupBy, Distinct, Except, Intersect, Union // Any, All, First, Last, Single // Sum, Average, Min, Max, Count // Python 的关键字更直观 # for, in, if, sorted, sum, len性能陷阱——不必要的转换:
// 不好:多次转换 var result = list.Where(x => x > 0).Select(x => x * 2).ToList(); // 好:一次转换 var result = list.Where(x => x > 0).Select(x => x * 2).ToList(); // 上面两个一样,但注意不要在循环中多次调用 ToList()# 不好:多次创建列表 result1 = [x for x in numbers if x > 0] result2 = [x * 2 for x in result1] # 好:一次完成 result = [x * 2 for x in numbers if x > 0]迁移指南:C# 开发者最容易犯的错
混淆推导式和生成器表达式:
[x for x in list]立即创建列表,(x for x in list)是生成器忘记推导式变量泄漏:Python 3.12 之前,推导式变量会泄漏到外部作用域
过度使用推导式:复杂逻辑应该用普通循环,可读性更重要
**用
sorted()代替.OrderBy()**:sorted()返回新列表,原列表不变**用
sum()/len()代替.Average()**:Python 没有内置的average()函数**用
in代替.Contains()**:"x" in list比list.__contains__("x")更 Pythonic
一句话总结
C# 的 LINQ 是"流水线",Python 的推导式是"压缩包"——都能处理集合,但风格完全不同。
下一篇咱们来聊聊生成器与迭代器——C# 的yield return和 Python 的yield,几乎一样的设计哲学!
📦示例代码:C# 转 Python 全系列配套练习代码(含 48 章示例)
GitHub:GitHub - LadyKiller1025/csharp-python-demos: C# vs Python 学习系列 - Demo 代码合集 · GitHub
Gitee:https://gitee.com/qakjhzx/csharp-python-demos
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!