您可以使用嵌套生成器表达式只调用一次函数:
[(x, fx) for (x, fx) in ((x, f(x)) for x in list_x) if fx == cond(x)]
生成器表达式以同步方式迭代以生成(x, fx) 元组用于列表理解。
如果您觉得这对读者来说更容易,您可以先将生成器表达式拆分为一个单独的名称:
mapped_x = ((x, f(x)) for x in list_x)
filtered_x = [(x, fx) for (x, fx) in mapped_x if fx == cond(x)]
重申这一点:生成器表达式是惰性执行的;对于for ... in mapped_x 循环中的每一步,表达式中的for 循环将逐步推进。
演示:
>>> list_x = range(5)
>>> f = lambda x: print('f({!r})'.format(x)) or (x ** 2 - 1)
>>> cond = lambda x: print('cond({!r})'.format(x)) or x % 2 == 0
>>> mapped_x = ((x, f(x)) for x in list_x)
>>> [(x, fx) for (x, fx) in mapped_x if fx == cond(x)]
f(0)
cond(0)
f(1)
cond(1)
f(2)
cond(2)
f(3)
cond(3)
f(4)
cond(4)
[(1, 0)]
注意f(x) 是如何只调用一次的,然后立即检查条件。
这有多高效取决于f(x) 调用的成本;生成器表达式作为单独的函数框架执行,解释器将在两个框架之间切换(列表推导的循环也是框架对象)。
如果 f(x) 是一个 Python 函数,那么您已经赢了,因为您现在将创建的函数框架对象的数量减半(每个 f(x) 调用也会创建一个框架对象,而创建这些框架对象的成本相对较高)。对于 C 函数,您应该使用 timeit module 创建一些试运行,以查看对于您预期的列表大小来说哪个更快。