能否带初始值对Python的reduce函数进行部分应用?
解决
reduce搭配partial时的参数顺序坑 这个问题我之前也踩过,本质是reduce的参数定义和partial的位置绑定逻辑不匹配搞出来的麻烦。先给你理清楚为啥会报错:
为啥会触发TypeError?
先回忆下functools.reduce的参数顺序:
reduce(function, iterable[, initializer])
当你写partial(reduce, lambda a,b: a+b, 100)的时候,partial是按位置顺序把参数塞给reduce的:
- 第一个绑定的是
function(就是那个求和的lambda) - 第二个绑定的是
reduce的第二个参数——也就是需要迭代的对象,但你传的是100(本来应该是初始值) - 等你调用
f([1,2,3])时,这个列表被当成了reduce的第三个参数(初始值),而100被当成了要迭代的对象,整数肯定不能迭代啊,所以直接抛出TypeError。
靠谱的解决办法
这里有几种实用的方案,按需选就行:
1. 用lambda包装调整参数顺序
最直接的方式就是用lambda把参数顺序转过来,让可迭代对象先传入,再把初始值递进去:
from functools import reduce, partial f = partial(lambda iterable, func, init: reduce(func, iterable, init), func=lambda a,b: a+b, init=100) print(f([1,2,3])) # 输出106,正确
要是嫌麻烦,直接写个lambda函数更简洁:
f = lambda iterable: reduce(lambda a,b: a+b, iterable, 100) print(f([1,2,3])) # 同样输出106
2. 自定义一个参数顺序反转的reduce包装
如果需要多次复用这种场景,不如自己写个小包装函数,把初始值放到可迭代对象前面:
from functools import reduce, partial def reduce_init_first(func, initializer, iterable): return reduce(func, iterable, initializer) # 现在用partial绑定就完全没问题了 f = partial(reduce_init_first, lambda a,b: a+b, 100) print(f([1,2,3])) # 106
3. 用关键字参数绑定(Python 3+专属)
从Python 3开始,reduce支持用关键字参数指定initializer,这应该是最优雅的解法:
from functools import reduce, partial f = partial(reduce, lambda a,b: a+b, initializer=100) print(f([1,2,3])) # 106,完美运行
直接通过关键字把100锁定为初始值,彻底避开位置参数的混淆问题。
测试一下
把上面的代码跑一遍,结果都是106,完全符合预期,不会再报错啦。
内容的提问来源于stack exchange,提问作者Solomon Bothwell
相关产品推荐
相关产品推荐

