У меня есть список, который я хочу отфильтровать по атрибуту элементов.
Что из следующего предпочтительнее (читаемость, производительность, другие причины)?
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 через замыкание, и это различие не будет применяться.
Другой вариант, который следует рассмотреть, - использовать генератор вместо понимания списка:
deffilterbyvalue(seq, value): for el in seq: if el.attribute==value: yield el
Затем в вашем основном коде (где действительно важна удобочитаемость) вы заменили и понимание списка, и фильтр, надеюсь, значимым именем функции.
Лично я считаю, что понимание списка легче читать. Из выражения более понятно, что происходит [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 inrange(1000) if prime(x)]
Я думаю, это выглядит немного лучше, чем использование filter. Но теперь рассмотрим
prime_cubes = [x*x*x for x inrange(1000) if prime(x*x*x)]
В этом случае мы хотим filter сопоставить с пост-вычисленным значением. Помимо проблемы двойного вычисления куба (представьте себе более дорогостоящее вычисление), существует проблема двойного написания выражения, нарушающего СУХУЮ эстетику. В этом случае я был бы склонен использовать
prime_cubes = filter(prime, [x*x*x for x inrange(1000)])
Ответ 4
Хотя filter может быть "более быстрым способом", "Pythonic way" заключался бы в том, чтобы не заботиться о таких вещах, если производительность не является абсолютно критичной (в этом случае вы бы не использовали Python!).