【问题标题】:Counter most_common elements as a generatorCounter most_common 元素作为生成器
【发布时间】:2026-02-08 14:50:01
【问题描述】:

我正在使用collections.Counter 对象。我想一一获取最常见的元素,但 most_common 方法为我提供了整个元素列表。
有什么办法可以将此列表作为生成器?

【问题讨论】:

  • 如我的回答中所述,可以伪造它,但我不禁想知道您希望通过拥有这样的生成器获得什么,因为在most_common() 可以返回它必须处理的任何内容之前它的所有输入并存储中间结果——因此无论您是否安排将其零碎返回给您,都会创建整个列表。换句话说,我认为没有一种增量方式来进行处理。
  • 你是对的。我在柜台上有很多元素,所以我试图避免创建一个包含所有元素的列表。
  • 所以听起来你同意你想要的东西是不可能的。在我看来,只要求n 最常见的元素可能至少会减少所创建的内部列表的大小。

标签: python iterator generator counter


【解决方案1】:

您可以像这样简单地将collection.Counter.most_common() 包装在生成器函数中:

from collections import Counter

def most_common(iterable, n=None):
    return iter(Counter(iterable).most_common() if n is None else
                Counter(iterable).most_common(n))

for item in most_common('Mississippi', 3):
    print item

注意:虽然似乎将 None 传递给 collection.Counter.most_common() 与不传递任何内容相同(一个常见的 Python 习惯用法),但当前文档确实没有这么说,所以我已决定谨慎行事并使用.. if .. else .. 条件表达式仅在不是None 时传递n,但其文档字符串确实明确表示“如果nNone,则列出所有元素计数”。

如果您不担心这种细微的差异,则可以将上述内容缩短为:

def most_common(iterable, n=None):
    return iter(Counter(iterable).most_common(n))

这很短,您可能只想在任何 Counter.most_common() 调用周围编写 iter() 代码,以避免调用额外包装函数的开销。

无论如何,输出应该是这样的:

('i', 4)
('s', 4)
('p', 2)

【讨论】:

  • 您可以将函数重写为return iter(Counter(iterable).most_common(n))。将None传递给most_common()相当于省略参数,for循环使用iter()可以更简洁的表达。
  • @Sven Marnach:感谢您提醒我有关 iter() 的信息,但我不完全同意您关于将 None 传递给 Counter.most_common() 的优化。
  • 不幸的是,这并不能解决 most_common() 的真正问题:它将完整数据复制到新变量中。最好有一个 most_common 生成器,它可以迭代数据而不复制它。
  • @Dennis:如果你想一想,most_common()必须复制数据,以便它可以用作关联值(计数)的键——它是毕竟是dict 的子类。
  • @martineau 我明白你的意思。好吧,Counter 可以在添加元素时对其进行排序(或保留顺序索引),然后可以使用 iter_most_common 方法来迭代这些元素。但是如果它保持元素未排序(我假设是这种情况),那么是的,它需要收集它们并在内存中排序。