Подтвердить что ты не робот

Как эффективно фильтровать вычисленные значения в понимании списка Python?

Синтаксис понимания списка Python позволяет легко фильтровать значения в понимании. Например:

result = [x**2 for x in mylist if type(x) is int]

Вернет список квадратов целых чисел в mylist. Однако, что, если тест включает некоторые (дорогостоящие) вычисления и вы хотите отфильтровать результат? Один из вариантов:

result = [expensive(x) for x in mylist if expensive(x)]

Это приведет к списку не "ложных" дорогих (x) значений, однако дорогостоящий() вызывается дважды для каждого x. Есть ли синтаксис понимания, который позволяет вам выполнять этот тест, только когда он дорого стоит один раз за x?

4b9b3361

Ответ 1

Если вычисления уже хорошо связаны с функциями, как насчет использования filter и map?

result = filter (None, map (expensive, mylist))

Вы можете использовать itertools.imap, если список очень большой.

Ответ 2

Придумал мой собственный ответ после минуты размышлений. Это можно сделать с помощью вложенных понятий:

result = [y for y in (expensive(x) for x in mylist) if y]

Я думаю, что это работает, хотя я считаю, что вложенные способы понимания являются лишь незначительно читаемыми

Ответ 3

Самый очевидный (и я бы сказал, что наиболее читаемый) ответ заключается в том, чтобы не использовать выражение для составления списка или генераторное выражение, а скорее настоящий генератор:

def gen_expensive(mylist):
    for item in mylist:
        result = expensive(item)
        if result:
            yield result

Требуется больше горизонтального пространства, но гораздо проще увидеть, что он делает с первого взгляда, и вы не повторяетесь.

Ответ 4

result = [x for x in map(expensive,mylist) if x]

map() вернет список значений каждого объекта в mylist, переданном дорогостоящему(). Затем вы можете перечислить-понять это и отказаться от ненужных значений.

Это похоже на вложенное понимание, но должно быть быстрее (поскольку интерпретатор python может легко оптимизировать его).

Ответ 5

Это именно то, что генераторы подходят для обработки:

result = (expensive(x) for x in mylist)
result = (do_something(x) for x in result if some_condition(x))
...
result = [x for x in result if x]  # finally, a list
  • Это позволяет полностью понять, что происходит на каждом этапе конвейера.
  • Явная неявная
  • Использует генераторы всюду до последнего шага, поэтому нет больших промежуточных списков

cf: "Трюки генератора для системных программистов" Дэвида Бизли

Ответ 7

Вы можете memoize дорогостоящий (x) (и если вы часто звоните в дорогостоящий (x), вы, вероятно, должны его memoize каким-либо образом. Эта страница дает реализацию memoize для python:

http://code.activestate.com/recipes/52201/

Это имеет дополнительное преимущество, при котором дорогостоящий (x) может быть запущен меньше N раз, поскольку любые повторяющиеся записи будут использовать памятку из предыдущего выполнения.

Обратите внимание, что это предполагает, что дорогостоящий (x) является истинной функцией и не зависит от внешнего состояния, которое может измениться. Если дорогой (x) действительно зависит от внешнего состояния, и вы можете обнаружить, когда это состояние изменится, или вы знаете, что оно не изменится во время понимания списка, вы можете reset сохранить заметки перед пониманием.

Ответ 8

У меня будет предпочтение:

itertools.ifilter(bool, (expensive(x) for x in mylist))

Это имеет то преимущество, что:

  • избегать None как функции (будет устранено в Python 3): http://bugs.python.org/issue2186
  • используйте только итераторы.

Ответ 9

Простой старое использование цикла for для добавления в список тоже:

result = []
for x in mylist:
    expense = expensive(x)
    if expense:
        result.append(expense)