Я хотел бы получить первый объект из списка, соответствующего условию. Важно, чтобы получающийся метод не обработал весь список, который мог быть довольно большим. Например, следующая функция соответствует:
def first(the_iterable, condition = lambda x: True):
for i in the_iterable:
if condition(i):
return i
Эта функция могла использоваться что-то вроде этого:
>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
Однако я не могу думать о пользе, встроенной / острота, чтобы позволить мне сделать это. Я особенно не хочу копировать эту функцию вокруг, если я не имею к. Существует ли встроенный способ получить первый объект, соответствующий условию?
В Python 2.6 или выше:
Если вы хотите, чтобы StopIteration
вызывался, если соответствующий элемент не найден:
next (x вместо x в the_iterable, если x> 3)
Если вы хотите, чтобы вместо этого возвращалось default_value
(например, None
):
next ((x for x in the_iterable, если x> 3), default_value)
Обратите внимание, что в этом случае вам понадобится дополнительная пара круглых скобок вокруг выражения генератора - они необходимы всегда, когда выражение генератора - не единственный аргумент.
Я вижу, что большинство ответов решительно игнорируют встроенный next
, и поэтому я предполагаю, что по какой-то загадочной причине они на 100% сосредоточены на версии 2.5 и старше - без упоминания проблемы версии Python (но тогда я не вижу упоминания в ответах, что действительно упоминает встроенный следующий
], поэтому я счел необходимым дать ответ сам - по крайней мере, проблема с "правильной версией" таким образом фиксируется ;-).
В версии 2.5 метод итераторов .next ()
немедленно вызывает StopIteration
, если итератор немедленно завершает свою работу, т. Е. Для вашего варианта использования, если ни один элемент в итераторе не удовлетворяет условие. Если вам все равно (например, вы знаете, что там должен быть хотя бы один удовлетворительный элемент), просто используйте .next ()
(лучше всего на genexp, строка для next
встроен в Python 2.6 и выше).
Если вы заботитесь, то лучше всего будет заключить что-то в функцию, как вы впервые указали в Q, и хотя предложенная вами реализация функции вполне хороша, вы также можете использовать itertools
, for ...: break
цикл, или genexp, или try / except StopIteration
в качестве тела функции, как предлагалось в различных ответах. Ни в одной из этих альтернатив нет особой добавленной стоимости, поэтому я бы выбрал совершенно простую версию, которую вы впервые предложили.
Oneliner:
thefirst = [i for i in range(10) if i > 3][0]
Если вы не уверены, что какой-либо элемент будет допустимым в соответствии с критериями, вы должны заключить его в try / except
, поскольку это [0]
может вызвать IndexError
.
Аналогично использованию ifilter
, вы можете использовать выражение-генератор:
>>> (x for x in xrange(10) if x > 5).next()
6
В любом случае, вы, вероятно, захотите поймать StopIteration
на случай, если ни один элемент не удовлетворяет вашему условию.
Технически говоря, я полагаю, вы можете сделать что-то вроде этого:
>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
...
>>> foo
6
Это позволит избежать необходимости делать блок try/except
. Но это кажется несколько неясным и оскорбительным для синтаксиса.
Модуль itertools
содержит функцию фильтрации для итераторов. . Первый элемент отфильтрованного итератора можно получить, вызвав для него next ()
:
from itertools import ifilter
print ifilter((lambda i: i > 3), range(10)).next()
Для более старых версий Python, в которых не существует следующей встроенной программы. :
(x for x in range(10) if x > 3).next()
Я бы написал так
next(x for x in xrange(10) if x > 3)