【问题标题】:Discrepancy between python sum(list) and reduce(lambda x, y : x+y, list)?python sum(list) 和 reduce(lambda x, y : x+y, list) 之间的差异?
【发布时间】:2019-01-13 23:55:04
【问题描述】:

sum(list)reduce(lambda total , element : total + element, list)有什么区别

orderItemsMap  = [149.94, 250.0, 199.99, 249.9, 149.94]

>>> sum(orderItemsMap)

999.77

orderItemsRevenue = reduce(lambda total , element : total +element , orderItemsMap)

>>>orderItemsRevenue

999.77

【问题讨论】:

标签: python python-2.7 list sum reduce


【解决方案1】:

sum 更清楚你的意图,而且更短。

阅读reduce 版本,我可以看到您正在减少具有某些功能的列表。然后我读了那个函数,发现它正在添加值。所以,我可以弄清楚你在总结数字。然后我可能还需要验证你是否正确实现了它。

阅读sum 版本,我立即知道您正在对数字求和,并且做得对。


sum 更快。

In [905]: %timeit sum(orderItemsMap)
173 ns ± 3.13 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [906]: %timeit reduce(lambda total , element : total +element , orderItemsMap)
601 ns ± 8.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

reduce 版本必须做一个通用循环,而sum 版本可以做一些优化。此外,reduce 版本必须为每对值调用您的lambda,而sum 版本可以直接跳转到__add__。而且,至少在 CPython 中,sum 对小整数求和进行了进一步优化。


sum 适用于空列表。

In [907]: sum([])
Out[907]: 0
In [908]: reduce(lambda total , element : total +element, [])
TypeError: reduce() of empty sequence with no initial value 

当然,您可以通过添加第三个参数使 reduce 为空列表工作:

In [909]: reduce(lambda total , element : total +element, [], 0)
Out[909]: 0

……但这让它变得更长,更不明显。


sum 不鼓励你不小心做你不想做的事情。

In [911]: sum(["abc", "def", "ghi"])
TypeError: unsupported operand type(s) for +: 'int' and 'str'
In [912]: sum(["abc", "def", "ghi"], '')
TypeError: sum() can't sum strings [use ''.join(seq) instead]
In [913]: reduce(lambda total , element : total +element, ["abc", "def", "ghi"])
Out[913]: 'abcdefghi'

使用reduce,我只是连接了一堆字符串,这可能需要二次时间,而我真正想做的是调用''.join,这需要线性时间。

好的,CPython 3.7 和 PyPy 3.5/6.0 碰巧优化了这种情况以使其更好......但他们没有这样做,例如,元组列表。


sum 不会让 Guido 哭泣。

许多 Python 开发人员讨厌 reduce。通常的引用是“在每个reduce 内部,都有一个for 循环在努力摆脱”。或SyntaxError('Lisp needs more parentheses')

我认为这有点极端,而且我知道我不是唯一一个在 Python 中找到了 reduce 的好用处的人(其他核心开发人员不会说服 Guido 移动它functools 而不是在 3.0 中删除它...),但它确实会引发一个标志,让我更仔细地查看我的代码(或其他人的),看看是否有更 Pythonic 的方式来编写它,并且经常在那里是。


reduce 可以做除求和以外的事情。

例如,假设您想从一堆数字中创建一个数字:

def fromdigits(*digits, base):
    return reduce(lambda acc, digit: base*acc + digit), digits, 0)

尝试使用sum 编写。

(虽然我敢打赌,这个例子让 Guido 哭了,但首先想到的不是树……)

【讨论】:

  • 注意:reduce 也适用于空输入,您只需要显式传递一个初始值作为第三个参数(其中sumstart 值默认为0) . reduce(lambda total , element : total +element, [], 0) 按预期工作。
  • "尝试使用sum 编写。"接受挑战! def fromdigits(*digits, base=10): return sum(map(operator.mul, map(base.__pow__, range(len(digits))), reversed(digits))) :-)
  • @ShadowRanger 该死,我以为有人会创建一个覆盖__add__ 的类。 :)
【解决方案2】:

从逻辑上讲,sum(list)reduce(lambda total, element: total + element, list, 0) 之间没有区别(注意默认值作为第三个参数传递给 reduce)。

sum 有一些小的区别;它有一个特殊的快速路径来对小整数求和(当总和可以放入 C long 时),它避免了每个加法 Python 级别的函数调用,并且即使给定 @987654327,它也拒绝对 str 对象求和'' 的值(逻辑上没有什么可以阻止它,但是在str 上使用重复添加是非常低效的,所以它拒绝str 将用户指向''.join,这是连接许​​多str 的正确方法)。

sum 不同,reduce 将适用于任何可添加类型,只要输入中至少有一个元素即可(其中sum 要求您为任何非数字传递start 值类型)。

一般而言,您应该为此使用sum(或''.join 用于str,或itertools.chain 用于组合像list/tuple 这样的可迭代对象); reduce 不那么专业,速度较慢,并且通常被维护者认为更难理解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-11
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多