C#转Python第2.7篇:如果你是从 C# 转 Python 的,functools 和 itertools 会让你觉得 Python 的工具箱比 LINQ 更灵活

C#转Python第2.7篇:如果你是从 C# 转 Python 的,functools 和 itertools 会让你觉得 Python 的工具箱比 LINQ 更灵活

在 C# 里做函数式编程,用 LINQ——WhereSelectAggregate,链式调用很优雅。

在 Python 里做函数式编程,用functoolsitertools——reducechainislice,函数组合很灵活。

两者都是处理集合的神器,但风格完全不同。C# 的 LINQ 是"方法链",Python 的工具是"函数组合"。刚转 Python 的时候,我以为 LINQ 已经够强了,直到我用上了 functools 和 itertools。

functools 模块

functools提供了高阶函数(操作函数的函数)。

reduce vs Aggregate

C# 版本:

using System.Linq; var numbers = new[] { 1, 2, 3, 4, 5 }; // 聚合 int sum = numbers.Aggregate((a, b) => a + b); // 15 int product = numbers.Aggregate((a, b) => a * b); // 120 // 带初始值 int sum10 = numbers.Aggregate(10, (a, b) => a + b); // 25

Python 版本:

from functools import reduce numbers = [1, 2, 3, 4, 5] # 聚合 total = reduce(lambda a, b: a + b, numbers) # 15 product = reduce(lambda a, b: a * b, numbers) # 120 # 带初始值 total10 = reduce(lambda a, b: a + b, numbers, 10) # 25 # 实际应用:找最大值 max_val = reduce(lambda a, b: a if a > b else b, numbers)
特性C#Python
方法名Aggregate()reduce()
位置LINQ 扩展方法functools模块
延迟执行支持不支持
初始值支持支持

partial vs 偏函数

C# 版本:

// C# 用 Func 委托模拟偏函数 Func<int, int, int> add = (a, b) => a + b; Func<int, int> add5 = b => add(5, b); Console.WriteLine(add5(3)); // 8 // 或者用方法 static Func<int, int> MakeAdder(int a) => b => a + b; var add10 = MakeAdder(10); Console.WriteLine(add10(5)); // 15

Python 版本:

from functools import partial def add(a, b): return a + b # 创建偏函数 add5 = partial(add, 5) print(add5(3)) # 8 add10 = partial(add, 10) print(add10(5)) # 15 # 实际应用:固定参数 def power(base, exponent): return base ** exponent square = partial(power, exponent=2) cube = partial(power, exponent=3) print(square(5)) # 25 print(cube(3)) # 27

lru_cache vs 缓存

C# 版本:

// C# 用 MemoryCache 或自己实现 using Microsoft.Extensions.Caching.Memory; var cache = new MemoryCache(new MemoryCacheOptions()); string GetCachedData(string key) { return cache.GetOrCreate(key, entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5); return ExpensiveComputation(key); }); }

Python 版本:

from functools import lru_cache @lru_cache(maxsize=128) def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) # 使用 print(fibonacci(100)) # 瞬间计算出来 # 查看缓存信息 print(fibonacci.cache_info()) # 清除缓存 fibonacci.cache_clear()

Python 的lru_cache比 C# 的MemoryCache更简单——一个装饰器搞定。

itertools 模块

itertools提供了迭代器相关的工具函数。

chain vs SelectMany

C# 版本:

var list1 = new[] { 1, 2, 3 }; var list2 = new[] { 4, 5, 6 }; var list3 = new[] { 7, 8, 9 }; // 展平 var flat = list1.SelectMany(x => new[] { x }) .Concat(list2.SelectMany(x => new[] { x })) .Concat(list3.SelectMany(x => new[] { x })) .ToList(); // 或者用 LINQ var flat2 = list1.Concat(list2).Concat(list3).ToList();

Python 版本:

from itertools import chain list1 = [1, 2, 3] list2 = [4, 5, 6] list3 = [7, 8, 9] # 展平 flat = list(chain(list1, list2, list3)) print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9] # 链接多个可迭代对象 flat2 = list(chain.from_iterable([list1, list2, list3])) print(flat2) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

islice vs Skip/Take

C# 版本:

var numbers = Enumerable.Range(1, 100); // 取前10个 var first10 = numbers.Take(10).ToList(); // 跳过前10个 var skip10 = numbers.Skip(10).ToList(); // 分页 var page = numbers.Skip(20).Take(10).ToList();

Python 版本:

from itertools import islice numbers = range(1, 101) # 取前10个 first10 = list(islice(numbers, 10)) print(first10) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 跳过前10个 skip10 = list(islice(numbers, 10, None)) print(skip10) # [11, 12, 13, ...] # 分页 page = list(islice(numbers, 20, 30)) print(page) # [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

product vs 笛卡尔积

C# 版本:

var colors = new[] { "红", "蓝", "绿" }; var sizes = new[] { "S", "M", "L" }; // 笛卡尔积 var combinations = colors .SelectMany(c => sizes, (c, s) => new { Color = c, Size = s }) .ToList();

Python 版本:

from itertools import product colors = ["红", "蓝", "绿"] sizes = ["S", "M", "L"] # 笛卡尔积 combinations = list(product(colors, sizes)) print(combinations) # [('红', 'S'), ('红', 'M'), ('红', 'L'), ('蓝', 'S'), ...] # 展平为单个元组列表 flat = [(c, s) for c, s in product(colors, sizes)]

groupby vs GroupBy

C# 版本:

var students = new[] { new { Name = "Alice", Grade = "A" }, new { Name = "Bob", Grade = "B" }, new { Name = "Charlie", Grade = "A" }, new { Name = "David", Grade = "B" }, }; // 分组 var grouped = students .GroupBy(s => s.Grade) .ToDictionary(g => g.Key, g => g.ToList());

Python 版本:

from itertools import groupby students = [ {"name": "Alice", "grade": "A"}, {"name": "Bob", "grade": "B"}, {"name": "Charlie", "grade": "A"}, {"name": "David", "grade": "B"}, ] # 注意:groupby 要求先排序 students.sort(key=lambda s: s["grade"]) # 分组 grouped = {} for grade, group in groupby(students, key=lambda s: s["grade"]): grouped[grade] = list(group) print(grouped) # {'A': [{'name': 'Alice', ...}, {'name': 'Charlie', ...}], ...}

常用 itertools 函数速查

函数说明C# 对应
chain()链接多个可迭代对象SelectMany()Concat()
islice()切片迭代器Skip().Take()
product()笛卡尔积SelectMany()
permutations()排列无内置
combinations()组合无内置
groupby()分组GroupBy()
filterfalse()反向过滤Where()取反
starmap()映射Select()
zip_longest()以最长为准的 zipZip()配合DefaultIfEmpty()

设计哲学

C# 的 LINQ 是"方法链模式"——每个操作返回新的序列,可以链式调用,适合复杂的数据处理管道。

Python 的 functools/itertools 是"函数组合"——每个函数都是独立的,可以自由组合,适合简单的转换和筛选。

C# 的 LINQ 像是"流水线",每个环节清晰可见; Python 的工具像是"乐高积木",可以自由拼接。

更深层的原因

  • C# 的 LINQ 是编译时优化的,编译器可以内联和优化

  • Python 的工具是运行时组合的,更灵活但性能稍差

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

  1. 忘记reduce需要 import:它在functools模块里,不在内置函数中

  2. groupby需要先排序:Python 的groupby要求输入已排序

  3. islice不返回列表:它返回迭代器,需要list()转换

  4. chain不是方法:它是函数,需要from itertools import chain

  5. 性能考虑:Python 的迭代器是惰性的,适合大数据集

坑点提醒

groupby需要先排序——否则分组不正确:

from itertools import groupby # 错误:未排序 data = [("A", 1), ("B", 2), ("A", 3)] for key, group in groupby(data, key=lambda x: x[0]): print(key, list(group)) # 输出: A [(A, 1)], B [(B, 2)], A [(A, 3)] # A 被分成了两组! # 正确:先排序 data.sort(key=lambda x: x[0]) for key, group in groupby(data, key=lambda x: x[0]): print(key, list(group)) # 输出: A [(A, 1), (A, 3)], B [(B, 2)]

islice不支持负索引——不能用islice(arr, -5, None)

from itertools import islice lst = [1, 2, 3, 4, 5] # 错误 # list(islice(lst, -5, None)) # ValueError # 正确 list(islice(lst, len(lst)-5, len(lst)))

reduce不如内置函数快——优先用sum()max()min()

from functools import reduce numbers = [1, 2, 3, 4, 5] # 不推荐 reduce(lambda a, b: a + b, numbers) # 推荐 sum(numbers)

一句话总结

C# 的 LINQ 是"流水线",Python 的 functools/itertools 是"乐高积木"——都能处理集合,但风格完全不同。

下一篇咱们来聊聊闭包与作用域——Python 的 LEGB 规则 vs C# 的闭包,作用域查找的"寻宝游戏"。


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

  • GitHub:https://github.com/LadyKiller1025/csharp-python-demos

  • Gitee:https://gitee.com/qakjhzx/csharp-python-demos

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