Ответ 1
Вы пытаетесь использовать семантику let
-statement в пониманиях списков python, область видимости которой доступна как для ___ for..in
(map), так и для if ___
(filter) части понимания, и область действия которой зависит от ..for ___ in...
.
Ваше решение, измененное:
Ваш (как вы допускаете нечитаемое) решение [ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]
- самый простой способ написать оптимизацию.
Основная идея: поднять x в кортеж (x, f (x)).
Некоторые утверждают, что самый "питонический" способ сделать что-то будет оригинальным [(x,f(x)) for x in iterable if f(x)]
и принять неэффективность.
Однако вы можете разделить ((y,fy) for y in iterable)
на функцию, если вы планируете сделать это много. Это плохо, потому что, если вы когда-либо захотите получить доступ к большему количеству переменных, чем x,fx
(например, x,fx,ffx
), вам нужно будет переписать все ваши списки. Поэтому это не отличное решение, если вы точно не знаете, что вам нужно только x,fx
и планируете повторно использовать этот шаблон.
Выражение генератора:
Основная идея: используйте более сложную альтернативу выражениям генератора: один, где python позволит вам писать несколько строк.
Вы могли бы просто использовать выражение генератора, с которым python хорошо играет:
def xfx(iterable):
for x in iterable:
fx = f(x)
if fx:
yield (x,fx)
xfx(exampleIterable)
Вот как я лично это сделал.
запоминанием/кэширование:
Основная идея: вы также можете использовать (злоупотреблять?) побочные эффекты и сделать f
глобальный кеш memoization, поэтому вы не повторяете операции.
У этого может быть немного накладных расходов, и требуется политика в отношении того, насколько большой кеш должен быть, и когда он должен быть собран из мусора. Таким образом, это следует использовать только в том случае, если у вас будет другое использование для memoizing f, или если f очень дорого. Но это позволит вам писать...
[ (x,f(x)) for x in iterable if f(x) ]
... как вы изначально хотели, без повышения производительности, выполняя дорогостоящие операции в f
дважды, даже если вы технически называете это дважды. Вы можете добавить декоратор @memoized
в f
: пример (без максимального размера кеша). Это будет работать до тех пор, пока х хешируется (например, число, кортеж, frozenset и т.д.).
Значения "пустышки":
Основная идея: захватить fx = f (x) в замыкании и изменить поведение понимания списка.
filterTrue(
(lambda fx=f(x): (x,fx) if fx else None)() for x in iterable
)
где filterTrue (iterable) является фильтром (None, iterable). Вы должны были бы изменить это, если бы ваш тип списка (2-кортеж) действительно был None
.