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# 开发者最容易犯的错

  1. 混淆推导式和生成器表达式[x for x in list] 立即创建列表,(x for x in list) 是生成器

  2. 忘记推导式变量泄漏:Python 3.12 之前,推导式变量会泄漏到外部作用域

  3. 过度使用推导式:复杂逻辑应该用普通循环,可读性更重要

  4. **用 sorted() 代替 .OrderBy()**:sorted() 返回新列表,原列表不变

  5. **用 sum()/len() 代替 .Average()**:Python 没有内置的 average() 函数

  6. **用 in 代替 .Contains()**:"x" in listlist.__contains__("x") 更 Pythonic

一句话总结

C# 的 LINQ 是"流水线",Python 的推导式是"压缩包"——都能处理集合,但风格完全不同。

下一篇咱们来聊聊生成器与迭代器——C# 的 yield return 和 Python 的 yield,几乎一样的设计哲学!


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

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

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐