2011-08-18 12 views
27

używam generatorów do wyszukiwania w listach jak ten prosty przykład:W jaki sposób można uzyskać generator Python, aby zwrócić None, a nie StopIteration?

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

(Wystarczy wykadrować przykładzie trochę używam bardzo dużo dłuższych list w stosunku do powyższego, a wpisy są trochę bardziej skomplikowane niż int. robię to w ten sposób, więc cała listy nie będzie przesuwany za każdym razem, kiedy wyszukiwania nich)

teraz gdybym zamiast zmienić się i == 666, byłoby zwrócić StopIteration ponieważ może” t znaleźć dowolny wpis 666 w a.

W jaki sposób mogę go zwrócić zamiast numeru None? Mógłbym oczywiście zawinąć to w klauzulę try ... except, ale czy jest tam bardziej pythonic sposób to zrobić?

+0

Czy mogę zapytać, dlaczego używasz generatory, aby szukać rzeczy? –

+0

Czego oczekujesz, jeśli szukasz czegoś, co już minęło? Dlaczego nie po prostu użyć bardziej "pythonic" sposób jak "if in a: ..."? –

+0

@Many D, 'if i in a' nie pomaga, jeśli chcesz uzyskać indeks znalezionego przedmiotu. – senderle

Odpowiedz

70

Jeśli używasz Python 2.6+ należy użyć next wbudowaną funkcję, a nie metoda next (który został zastąpiony __next__ w 3.x). next wbudowaną bierze domyślną opcjonalnego argumentu, aby powrócić jeśli iterator jest wyczerpany, zamiast podnoszenia StopIteration:

next((i for i, v in enumerate(a) if i == 666), None) 
+1

+1, jest to najlepszy sposób, aby to zrobić. – senderle

+0

Nie wiedziałem, że funkcja wbudowana uległa zmianie. To na pewno znacznie łatwiejsze – c00kiemonster

7

Można łańcucha z generatora (None,):

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

ale myślę a.index (2) nie będzie przemierzać całą listę, po 2 zostanie znaleziony, wyszukiwanie zostanie zakończone. można to sprawdzić:

>>> timeit.timeit("a.index(0)", "a=range(10)") 
0.19335955439601094 
>>> timeit.timeit("a.index(99)", "a=range(100)") 
2.1938486138533335 
+1

Rzecz łańcucha była bardzo sprytna, nie myślałem o tym. Tak, 'index()' nie jest zły, ale w moim prawdziwym przypadku nie mogę go naprawdę użyć, ponieważ wpisy na liście są obiektami, a nie zmiennymi. EDYCJA: Porównuję atrybuty obiektów i inne zabawne rzeczy, a nie tylko szukam samego obiektu. – c00kiemonster

+1

Przykro mi to mówić, ale jest to zbyt skomplikowane rozwiązanie. Funkcja wbudowana 'next' już oferuje tę funkcjonalność w czysty sposób. @ c00kiemonster, myślę, że powinieneś użyć (i zaakceptować) [odpowiedź zeekay] (http://stackoverflow.com/questions/7102050/how-can-i-get-a-python-generator-to-return-none-rather -than-stopiteration/7102204 # 7102204). – senderle

+0

Bez obaw, dobry sposób, aby dowiedzieć się nowszych rzeczy w Pythonie, jeśli nic innego ... – c00kiemonster

Powiązane problemy