Вопрос-Ответ

List comprehension vs. lambda + filter

Понимание списка по сравнению с lambda + filter

У меня есть список, который я хочу отфильтровать по атрибуту элементов.

Что из следующего предпочтительнее (читаемость, производительность, другие причины)?

xs = [x for x in xs if x.attribute == value]
xs = filter(lambda x: x.attribute == value, xs)
Переведено автоматически
Ответ 1

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

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

Ответ 2

Это несколько религиозная проблема в Python. Несмотря на то, что Гвидо рассматривал возможность удаления map, filter и reduce из Python 3reduce, было достаточно негативной реакции, которая в итоге только была перенесена из встроенных модулей в functools.reduce.reduce.

Лично я считаю, что понимание списка легче читать. Из выражения более понятно, что происходит [i for i in list if i.attribute == value] поскольку все поведение находится на поверхности, а не внутри функции filter .

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

Кроме того, поскольку BDFL хотелfilter, чтобы он исчез из языка, это, конечно, автоматически делает понимание списка более питонистским ;-)

Ответ 3

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

Очень частый вариант использования - извлекать значения некоторого повторяющегося X, подчиняющегося предикату P(x):

[x for x in X if P(x)]

но иногда вы хотите сначала применить какую-то функцию к значениям:

[f(x) for x in X if P(f(x))]



В качестве конкретного примера рассмотрим

primes_cubed = [x*x*x for x in range(1000) if prime(x)]

Я думаю, это выглядит немного лучше, чем использование filter. Но теперь рассмотрим

prime_cubes = [x*x*x for x in range(1000) if prime(x*x*x)]

В этом случае мы хотим filter сопоставить с пост-вычисленным значением. Помимо проблемы двойного вычисления куба (представьте себе более дорогостоящее вычисление), существует проблема двойного написания выражения, нарушающего СУХУЮ эстетику. В этом случае я был бы склонен использовать

prime_cubes = filter(prime, [x*x*x for x in range(1000)])
Ответ 4

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

python list