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

Nic*_*ick 21 python list-comprehension

Python列表理解语法可以轻松地在理解中过滤值.例如:

result = [x**2 for x in mylist if type(x) is int]
Run Code Online (Sandbox Code Playgroud)

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

result = [expensive(x) for x in mylist if expensive(x)]
Run Code Online (Sandbox Code Playgroud)

这将导致非"虚假"昂贵(x)值的列表,但是每个x调用两次昂贵的().是否有一种理解语法允许您进行此测试,而每次只调用一次昂贵的一次?

Nic*_*ick 23

经过一分钟的思考后,我想出了自己的答案.它可以通过嵌套的理解来完成:

result = [y for y in (expensive(x) for x in mylist) if y]
Run Code Online (Sandbox Code Playgroud)

我猜这有用,虽然我发现嵌套的理解只是边缘可读


Joh*_*kin 21

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

result = filter (None, map (expensive, mylist))
Run Code Online (Sandbox Code Playgroud)

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


Tho*_*ers 7

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

def gen_expensive(mylist):
    for item in mylist:
        result = expensive(item)
        if result:
            yield result
Run Code Online (Sandbox Code Playgroud)

它需要更多的水平空间,但更容易看到它一目了然,你最终不会重复自己.

  • 我认为如果你多次使用gen_expensive(),这是值得做的,即便如此,我认为过滤结果会胜出,因为你可以看到一行上发生了什么,而不是必须挖掘定义gen_expensive(). (2认同)

Dan*_*dey 6

result = [x for x in map(expensive,mylist) if x]
Run Code Online (Sandbox Code Playgroud)

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

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


Gre*_*ind 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
Run Code Online (Sandbox Code Playgroud)
  1. 这使得在管道的每个阶段发生的事情都非常清楚.
  2. 显式过度隐含
  3. 使用发生器到处都是最后一步,所以没有大的中间列表

cf: David Beazley的"系统程序员的生成器技巧"