代码之家  ›  专栏  ›  技术社区  ›  Nick

如何有效地过滤python列表理解中的计算值?

  •  20
  • Nick  · 技术社区  · 17 年前

    python list理解语法使在理解中过滤值变得容易。例如:

    result = [x**2 for x in mylist if type(x) is int]
    

    将返回mylist中整数平方的列表。但是,如果测试涉及一些(昂贵的)计算,并且您想要对结果进行过滤,那该怎么办?一种选择是:

    result = [expensive(x) for x in mylist if expensive(x)]
    

    这将导致一个非“假”昂贵(x)值列表,但是每个x调用两次昂贵的()。是否有一个理解语法允许您在每个x只调用昂贵的一次的情况下执行此测试?

    9 回复  |  直到 13 年前
        1
  •  21
  •   John Millikin    17 年前

    如果计算已经很好地绑定到函数中,那么使用 filter map ?

    result = filter (None, map (expensive, mylist))
    

    你可以使用 itertools.imap 如果列表很大。

        2
  •  22
  •   Nick    17 年前

    经过一分钟的思考,我想出了自己的答案。它可以通过嵌套的理解来实现:

    result = [y for y in (expensive(x) for x in mylist) if y]
    

    我想这是可行的,尽管我发现嵌套的理解只有一点点可读性。

        3
  •  7
  •   Thomas Wouters    17 年前

    最明显的(我认为是最易读的)答案是不要使用列表理解或生成器表达式,而是使用真正的生成器:

    def gen_expensive(mylist):
        for item in mylist:
            result = expensive(item)
            if result:
                yield result
    

    它需要更多的水平空间,但一眼就能看清楚它在做什么要容易得多,最终你不会重复你自己。

        4
  •  6
  •   Dan Udey    17 年前
    result = [x for x in map(expensive,mylist) if x]
    

    map()将返回传递给昂贵()的mylist中每个对象的值列表。然后您可以列出理解这一点的列表,并丢弃不必要的值。

    这有点像嵌套的理解,但应该更快(因为Python解释器可以相当容易地优化它)。

        5
  •  5
  •   Gregg Lind    17 年前

    这正是发电机适合处理的问题:

    result = (expensive(x) for x in mylist)
    result = (do_something(x) for x in result if some_condition(x))
    ...
    result = [x for x in result if x]  # finally, a list
    
    1. 这就清楚地知道了在管道的每个阶段都发生了什么。
    2. 显式/隐式
    3. 在最后一步之前到处使用生成器,因此没有大的中间列表

    囊性纤维变性: 'Generator Tricks for System Programmers' by David Beazley

        6
  •  2
  •   yukondude    17 年前

    你可以一直 memoize 这个 expensive() 函数,以便第二次调用它仅仅是对 x .

    Here's just one of many implementations of memoize as a decorator .

        7
  •  2
  •   rcreswick    17 年前

    你可以回忆昂贵的(x)(如果你经常打电话给昂贵的(x),你可能应该以任何方式回忆它。此页提供了针对python的memoize的实现:

    http://code.activestate.com/recipes/52201/

    这有一个额外的好处,即运行昂贵的(x) 较少的 超过n次,因为任何重复条目都将使用上一次执行的备忘录。

    请注意,这假定昂贵的(x)是一个真正的函数,并且不依赖可能更改的外部状态。如果昂贵的(x)确实依赖于外部状态,并且您可以检测该状态何时更改,或者您知道它 习惯于 在列表理解过程中更改,然后可以在理解之前重置备忘录。

        8
  •  1
  •   Bengt    13 年前

    我将优先考虑:

    itertools.ifilter(bool, (expensive(x) for x in mylist))
    

    这样做的好处是:

        9
  •  0
  •   Bengt    13 年前

    有一个简单的旧用法 for 循环以追加到列表:

    result = []
    for x in mylist:
        expense = expensive(x)
        if expense:
            result.append(expense)