Использовать выражения генератора.
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)))
... но я могу быть субъективным.