Get the first item from an iterable that matches a condition
Получение первого элемента из итерируемого объекта, соответствующего условию
Я хотел бы получить первый элемент из списка, соответствующего условию. Важно, чтобы результирующий метод не обрабатывал весь список, который может быть довольно большим. Например, следующая функция является адекватной:
deffirst(the_iterable, condition = lambda x: True): for i in the_iterable: if condition(i): return i
Однако я не могу придумать хорошую встроенную / однострочную программу, которая позволила бы мне это сделать. Я не особенно хочу копировать эту функцию, если мне не нужно. Есть ли встроенный способ получить первый элемент, соответствующий условию?
Переведено автоматически
Ответ 1
Python 2.6+ и Python 3:
Если вы хотите, чтобы StopIteration был вызван, если соответствующий элемент не найден:
next(x for x in the_iterable if x > 3)
Если вы хотите, чтобы вместо него был возвращен default_value (например, None):
next((x for x in the_iterable if x > 3), default_value)
Обратите внимание, что в этом случае вам понадобится дополнительная пара круглых скобок вокруг выражения генератора - они необходимы всякий раз, когда выражение генератора не является единственным аргументом.
Я вижу, что большинство ответов решительно игнорируют next встроенное, и поэтому я предполагаю, что по какой-то загадочной причине они на 100% ориентированы на версии 2.5 и старше - без упоминания проблемы с версией Python (но тогда я не вижу этого упоминания в ответах, в которых действительно упоминается next встроенное, вот почему я счел необходимым предоставить ответ самостоятельно - по крайней мере, проблема с "правильной версией" регистрируется таким образом;-).
Python <= 2.5
.next() Метод iterators немедленно запускает работу, StopIteration если итератор немедленно завершается - т. Е. Для вашего варианта использования, если ни один элемент в итерируемом объекте не удовлетворяет условию. Если вам все равно (т. Е. вы знаете, что должен быть хотя бы один удовлетворяющий элемент), тогда просто используйте .next() (лучше всего в genexp, строка для next встроенного в Python 2.6 и выше).
Если вам не все равно, лучше всего обернуть все в функцию, как вы сначала указали в своем Q, и хотя предложенная вами реализация функции просто великолепна, вы могли бы в качестве альтернативы использовать itertools, for...: break цикл, или genexp, или try/except StopIteration в качестве тела функции, как предлагалось в различных ответах. Ни в одной из этих альтернатив нет особой добавленной стоимости, поэтому я бы выбрал предельно простую версию, которую вы предложили первой.
Ответ 2
Чертовы исключения!
Мне нравится ответ Алекса Мартелли. Однако, поскольку next() возникает StopIteration исключение, когда элементов нет, я бы использовал следующий фрагмент, чтобы избежать исключения:
a = [] item = next((x for x in a), None)
Например,
a = [] item = next(x for x in a)
Вызовет StopIteration исключение;
Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Ответ 3
Как повторно используемая, документированная и протестированная функция
deffirst(iterable, condition = lambda x: True): """ Returns the first item in the `iterable` that satisfies the `condition`.
If the condition is not given, returns the first item of the iterable.
Raises `StopIteration` if no item satysfing the condition is found.
@zorf предложил версию этой функции, в которой вы можете получить предопределенное возвращаемое значение, если итерация пуста или в ней нет элементов, соответствующих условию:
deffirst(iterable, default = None, condition = lambda x: True): """ Returns the first item in the `iterable` that satisfies the `condition`.
If the condition is not given, returns the first item of the iterable.
If the `default` argument is given and the iterable is empty, or if it has no items matching the condition, the `default` argument is returned if it matches the condition.
The `default` argument being None is the same as it not being given.
Raises `StopIteration` if no item satisfying the condition is found and default is not given or doesn't satisfy the condition.
try: returnnext(x for x in iterable if condition(x)) except StopIteration: if default isnotNoneand condition(default): return default else: raise
Ответ 4
Наиболее эффективным способом в Python 3 является один из следующих (используя аналогичный пример):
В стиле "понимание":
next(i for i inrange(100000000) if i == 1000)
ПРЕДУПРЕЖДЕНИЕ: выражение работает также с Python 2, но в примере используется range которое возвращает итерируемый объект в Python 3 вместо списка, подобного Python 2 (если вы хотите создать итерируемый объект в Python 2, используйте xrange вместо этого).
Обратите внимание, что выражение избегает построения списка в выражении понимания next([i for ...]), что привело бы к созданию списка со всеми элементами перед фильтрацией элементов и привело бы к обработке всех параметров, вместо того, чтобы останавливать итерацию один раз i == 1000.
С "функциональным" стилем:
next(filter(lambda i: i == 1000, range(100000000)))
ПРЕДУПРЕЖДЕНИЕ: это не работает в Python 2, даже при замене range на xrange из-за того, что filter создает список вместо итератора (неэффективно), а next функция работает только с итераторами.
Значение по умолчанию
Как упоминалось в других ответах, вы должны добавить дополнительный параметр в функцию next если вы хотите избежать исключения, возникающего при невыполнении условия.
"функциональный" стиль:
next(filter(lambda i: i == 1000, range(100000000)), False)
стиль "понимания":
С помощью этого стиля вам нужно окружить выражение понимания () чтобы избежать SyntaxError: Generator expression must be parenthesized if not sole argument:
next((i for i inrange(100000000) if i == 1000), False)