在 C# 里做函数式编程,用 LINQ——Where、Select、Aggregate,链式调用很优雅。
在 Python 里做函数式编程,用functools和itertools——reduce、chain、islice,函数组合很灵活。
两者都是处理集合的神器,但风格完全不同。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); // 25Python 版本:
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)); // 15Python 版本:
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)) # 27lru_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() | 以最长为准的 zip | Zip()配合DefaultIfEmpty() |
设计哲学
C# 的 LINQ 是"方法链模式"——每个操作返回新的序列,可以链式调用,适合复杂的数据处理管道。
Python 的 functools/itertools 是"函数组合"——每个函数都是独立的,可以自由组合,适合简单的转换和筛选。
C# 的 LINQ 像是"流水线",每个环节清晰可见; Python 的工具像是"乐高积木",可以自由拼接。
更深层的原因:
C# 的 LINQ 是编译时优化的,编译器可以内联和优化
Python 的工具是运行时组合的,更灵活但性能稍差
迁移指南:C# 开发者最容易犯的错
忘记
reduce需要 import:它在functools模块里,不在内置函数中groupby需要先排序:Python 的groupby要求输入已排序islice不返回列表:它返回迭代器,需要list()转换chain不是方法:它是函数,需要from itertools import chain性能考虑: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
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!