这是一段代码片段,显示了我想要优化的代码:
result = [(item, foo(item))
for item in item_list
if cond1(item) and cond2(foo(item))]
在上面的代码段中,我两次致电foo(item)
。我想不出一种方法来迭代列表,只为条件和结果列表维护item
和foo(item)
。
也就是说,我希望保留item
和foo(item)
,而不必两次循环列表,而无需再拨打foo(item)
两次。
我知道我可以用第二个嵌套列表理解来做到这一点:
result = [(item, foo_item)
for item, foo_item in [(i, foo(i)) for i in item_list]
if cond1(item) and cond2(foo_item)]
但是这似乎在item_list
两次循环,我想避免。
因此,第一个示例每个列表项调用foo
两次。第二个示例循环遍历列表两次(或显示)。我想循环一次并为每个项目拨打foo
一次。
答案 0 :(得分:4)
它没有,但在这里:
result = [(item, foo_item)
for item, foo_item in ((i, foo(i)) for i in item_list)
if cond1(item) and cond2(foo_item)]
将内部列表理解转换为生成器表达式可确保我们不使用不必要的临时列表。
答案 1 :(得分:4)
就像我在这里一再被告知的那样, 在这种情况下最好的事情就是不要使用列表理解:
result = []
for item in item_list:
if cond1(item):
value = foo(item)
if cond2(value):
result.append((item, value))
但我是顽固的,所以让我们看看我能想出什么(并保持理解力) (哦,等等 - 我的代码都错了。仍然 - 解包和拥有中间变量是不重复调用的直接方式)
答案 2 :(得分:3)
这看起来如何?
result = [ (i, fi) for i in item_list if cond1(i)
for fi in (foo(i),) if cond2(fi) ]
答案 3 :(得分:3)
使用生成器表达式。
result = [(item, foo_item)
for item, foo_item in ((i, foo(i)) for i in item_list)
if cond1(item) and cond2(foo_item)]
解释器将只遍历每个元素一次,因为只有当外循环需要时,生成器表达式才会计算(i, foo(i))
。
假设foo
价格昂贵并且没有副作用,我甚至会尝试这样做:
result = [(item, foo_item)
for item, foo_item in ((i, foo(i)) for i in item_list if cond1(i))
if cond2(foo_item)]
这样就不会为没有通过第一个条件的元素调用foo。实际上,在功能上写这个对我来说看起来更好:
from itertools import imap, ifilter
result = filter((lambda i,f:cond2(f)),
imap((lambda i:(i, foo(i))),
ifilter(cond1, item_list)))
......但我可能是主观的。
答案 4 :(得分:1)
这是我们拥有发电机的众多原因之一:
def generator( items ):
for item in items:
if cond1(item):
food = foo(item)
if food:
yield item, food
result = list(generator(item_list))
LC只有在看起来很好的时候才有用 - 如果你必须将它们分散在3行以上才能使它们可读,这是一个坏主意。