Как я могу заставить генератор Python возвращать None, а не StopIteration?

Я использую генераторы для поиска в списках, как в этом простом примере:

>>> a = [1,2,3,4]
>>> (i for i, v in enumerate(a) if v == 4).next()
3

(Просто чтобы немного обрамить пример, я использую гораздо более длинные списки по сравнению с приведенным выше, и записи немного сложнее, чем int. Я делаю это таким образом, чтобы не приходилось каждый раз просматривать все списки. я их ищу)

Теперь, если бы я вместо этого изменил это на i == 666, он вернул бы StopIteration, потому что не может найти запись 666 в a.

Как я могу заставить его возвращать None вместо этого? Я мог бы, конечно, обернуть это в предложение try ... except, но есть ли более питонический способ сделать это?


person c00kiemonster    schedule 18.08.2011    source источник
comment
Могу я спросить, почему вы используете генераторы для поиска вещей?   -  person Conrad.Dean    schedule 18.08.2011
comment
Что вы ожидаете, если будете искать что-то, мимо чего вы уже прошли? Почему бы просто не использовать более «питоновский» способ, например if i in a: ...?   -  person Manny D    schedule 18.08.2011
comment
@Manny D, if i in a не поможет, если вы хотите получить индекс найденного элемента.   -  person senderle    schedule 18.08.2011
comment
@senderle Вы можете использовать a.index(i). Вы не понимаете тонкостей использования enumerate, правда, но я действительно понимаю, почему вы должны использовать генератор для поиска в списке.   -  person Manny D    schedule 18.08.2011
comment
@Manny D, верно, но только для итераций с определенным методом index. Кроме того, если вы хотите проверить что-то другое, кроме простого равенства, например, если вы хотите найти первый элемент, равный › 5, тогда index не поможет. Тем не менее, вы правы в том, что в конкретном примере c00kiemonster index является более разумным подходом.   -  person senderle    schedule 18.08.2011
comment
Как я сказал в комментарии ниже, фактические элементы списка являются объектами, а сравнения включают атрибуты, поэтому я действительно не знаю, как впихнуть их в идиому if i in a   -  person c00kiemonster    schedule 18.08.2011
comment
@senderle Хм, это хороший пример, не думал об этом. Генератор в этом случае лучше. @c00kiemonster: я считаю, что вы можете определить свойство __iter__ своего класса, чтобы иметь возможность использовать идиому if i in a.   -  person Manny D    schedule 18.08.2011
comment
@ Мэнни Д, неплохая идея. Спасибо за чаевые   -  person c00kiemonster    schedule 18.08.2011


Ответы (2)


Если вы используете Python 2.6+, вам следует использовать встроенную функцию next, а не метод next ( который был заменен на __next__ в 3.x). Встроенный next принимает необязательный аргумент по умолчанию для возврата, если итератор исчерпан, вместо того, чтобы поднимать StopIteration:

next((i for i, v in enumerate(a) if i == 666), None)
person zeekay    schedule 18.08.2011
comment
Я не знал, что встроенная функция изменилась. Это делает его намного проще, конечно - person c00kiemonster; 18.08.2011
comment
Спасибо за ответ. Но я использую Python 3.6.3, который тоже работает для моего проекта. - person Johnny; 28.04.2018

Вы можете связать генератор с (None,):

from itertools import chain
a = [1,2,3,4]
print chain((i for i, v in enumerate(a) if v == 6), (None,)).next()

но я думаю, что a.index(2) не будет проходить по всему списку, когда будет найдено 2, поиск будет завершен. вы можете проверить это:

>>> timeit.timeit("a.index(0)", "a=range(10)")
0.19335955439601094
>>> timeit.timeit("a.index(99)", "a=range(100)")
2.1938486138533335
person HYRY    schedule 18.08.2011
comment
С цепью было очень умно, не подумал об этом. Да, index() неплохо, но в моем реальном случае я не могу его использовать, так как записи списка являются объектами, а не переменными. РЕДАКТИРОВАТЬ: я сравниваю атрибуты объекта и другие забавные вещи, а не просто ищу сам объект. - person c00kiemonster; 18.08.2011
comment
Извините, что говорю это, но это слишком сложное решение. Встроенная функция next уже предлагает эту функциональность в чистом виде. @c00kiemonster, я думаю, вам следует использовать (и принимать) ответ Зикей. - person senderle; 18.08.2011