【问题标题】:Get number of items from list (or other iterable) with certain conditionpython:在特定条件下从列表(序列)中获取项目数
【发布时间】:2013-02-28 18:49:42
【问题描述】:

假设我有一个包含大量项目的列表。

l = [ 1, 4, 6, 30, 2, ... ]

我想从该列表中获取项目数,其中项目应满足特定条件。我的第一个想法是:

count = len([i for i in l if my_condition(l)])

但如果 my_condition() 过滤列表也有大量项目,我认为 为过滤结果创建新列表只是浪费内存。为了效率,恕我直言,上面的调用不能比:

count = 0
for i in l:
    if my_condition(l):
        count += 1

有没有什么函数式的方法可以在不生成临时列表的情况下获得满足特定条件的项目数?

提前致谢。

【问题讨论】:

  • 生成器和列表之间的选择是执行时间和内存消耗之间的选择。如果您分析代码,您会惊讶于结果与直觉相反的频率。过早的优化是万恶之源。

标签: python list count functional-programming sequence


【解决方案1】:

您可以使用generator expression

>>> l = [1, 3, 7, 2, 6, 8, 10]
>>> sum(1 for i in l if i % 4 == 3)
2

甚至

>>> sum(i % 4 == 3 for i in l)
2

它使用了int(True) == 1这一事实。

或者,您可以使用 itertools.imap (python 2) 或简单地使用 map (python 3):

>>> def my_condition(x):
...     return x % 4 == 3
... 
>>> sum(map(my_condition, l))
2

【讨论】:

  • @mgilson:我认为它从来没有进行过这种计算——start 默认为 0,所以第一个加法是 True + 0,不是吗?
  • 是的。也许我应该更清楚...... int(True) 是什么并不重要。 int("1") == 1 也可以,但这并不意味着你可以做到 "1" + 0。重要的是python如何评估integer + Trueinteger + False
  • @mgilson:嗯,好吧,你说服了我。
  • 重点是 boolint 的子类,因此您可以轻松添加布尔值和整数(True 的值为 1,False 的值为0).
  • 嗯,这就是我提到int(True) == 1 的意思,但你的观点int("1") == 1 证明,以这种方式缩写它可能暗示不正确的事情。
【解决方案2】:

您需要generator comprehension,而不是此处的列表。

例如,

l = [1, 4, 6, 7, 30, 2]

def my_condition(x):
    return x > 5 and x < 20

print sum(1 for x in l if my_condition(x))
# -> 2
print sum(1 for x in range(1000000) if my_condition(x))
# -> 14

或者使用itertools.imap(虽然我认为显式列表和生成器表达式看起来更像Pythonic)。

请注意,尽管在 sum 示例中并不明显,但您可以很好地编写生成器推导。例如,

inputs = xrange(1000000)      # In Python 3 and above, use range instead of xrange
odds = (x for x in inputs if x % 2)  # Pick odd numbers
sq_inc = (x**2 + 1 for x in odds)    # Square and add one
print sum(x/2 for x in sq_inc)       # Actually evaluate each one
# -> 83333333333500000

这项技术很酷的一点是,您可以在代码中指定概念上单独的步骤,而无需强制评估和存储在内存中,直到评估最终结果。

【讨论】:

    【解决方案3】:

    如果您更喜欢函数式编程,也可以使用 reduce 来完成此操作

    reduce(lambda count, i: count + my_condition(i), l, 0)
    

    这样你只做 1 遍,不会生成中间列表。

    【讨论】:

      【解决方案4】:

      你可以这样做:

      l = [1,2,3,4,5,..]
      count = sum(1 for i in l if my_condition(i))
      

      只为满足条件的每个元素加 1。

      【讨论】:

        【解决方案5】:
        from itertools import imap
        sum(imap(my_condition, l))
        

        【讨论】:

        • imap 不适用于当前的 Python。
        猜你喜欢
        • 2022-12-07
        • 2020-11-07
        • 1970-01-01
        • 1970-01-01
        • 2015-02-04
        • 1970-01-01
        • 2013-07-22
        • 2011-11-22
        • 2020-09-19
        相关资源
        最近更新 更多