Ответ 1
next(x for x in seq if predicate(x))
Он поднимает StopIteration
, если его нет.
next(ifilter(predicate, seq), None)
возвращает None
, если такого элемента нет.
Глупый вопрос: мне нужен идиоматический способ найти первый элемент в списке, который соответствует предикату.
Текущий код довольно уродливый:
[x for x in seq if predicate(x)][0]
Я подумал об изменении его:
from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()
Но должно быть что-то более элегантное... И было бы неплохо, если бы оно вернуло значение None
, а не увеличивало исключение, если совпадение не найдено.
Я знаю, что могу просто определить такую функцию, как:
def get_first(predicate, seq):
for i in seq:
if predicate(i): return i
return None
Но совершенно безвкусно начать заполнять код такими полезными функциями (и люди, вероятно, не заметят, что они уже есть, поэтому они, как правило, повторяются с течением времени), если есть встроенные вставки, которые уже предоставляют одинаковые.
next(x for x in seq if predicate(x))
Он поднимает StopIteration
, если его нет.
next(ifilter(predicate, seq), None)
возвращает None
, если такого элемента нет.
Вы можете использовать выражение генератора со значением по умолчанию, а затем next
it:
next((x for x in seq if predicate(x)), None)
Хотя для этого однострочного интерфейса вам нужно использовать Python >= 2.6.
В этой довольно популярной статье мы обсудим эту проблему: Чистая функция find-in-list Python?.
Я не думаю, что что-то не так с решениями, которые вы предложили в своем вопросе.
В моем собственном коде я бы реализовал его так:
(x for x in seq if predicate(x)).next()
Синтаксис с ()
создает генератор, который более эффективен, чем создание всего списка сразу с помощью []
.
J.F. Ответ Себастьяна наиболее изящный, но требует python 2.6, как указано fortran.
Для версии Python < 2.6, здесь лучшее, что я могу придумать:
from itertools import repeat,ifilter,chain
chain(ifilter(predicate,seq),repeat(None)).next()
Альтернативно, если вам нужен список позже (список обрабатывает StopIteration), или вам нужно больше, чем только первое, но все же не все, вы можете сделать это с помощью islice:
from itertools import islice,ifilter
list(islice(ifilter(predicate,seq),1))
UPDATE: Хотя я лично использую предопределенную функцию first(), которая улавливает StopIteration и возвращает None, здесь возможно улучшение по сравнению с приведенным выше примером: не используйте filter/ifilter:
from itertools import islice,chain
chain((x for x in seq if predicate(x)),repeat(None)).next()