如何在Python列表推导中有效地过滤计算值?

时间:2008-09-24 22:08:57

标签: python list-comprehension

Python列表推导语法可以轻松过滤理解中的值。例如:

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调用两次昂贵的()。是否有一种理解语法允许您进行此测试,而每次只调用昂贵的一次?

9 个答案:

答案 0 :(得分:22)

经过一分钟的思考后得出了我自己的答案。可以使用嵌套的理解来完成:

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

我猜这有效,但我发现嵌套的理解只是边缘可读

答案 1 :(得分:21)

如果计算已经很好地捆绑到函数中,那么使用filtermap怎么样?

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

如果列表非常大,您可以使用itertools.imap

答案 2 :(得分:7)

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

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

它需要更多的水平空间,但是一眼就能看到它的功能更容易,最终你不会重复自己。

答案 3 :(得分:6)

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

map()将返回传递给expensive()的mylist中每个对象的值列表。然后你可以列出 - 理解它,并丢弃不必要的值。

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

答案 4 :(得分:5)

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

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. 在最后一步使用发生器到处都是,所以没有大的中间列表
  4. cf:'Generator Tricks for System Programmers' by David Beazley

答案 5 :(得分:2)

您可以始终memoize expensive()函数,以便第二次调用它只是查找x的计算值。

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

答案 6 :(得分:2)

你可以记住昂贵的(x)(如果你经常调用昂贵的(x),你可能应该以任何方式记住它。这个页面给出了python的memoize实现:

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

这有一个额外的好处,即昂贵的(x)可以运行而不是N次,因为任何重复的条目都将使用前一次执行的备忘录。

请注意,这假设昂贵(x)是真正的函数,并且不依赖于可能更改的外部状态。如果昂贵(x)确实取决于外部状态,并且您可以检测到该状态发生变化,或者您知道在列表理解期间不会更改,那么您可以在理解之前重置备忘录。

答案 7 :(得分:1)

我会偏爱:

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

这有利于:

答案 8 :(得分:0)

还有一个for循环的简单旧用法附加到列表中:

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