查看: 154|回复: 0

Python reduce函数累积计算与initializer用法

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在 Python 的函数式工具中,map 负责逐元素映射,filter 负责筛选,而 functools.reduce 负责把可迭代对象折叠成一个结果。它的执行方式是把序列元素两两聚合:先算前两个,再把结果与下一个继续算,直到只剩下一个累积值。这个思路适合累加、累乘、求最值、配置合并和流水线处理。

一、语法和工作原理
  1. from functools import reduce
  2. # reduce(function, iterable[, initializer])
  3. # function: 二元函数,参数为(累积值, 下一个元素)
  4. # iterable: 可迭代对象
  5. # initializer: 可选初始累积值
复制代码

没有 initializer 时,累积值先取第一个元素,再遍历剩余元素;有 initializer 时,累积值从 initializer 开始,再遍历所有元素。空序列且没有 initializer 会抛 TypeError:reduce() of empty iterable with no initial value。所以实际项目里建议提供 initializer,让空数据也有确定结果。
  1. from functools import reduce
  2. numbers = [1, 2, 3, 4, 5]
  3. def add_and_show(a, b):
  4.     result = a + b
  5.     print(f'{a} + {b} = {result}')
  6.     return result
  7. total = reduce(add_and_show, numbers)
  8. print(f'最终结果: {total}')
  9. # 1 + 2 = 3
  10. # 3 + 3 = 6
  11. # 6 + 4 = 10
  12. # 10 + 5 = 15
  13. # 最终结果: 15
复制代码

initializer 会改变起始值,但不改变计算规则。例如初始值为 0 时结果可能相同,初始值为 10 时会在原结果上多加 10;空列表配合初始值 0 可安全返回 0。
  1. from functools import reduce
  2. numbers = [1, 2, 3, 4, 5]
  3. print(reduce(lambda a, b: a + b, numbers))       # 15
  4. print(reduce(lambda a, b: a + b, numbers, 0))    # 15
  5. print(reduce(lambda a, b: a + b, numbers, 10))   # 25
  6. empty_list = []
  7. # reduce(lambda a, b: a + b, empty_list)  # TypeError
  8. print(reduce(lambda a, b: a + b, empty_list, 0)) # 0
复制代码

二、聚合计算与数学运算

用 operator 模块里的 add、mul,可以让聚合代码更直观:累加、累乘、找最大最小值、拼接字符串都可由 reduce 完成。字符串拼接时,lambda a, b: a + b 得到无分隔结果,lambda a, b: f'{a} {b}' 得到空格分隔结果。
  1. from functools import reduce
  2. import operator
  3. import math
  4. numbers = [1, 2, 3, 4, 5]
  5. print(reduce(operator.add, numbers))  # 15
  6. print(reduce(operator.mul, numbers))  # 120
  7. print(reduce(lambda a, b: a if a > b else b, numbers))  # 5
  8. print(reduce(lambda a, b: a if a < b else b, numbers))  # 1
  9. words = ['Python', '是', '一门', '优雅的', '语言']
  10. print(reduce(lambda a, b: a + b, words))       # Python是一门优雅的语言
  11. print(reduce(lambda a, b: f'{a} {b}', words))  # Python 是 一门 优雅的 语言
复制代码

阶乘、最大公约数、最小公倍数也有对应实现。阶乘用 reduce(operator.mul, range(1, n + 1));最大公约数用 reduce(math.gcd, numbers);最小公倍数用 reduce(math.lcm, numbers),其中 math.lcm 需要 Python 3.9+。注意空列表要提前校验,否则 reduce 会缺少初始值。
  1. from functools import reduce
  2. import operator
  3. import math
  4. def factorial(n):
  5.     if n < 0:
  6.         raise ValueError('阶乘只对非负整数定义')
  7.     if n == 0:
  8.         return 1
  9.     return reduce(operator.mul, range(1, n + 1))
  10. print(factorial(5))  # 120
  11. print(factorial(10)) # 3628800
  12. def gcd_of_list(numbers):
  13.     if not numbers:
  14.         raise ValueError('列表不能为空')
  15.     return reduce(math.gcd, numbers)
  16. print(gcd_of_list([48, 64, 96]))  # 16
  17. print(gcd_of_list([100, 75, 25])) # 25
  18. print(gcd_of_list([17, 31]))      # 1
  19. def lcm_of_list(numbers):
  20.     if not numbers:
  21.         raise ValueError('列表不能为空')
  22.     return reduce(math.lcm, numbers)  # Python 3.9+
  23. print(lcm_of_list([4, 6, 8]))  # 24
复制代码

三、模拟 map 和 filter 只适合理解原理

reduce 可以通过累积列表模拟 map 和 filter,但实际开发中不要这样写,因为 map、filter、列表推导式更直接。
  1. from functools import reduce
  2. def my_map(func, iterable):
  3.     return reduce(lambda acc, item: acc + [func(item)], iterable, [])
  4. print(my_map(lambda x: x ** 2, [1, 2, 3, 4, 5]))  # [1, 4, 9, 16, 25]
  5. def my_filter(func, iterable):
  6.     return reduce(lambda acc, item: acc + [item] if func(item) else acc, iterable, [])
  7. print(my_filter(lambda x: x % 2 == 0, range(1, 11)))  # [2, 4, 6, 8, 10]
复制代码

四、高级场景:流水线和字典合并

管道处理是 reduce 比较有价值的地方:把多个数据处理函数放进列表,再用 reduce(lambda data, step: step(data), pipeline, raw_data) 依次调用。原文示例先去掉 None,再转整数,再过滤正数,最后乘以 2,得到 [2, 6, 10, 16]。
  1. from functools import reduce
  2. def remove_none(data):
  3.     return [x for x in data if x is not None]
  4. def convert_to_int(data):
  5.     return [int(x) for x in data]
  6. def filter_positive(data):
  7.     return [x for x in data if x > 0]
  8. def multiply_by(data, factor):
  9.     return [x * factor for x in data]
  10. pipeline = [
  11.     remove_none,
  12.     convert_to_int,
  13.     filter_positive,
  14.     lambda data: multiply_by(data, 2),
  15. ]
  16. raw_data = ['1', None, '3', '-2', '5', None, '0', '8']
  17. result = reduce(lambda data, step: step(data), pipeline, raw_data)
  18. print(result)  # [2, 6, 10, 16]
复制代码

字典合并也可以使用 reduce。浅合并用 lambda a, b: {**a, **b},后面的键覆盖前面的键。嵌套字典需要自己实现 deep_merge:遇到两边都是 dict 的键时递归合并,否则直接覆盖。
  1. from functools import reduce
  2. dicts = [
  3.     {'host': 'localhost', 'port': 8080},
  4.     {'port': 9090, 'debug': True},
  5.     {'timeout': 30, 'retries': 3},
  6. ]
  7. merged = reduce(lambda a, b: {**a, **b}, dicts)
  8. print(merged)
  9. # {'host': 'localhost', 'port': 9090, 'debug': True, 'timeout': 30, 'retries': 3}
  10. configs = [
  11.     {'database': {'host': 'localhost', 'port': 5432}, 'debug': False},
  12.     {'database': {'port': 5433, 'name': 'mydb'}, 'cache': True},
  13.     {'debug': True, 'timeout': 30},
  14. ]
  15. def deep_merge(a, b):
  16.     result = dict(a)
  17.     for key, value in b.items():
  18.         if key in result and isinstance(result[key], dict) and isinstance(value, dict):
  19.             result[key] = deep_merge(result[key], value)
  20.         else:
  21.             result[key] = value
  22.     return result
  23. final_config = reduce(deep_merge, configs, {})
  24. print(final_config)
  25. # {'database': {'host': 'localhost', 'port': 5433, 'name': 'mydb'}, 'debug': True, 'cache': True, 'timeout': 30}
复制代码

五、reduce 与内置函数的取舍

能用内置函数时优先用内置函数。sum、math.prod、max、''.join 通常比 reduce 更清晰,也更容易被其他开发者理解。只有复杂聚合逻辑或流水线场景,reduce 才更自然。若 reduce 让代码难读,应改回 for 循环。
  1. from functools import reduce
  2. import operator
  3. import math
  4. numbers = [1, 2, 3, 4, 5]
  5. total1 = reduce(operator.add, numbers)  # 15
  6. total2 = sum(numbers)                   # 15,更清晰
  7. product1 = reduce(operator.mul, numbers)  # 120
  8. product2 = math.prod(numbers)             # 120,Python 3.8+
  9. max1 = reduce(lambda a, b: a if a > b else b, numbers)  # 5
  10. max2 = max(numbers)                                     # 5,更好
  11. words = ['a', 'b', 'c']
  12. concat1 = reduce(lambda a, b: a + b, words)  # 'abc'
  13. concat2 = ''.join(words)                     # 'abc',更好
复制代码

六、总结

reduce(func, iterable[, initializer]) 的核心是把序列累积为单个值。建议始终提供 initializer,避免空序列报错;简单聚合优先用 sum、max、math.prod 等内置函数;reduce 模拟 map/filter 仅作学习;管道处理和深度合并字典是较实用的高级场景。当需要把一组数据折叠成一个结果时,再考虑 reduce。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-25 12:50 , Processed in 0.019938 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部