11

Сравнение: генераторы списков против lambda + filter

9

У меня есть список, который я хочу отфильтровать по атрибуту элементов. Какой из следующих вариантов является более предпочтительным с точки зрения читаемости, производительности или по другим причинам?

xs = [x for x in xs if x.attribute == value]

или

xs = filter(lambda x: x.attribute == value, xs)

Какой способ лучше использовать в разных ситуациях?

5 ответ(ов)

7

Это странно, как восприятие красоты варьируется у разных людей. Я нахожу списковые включения гораздо более понятными, чем filter + lambda, но выбирайте тот вариант, который вам удобнее.

Есть две вещи, которые могут замедлить использование filter.

Первая — это накладные расходы на вызовы функций. Как только вы используете функцию на Python (будь то созданная с помощью def или lambda), с большой долей вероятности filter будет медленнее, чем списковое включение. Хотя это почти наверняка не имеет значения, и вам не стоит беспокоиться о производительности, пока вы не замерили свое время выполнения и не обнаружили узкое место, тем не менее разница будет заметна.

Вторая проблема, которую следует учесть, — это то, что лямбда-функция вынуждена обращаться к переменной из области видимости (value). Это медленнее, чем доступ к локальной переменной, и в Python 2.x списковое включение обращается только к локальным переменным. Если вы используете Python 3.x, то списковое включение выполняется в отдельной функции, поэтому оно также будет обращаться к value через замыкание, и эта разница не будет актуальна.

Еще один вариант — рассмотреть использование генератора вместо спискового включения:

def filterbyvalue(seq, value):
   for el in seq:
       if el.attribute == value:
           yield el

Таким образом, в вашем основном коде (где действительно важна читаемость) вы заменили и списковое включение, и filter на, надеюсь, осмысленное имя функции.

0

Хотя filter может быть "быстрее", "питоническим" способом было бы не беспокоиться о таких вещах, если производительность не является абсолютно критичной (в таком случае вы бы, скорее всего, не использовали Python!).

0

Я бы хотел добавить, что в Python 3 функция filter() возвращает объект-итератор, поэтому, чтобы получить отфильтрованный список, вам нужно передать результат вызова filter() в функцию list(). В Python 2 код будет выглядеть так:

lst_a = range(25)  # произвольный список
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = filter(lambda num: num % 2 == 0, lst_a)

Списки lst_b и lst_c будут иметь одинаковые значения и будут созданы примерно за одно и то же время, так как filter() эквивалентен [x for x in y if z]. Однако в Python 3 этот же код оставит lst_c как объект filter, а не как отфильтрованный список. Чтобы получить такие же значения в Python 3, используйте:

lst_a = range(25)  # произвольный список
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = list(filter(lambda num: num % 2 == 0, lst_a))

Проблема заключается в том, что list() принимает в качестве аргумента итерируемый объект и создает на его основе новый список. В результате использование filter() в таком виде в Python 3 может занимать в два раза больше времени, чем метод [x for x in y if z], поскольку необходимо итерироваться как по выходным данным из filter(), так и по оригинальному списку.

0

Важное отличие заключается в том, что генератор списков (list comprehension) возвращает list, в то время как функция filter возвращает объект filter, который нельзя использовать так же, как список (например, на него нельзя вызвать len, что не работает с результатом filter).

В процессе самообучения я столкнулся с похожей проблемой.

С учетом вышесказанного, если есть способ получить результат в виде list из filter, как это делается в .NET с помощью lst.Where(i => i.something()).ToList(), мне было бы интересно это узнать.

ПРИМЕЧАНИЕ: это относится к Python 3, а не к 2 (см. обсуждение в комментариях).

0

Я считаю второй способ более читаемым. Он четко показывает намерение: отфильтровать коллекцию.

P.S.: Не используйте 'list' в качестве имени переменной.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь