在列表理解中的if语句中捕获方法

时间:2017-04-03 07:26:19

标签: python list if-statement list-comprehension

我有以下用例:

[(x, f(x)) for x in list_x if f(x) == cond(x)]

上面列表理解,我认为两次进行f(x)调用?如何避免这种情况并捕获f(x)的值,以便只调用f(x)一次。

我想,简单的解决方法是将上面的列表理解转换为for循环,但我很好奇是否可以使用列表理解来有效地完成。

3 个答案:

答案 0 :(得分:6)

您可以使用嵌套的生成器表达式仅调用一次函数:

[(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循环中的每个步骤,表达式中的for ... in mapped_x循环都是逐步进行的。

演示:

>>> 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创建一些试运行,以查看预期列表大小的速度。

答案 1 :(得分:1)

如果您不想两次拨打f,可以先将f应用于列表

[a for a in map(lambda x: (x, f(x)), list_x) if a[1] == cond(x)]

答案 2 :(得分:1)

过滤器将是一种干净的方式。它将评估传递的函数,如果它返回true,它将保留元素,否则为no。

print (list(filter(lambda x: x[1] == cond(x[0]), [(x, f(x)) for x in list_x])))