在 Python 中对 Counter 对象求和的有效方法

Ian*_* Te 3 python parallel-processing performance

有没有更有效的方法或库可以更快地添加 Counter 对象?

到目前为止,我正在使用以下代码,我需要比它更快的东西:

cnt = sum([Counter(objects) for objects in object_list], Counter())
Run Code Online (Sandbox Code Playgroud)

Sha*_*ger 5

不要制作大量临时Counters,只需制作一个,并让它计算一切

from collections import Counter
from itertools import chain

cnt = Counter(chain.from_iterable(object_list)
Run Code Online (Sandbox Code Playgroud)

Counter从较小的输入生成一堆单独的s 是昂贵的,并且否认了CounterC 加速器用于计算输入可迭代项给您的一些性能优势。使用sum将它们组合起来使其成为画家的 Schlemiel 算法,因为它生成了大量Counter逐渐增加大小的临时s(工作最终大致是计数的项目总数O(m * n)在哪里n,以及m它们被拆分的对象数量)。对扁平化的输入可迭代对象计数一次可以将工作归结为O(n).

将可迭代对象的可迭代对象扁平化为单个输入流并将其全部计数一次可显着减少运行时间,尤其是对于大量较小对象。

chain.from_iterable像这样使用相当于:

cnt = Counter(item for object in object_list for item in object)
Run Code Online (Sandbox Code Playgroud)

但将工作推到 CPython 参考解释器上的 C 层;如果 的内容也是object_list在 C 中实现的所有内置类型,那么当您使用 时根本不会执行任何字节码chain.from_iterable,从而消除了大量解释器开销。

如果您必须有一堆Counters,至少可以通过就地更新 accumulator 来避免 Schlemiel the Painter 算法Counter。你可以用一种丑陋的方式将它排成一行(这仍然会产生临时的Counters,但至少它不会产生每次都丢弃的逐渐变大的临时对象):

cnt = functools.reduce(operator.iadd, map(Counter, object_list), Counter())
Run Code Online (Sandbox Code Playgroud)

或使其更具可读性(并避免任何额外的临时文件):

cnt = Counter()
for obj in object_list:
    cnt.update(obj)  # cnt += Counter(obj) works, but involves unnecessary temporary
Run Code Online (Sandbox Code Playgroud)