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

StackOverflow https://stackoverflow.com/questions/130262

  •  02-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

Это занимает больше места по горизонтали, но гораздо легче увидеть, что это делает с первого взгляда, и в конечном итоге вы не повторяетесь.

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

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

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

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

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
  1. Это делает абсолютно понятным, что происходит на каждом этапе конвейера.
  2. Явное преобладает над неявным
  3. Использует генераторы везде до последнего шага, поэтому никаких больших промежуточных списков

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

Ты всегда мог бы запоминать тот самый expensive() функция, так что вызов ее во второй раз - это просто поиск вычисленного значения x.

Вот лишь одна из многих реализаций memoize в качестве декоратора.

Вы могли бы запомнить дорогой (x) (и если вы часто вызываете дорогой (x), вам, вероятно, следует запомнить его любым способом.На этой странице представлена реализация memoize для python:

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

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

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

Я буду отдавать предпочтение:

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

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

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

Существует простое старое использование a for цикл для добавления в список тоже есть:

result = []
for x in mylist:
    expense = expensive(x)
    if expense:
        result.append(expense)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top