Я обнаружил, что у меня есть базовая потребность в фильтрации: у меня есть список, и я должен отфильтровать его по атрибуту элементов.
Мой код выглядел так:
my_list = [x for x in my_list if x.attribute == value]
Но тогда я подумал: не лучше ли написать это так?
my_list = filter(lambda x: x.attribute == value, my_list)
Это более читабельно, и если нужно для производительности, лямбда может быть извлечена, чтобы получить что-то.
Вопрос: есть ли какие-то предостережения при использовании второго способа? Есть разница в производительности? Я полностью пропускаю Pythonic Way ™ и должен сделать это еще одним способом (например, использовать itemgetter вместо лямбды)?
filter
было более читабельным. Если у вас есть простое выражение, которое можно использовать как есть в listcomp, но его нужно заключить в лямбду (или аналогично сконструировать изpartial
илиoperator
функций и т. Д.) Для передачиfilter
, то тогда listcomps победит.filter
является объектом генератора фильтров, а не списком.Ответы:
Странно, насколько различна красота у разных людей. Я считаю, что понимание списка намного яснее, чем
filter
+lambda
, но используйте то, что вам легче.Есть две вещи, которые могут замедлить ваше использование
filter
.Первый - это накладные расходы при вызове функции: как только вы используете функцию Python (созданную с помощью
def
илиlambda
), вполне вероятно, что фильтр будет медленнее, чем понимание списка. Это почти наверняка недостаточно для того, чтобы иметь значение, и вы не должны много думать о производительности, пока не рассчитаете свой код и не обнаружите, что это узкое место, но разница будет.Другие накладные расходы, которые могут возникнуть, это то, что лямбда вынуждена обращаться к переменной области (
value
). Это медленнее, чем доступ к локальной переменной, а в Python 2.x понимание списка доступно только для локальных переменных. Если вы используете Python 3.x, постижение списка выполняется в отдельной функции, поэтому он также будет доступенvalue
через замыкание, и это различие не будет применяться.Другой вариант, который следует рассмотреть, - использовать генератор вместо понимания списка:
Затем в вашем основном коде (где читаемость действительно имеет значение) вы заменили как понимание списка, так и фильтр на многообещающее имя функции.
источник
[]
на()
. Кроме того, я согласен, что список комп более красив.filter
что быстрее использовать функцию обратного вызова Python.Это несколько религиозная проблема в Python. Несмотря на то, что Гвидо рассматривал возможность удаления
map
,filter
иreduce
из Python 3 было достаточно негативной реакции, которая в итогеreduce
была перемещена только из встроенных модулей в functools.reduce .Лично я нахожу список понимания легче читать. Более явно то, что происходит из выражения,
[i for i in list if i.attribute == value]
поскольку все поведение находится на поверхности, а не внутри функции фильтра.Я бы не стал сильно беспокоиться о разнице в производительности между двумя подходами, поскольку она незначительна. Я бы действительно оптимизировал это, только если это оказалось узким местом в вашем приложении, что маловероятно.
Кроме того, так как BDFL хотел
filter
уйти от языка, то, конечно, это автоматически делает списки более Pythonic ;-)источник
Поскольку любая разница в скорости обязательно будет крошечной, использование фильтров или списочных представлений зависит от вкуса. В целом я склонен использовать понимание (что, похоже, согласуется с большинством других ответов здесь), но есть один случай, когда я предпочитаю
filter
.Очень частый вариант использования - извлечение значений некоторого итерируемого X с использованием предиката P (x):
но иногда вы хотите сначала применить некоторую функцию к значениям:
В качестве конкретного примера рассмотрим
Я думаю, что это выглядит немного лучше, чем при использовании
filter
. Но теперь посмотримВ этом случае мы хотим
filter
против пост-вычисленного значения. Помимо проблемы вычисления куба дважды (представьте себе более дорогой расчет), существует проблема написания выражения дважды, нарушая эстетику DRY . В этом случае я был бы склонен использоватьисточник
[prime(i) for i in [x**3 for x in range(1000)]]
x*x*x
не может быть простым числом, как это имеет место,x^2
иx
как фактор, пример не имеет смысла математически, но, возможно, он все еще полезен. (Может быть, мы могли бы найти что-то лучше?)prime_cubes = filter(prime, (x*x*x for x in range(1000)))
prime_cubes = [1]
сэкономить память и циклы процессора ;-)[]
Хотя это
filter
может быть «более быстрый путь», «Pythonic way» не заботится о таких вещах, если производительность не является абсолютно критической (в этом случае вы не будете использовать Python!).источник
Я подумал, что просто добавлю, что в python 3 filter () на самом деле является объектом итератора, поэтому вам нужно передать вызов метода filter в list (), чтобы построить отфильтрованный список. Итак, в Python 2:
списки b и c имеют одинаковые значения и были заполнены примерно в то же время, что filter () был эквивалентен [x для x в y, если z]. Однако в 3 этот же код оставил бы список c, содержащий объект фильтра, а не отфильтрованный список. Чтобы получить те же значения в 3:
Проблема в том, что list () принимает в качестве аргумента итерацию и создает новый список из этого аргумента. В результате использование фильтра в Python 3 таким способом занимает вдвое больше времени, чем метод [x для x в y, если z], потому что вам приходится перебирать выходные данные filter (), а также исходный список.
источник
Важным отличием является то, что понимание списка вернет некоторое
list
время, в то время как фильтр возвращает afilter
, которым вы не можете манипулировать как alist
(то есть: вызовитеlen
его, который не работает с возвращениемfilter
).Мое самообучение привело меня к некоторой аналогичной проблеме.
Это , как говорится, если есть способ , чтобы полученный
list
от Аfilter
, немного , как вы могли бы сделать в .NET , когда вы делаетеlst.Where(i => i.something()).ToList()
, мне интересно знать.РЕДАКТИРОВАТЬ: Это относится к Python 3, а не 2 (см. Обсуждение в комментариях).
источник
a = [1, 2, 3, 4, 5, 6, 7, 8]
f = filter(lambda x: x % 2 == 0, a)
lc = [i for i in a if i % 2 == 0]
>>> type(f)
<class 'filter'>
>>> type(lc)
<class 'list'>
list()
по результатуlist(filter(my_func, my_iterable))
. И, конечно , вы могли бы заменитьlist
сset
, илиtuple
, или что - нибудь еще , что требуется итератор. Но для любого, кроме функциональных программистов, дело даже в том, чтобы использовать понимание списка, а неfilter
явное преобразование вlist
.Я считаю второй способ более читабельным. Это точно говорит вам, что вы хотите: отфильтруйте список.
PS: не используйте «список» в качестве имени переменной
источник
как правило
filter
, немного быстрее, если использовать встроенную функцию.Я ожидаю, что понимание списка будет немного быстрее в вашем случае
источник
Фильтр это просто так. Он отфильтровывает элементы списка. Вы можете видеть, что определение упоминает то же самое (в официальной ссылке на документы, которую я упоминал ранее). Принимая во внимание, что понимание списка - это то, что создает новый список после воздействия на что-то из предыдущего списка. (И фильтрация, и понимание списка создают новый список и не выполняют операции вместо старого списка. Новый список здесь представляет собой что-то вроде списка с скажем, совершенно новый тип данных. Как преобразование целых чисел в строку и т. д.)
В вашем примере, лучше использовать фильтр, чем понимание списка, согласно определению. Однако, если вы хотите, скажем, other_attribute из элементов списка, в вашем примере вы должны получить новый список, тогда вы можете использовать понимание списка.
Вот как я на самом деле помню о фильтрах и списках. Удалите несколько вещей из списка и оставьте остальные элементы нетронутыми, используйте фильтр. Используйте некоторую логику самостоятельно для элементов и создайте разбавленный список, подходящий для какой-то цели, используйте понимание списка.
источник
Вот небольшой фрагмент, который я использую, когда мне нужно отфильтровать что-то после понимания списка. Просто комбинация фильтра, лямбды и списков (иначе называемых верностью кошки и чистотой собаки).
В этом случае я читаю файл, убираю пустые строки, закомментированные строки и что-нибудь после комментария к строке:
источник
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))
В дополнение к принятому ответу, есть угловой случай, когда вы должны использовать фильтр вместо понимания списка. Если список не подлежит изменению, вы не можете напрямую обработать его с помощью понимания списка. Пример из реальной жизни, если вы используете
pyodbc
для чтения результатов из базы данных. ВfetchAll()
результатахcursor
является unhashable списка. В этой ситуации, чтобы напрямую манипулировать возвращаемыми результатами, следует использовать фильтр:Если вы используете здесь понимание списка, вы получите ошибку:
источник
>>> hash(list()) # TypeError: unhashable type: 'list'
во-вторых, это прекрасно работает:processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]
Мне потребовалось некоторое время, чтобы ознакомиться с
higher order functions
filter
иmap
. Так что я привык к ним, и мне действительно понравилось,filter
так как было ясно, что оно фильтрует, сохраняя все правдивое, и я чувствовал себя классно, что я знал некоторыеfunctional programming
термины.Затем я прочитал этот отрывок (Свободная Книга Питона):
И теперь я думаю, зачем беспокоиться о концепции
filter
/,map
если вы можете достичь ее с помощью уже широко распространенных идиом, таких как списки. Кроме тогоmaps
иfilters
являются своего рода функциями. В этом случае я предпочитаю использоватьAnonymous functions
лямбды.Наконец, просто для того, чтобы проверить его, я рассчитал оба метода (
map
иlistComp
), и я не увидел какой-либо существенной разницы в скорости, которая бы оправдывала споры об этом.источник
Любопытно, что на Python 3 фильтр работает быстрее, чем списки.
Я всегда думал, что понимание списка будет более производительным. Примерно так: [имя для name в brand_names_db, если name не None] Сгенерированный байт-код немного лучше.
Но они на самом деле медленнее:
источник
if not None
в списке понимании вы являетесь определением функции лямбды (обратите внимание наMAKE_FUNCTION
заявление). Во-вторых, результаты отличаются, так как версия понимания списка будет удалять толькоNone
значение, тогда как версия фильтра удалит все «ложные» значения. Сказав это, вся цель микробенчмаркинга бесполезна. Это миллион итераций, раз 1к пунктов! Разница незначительная .Мой дубль
источник
i
никогда не говорилось, что нетdict
, и в этом нет необходимостиlimit
. Кроме этого, как это отличается от того, что предложила ФП, и как оно отвечает на вопрос?