生成器表达式包括条件测试而不调用函数两次?

Generator expression including conditional test without calling a function twice?

假设我有一个执行一些繁重计算的函数。

def f(x):
    ... 
    return result

然后我有一个列表,其中包含要传递给 f() 的值:

my_list = [2, 98, 4, 34, 23, 11]

我想在此列表中找到第一个元素 x,它验证 f(x) 上的条件(比方说 f(x) != 0),并获得此计算结果。

基本上,我会像这样写一个 for 循环:

def first_match(my_list):
    for x in my_list:
        r = f(x)
        if r != 0:
            return r

我想知道是否有办法使用生成器表达式获得相同的结果?

到目前为止我的想法是这样的:

r = next(f(x) if f(x) != 0 for x in my_list)

问题是这会调用 f() 两次。

一般来说,生成器表达式可以构造成生成器函数。例如,这个 f(x) 具有惰性求值,正如您对生成器所期望的那样:

def f(x):
    for i in x:
        yield i

是的,这是一个空委托,但您的示例可以用于您的功能(我不明白其意图所以没有猜测)。

您可以使用嵌套的生成器表达式来避免双重函数调用:

next(y for y in (f(x) for x in my_list) if y != 0)

也许使用itertools.ifilter

from itertools import ifilter
next(ifilter(lambda x: f(x) != 0, my_list))

有一种方法可以使用生成器表达式,如 by Blckknght,但您问题中的 first_match() 函数也没有任何问题。并不是所有的东西都必须是单行的:)。

还有另一种方法:

try:
    from itertools import ifilter as filter  # Python 2
except ImportError:
    pass  # Python 3

predicate = lambda x: x != 0
r = next(filter(predicate, (f(x) for x in my_list)), None)

next() 的第二个参数(上面代码中的 None )是 return 如果您作为第一个参数传入的迭代器中没有任何内容。如果您不指定它并且迭代器已用完,则会引发 StopIteration 异常。您可以将其更改为任何您想要的。

理解可能是嵌套的映射函数,带有输入的选项过滤。在这种情况下,您想要过滤输出,所以这样做。令 y = f(x) = xx 并令 c(y) = (y > 100)。然后 (y for y in (xx for x in my_list) if y > 100) 为您提供过滤后的输出。正如您所注意到的,可以使用 next.

停止对第一个真实元素的过滤器(第一个真实搜索)。
my_list = [2, 98, 4, 34, 23, 11]
ge = (y for y in (x*x for x in my_list) if y > 100)

print(next(ge))
# 9604