Получить первый элемент из итерабельного, который соответствует условию

Я хотел бы получить первый элемент из списка, соответствующего условию. Важно, чтобы полученный метод не обрабатывал весь список, который может быть довольно большим. Например, достаточна следующая функция:

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 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 встроенном ящике, поэтому я счел нужным дать ответ сам – по крайней мере, проблема «правильной версии» становится такой же:;).

    В 2.5, метод iterators .next() немедленно вызывает StopIteration если iterator немедленно заканчивается, т. StopIteration Для вашего прецедента, если ни один элемент в iterable не удовлетворяет условию. Если вам все равно (т. .next() Вы знаете, что должен быть хотя бы один удовлетворительный элемент), тогда просто используйте .next() (лучше всего на linux, строка для next встроенного в Python 2.6 и выше).

    Если вы позаботитесь, обертывание вещей в функции, как вы впервые указали в вашем Q, кажется лучшим, и, хотя реализация функции, которую вы предложили, просто прекрасна, вы также можете использовать itertools , for...: break loop или genexp , или try/except StopIteration как тело функции, так как предлагаются различные ответы. В любой из этих альтернатив нет большой добавленной стоимости, поэтому я бы выбрал совершенно простую версию, которую вы впервые предложили.

    В качестве многоразовой, документированной и проверенной функции

     def first(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. >>> first( (1,2,3), condition=lambda x: x % 2 == 0) 2 >>> first(range(3, 100)) 3 >>> first( () ) Traceback (most recent call last): ... StopIteration """ return next(x for x in iterable if condition(x)) 

    Подобно использованию 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 . Но это кажется неясным и оскорбительным для синтаксиса.

    Проклятые исключения!

    Мне нравится этот ответ . Однако, поскольку next() StopIteration исключение StopIteration когда нет элементов, я бы использовал следующий fragment, чтобы избежать исключения:

     a = [] item = next((x for x in a), None) 

    Например,

     a = [] item = next(x for x in a) 

    StopIteration исключение StopIteration ;

     Traceback (most recent call last): File "", line 1, in  StopIteration 

    Для более старых версий Python, где следующий встроенный модуль не существует:

     (x for x in range(10) if x > 3).next() 

    Я бы написал это

     next(x for x in xrange(10) if x > 3) 

    Модуль itertools содержит функцию фильтра для iteratorов. Первый элемент отфильтрованного iteratorа может быть получен путем вызова next() на нем:

     from itertools import ifilter print ifilter((lambda i: i > 3), range(10)).next() 

    Используя

     (index for index, value in enumerate(the_iterable) if condition(value)) 

    можно проверить условие значения первого элемента в переменной_имя и получить его индекс без необходимости оценивать все элементы в файле_имя .

    Полное выражение для использования

     first_index = next(index for index, value in enumerate(the_iterable) if condition(value)) 

    Здесь first_index принимает значение первого значения, указанного в выражении, описанном выше.

    Поскольку вы запросили встроенный StopIteration , это позволит избежать исключения исключения StopIteration , хотя для этого требуется, чтобы ваш итеративный файл был небольшим, поэтому вы можете перечислить его в список, поскольку это единственная конструкция, которую я знаю, проглотит StopIteration и позволит вам заглянуть в значения:

     (lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION)) 

    (Если элемент не совпадает, вы получите None а не исключение StopIteration .)

    На этот вопрос уже есть отличные ответы. Я только добавляю свои два цента, потому что я приземлился здесь, пытаясь найти решение моей собственной проблемы, которая очень похожа на OP.

    Если вы хотите найти ИНДЕКС первого элемента, соответствующего критериям с использованием генераторов, вы можете просто сделать:

     next(index for index, value in enumerate(iterable) if condition) 

    Наиболее эффективный способ в Python 3 – один из следующих (с использованием аналогичного примера):

    С «пониманием» стиля:

     next(i for i in range(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 создает список вместо iteratorа (неэффективен), а next функция работает только с iteratorами.

    Значение по умолчанию

    Как упоминалось в других ответах, вы должны добавить дополнительный параметр к функции next если вы хотите избежать исключения, возникающего, когда условие не выполняется.

    «функциональный» стиль:

     next(filter(lambda i: i == 1000, range(100000000)), False) 

    стиль «понимания» :

    С помощью этого стиля вам необходимо окружить выражение понимания с помощью () чтобы избежать SyntaxError: Generator expression must be parenthesized if not sole argument :

     next((i for i in range(100000000) if i == 1000), False) 

    В Python 3:

     a = (None, False, 0, 1) assert next(filter(None, a)) == 1 

    В Python 2.6:

     a = (None, False, 0, 1) assert next(iter(filter(None, a))) == 1 

    EDIT: Я думал, что это очевидно, но, по-видимому, нет: вместо None вы можете передать функцию (или lambda ) с проверкой на условие:

     a = [2,3,4,5,6,7,8] assert next(filter(lambda x: x%2, a)) == 3 

    Один лайнер:

     thefirst = [i for i in range(10) if i > 3][0] 

    Если вы не уверены, что какой-либо элемент будет действителен в соответствии с критериями, вы должны заключить это с помощью try/except так как [0] может вызвать IndexError .

    Interesting Posts

    SSRS 2005 Устанавливает SimplePageHeaders в отчете вместо сервера?

    Ошибка Webkit с `: hover` и несколькими соседними селекторами

    Затухание в каждом элементе – один за другим

    Поддержка контроля версий в Microsoft Word 2007

    Выключение монитора DisplayPort отключает монитор полностью

    Получить измененный контент HTML после его обновления Javascript? (HtmlUnit)

    Использование зарядного устройства с более высоким током на ноутбуке, для которого требуется более низкий ток

    cin для int, вводящего char, вызывает Loop, который должен проверять входные данные

    Как я могу сделать приложения с интенсивной полосой пропускания более низкими приоритетами, чем другие приложения, такие как веб-просмотр / скайп

    Отключить автоматический запуск файла .exe?

    Доступ к event.target внутри обратного вызова в реакции

    Селектор CSS (идентификатор содержит часть текста)

    Как вернуться к Windows 7 после установки Windows 8?

    Как удалить приложение в Windows, когда его деинсталлятор отсутствует?

    Простое объяснение classификации Наив Байеса

    Давайте будем гением компьютера.