可以捕获Python列表推导式的返回值用于条件吗?

7 投票
4 回答
1430 浏览
提问于 2025-04-17 09:41

我想在一个列表推导式中构造一个值,同时对这个值进行过滤。比如说:

[expensive_function(x) for x in generator where expensive_function(x) < 5]

我想避免在每次迭代中调用一次expensive_function两次。

这个generator可能会返回一个无限的序列,而列表推导式并不是懒惰求值的。所以这样写是行不通的:

[y in [expensive_function(x) for x in generator where expensive_function(x)] where y < 5]

我可以用其他方式来写这个,但我觉得用列表推导式来做是合适的,我相信这是一种常见的用法(不管是否可行!)。

4 个回答

2

你应该创建两个生成器表达式:

ys_all = (expensive(x) for x in xs)
ys_filtered = (y for y in ys_all if y <5)

或者

from itertools import imap, ifilter
ys = ifilter(lambda y : y < 5, imap(expensive, xs))
3

我来回答一下关于如何在列表推导式中捕获中间结果以便在条件中使用的部分问题,同时忽略从无限生成器构建列表推导式的问题(显然这个是行不通的),以防有人在这里寻找标题中的问题答案。

假设你有一个这样的列表推导式:

[expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0]

你想避免在通过过滤条件时计算 expensive_function 两次。某些编程语言(比如Scala、Haskell等)有更灵活的推导语法,可以让你给从推导变量计算出的表达式命名,这样你就可以做类似下面的事情:

# NOT REAL PYTHON
[result for x in xrange(5) for result = expensive_function(x) if result % 2 == 0]

不过,你也可以通过将赋值 result = expensive_function(x) 转换成对一个元素的序列进行另一个 for 循环来轻松模拟这一点:

[result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0]

这里是证明:

>>> def expensive_function(x):
        print 'expensive_function({})'.format(x)
        return x + 10
>>> [expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0]
expensive_function(0)
expensive_function(0)
expensive_function(1)
expensive_function(2)
expensive_function(2)
expensive_function(3)
expensive_function(4)
expensive_function(4)
[10, 12, 14]
>>> [result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0]
expensive_function(0)
expensive_function(1)
expensive_function(2)
expensive_function(3)
expensive_function(4)
[10, 12, 14]
10

如果generator可能是无限的,你就不应该使用列表推导式。而且并不是所有的代码都必须写成一行。

def filtered_gen(gen):
    for item in gen:
        result = expensive_function(item)
        if result < 5:
            yield result

撰写回答