Chciałbym uzyskać pierwszy element z listy spełniający warunek. Ważne jest, aby wynikowa metoda nie przetwarzała całej listy, która może być dość duża. Na przykład wystarczająca jest następująca funkcja:
def first(the_iterable, condition = lambda x: True):
for i in the_iterable:
if condition(i):
return i
Tej funkcji można użyć w następujący sposób:
>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
Nie mogę jednak wymyślić dobrego wbudowanego / jedno-liniowego, który pozwoliłby mi to zrobić. Nie chcę szczególnie kopiować tej funkcji, jeśli nie muszę. Czy istnieje wbudowany sposób, aby pierwszy element pasował do określonego warunku?
Odpowiedzi:
W Python 2.6 lub nowszym:
Jeśli chcesz
StopIteration
zostać podniesiony, jeśli nie znaleziono pasującego elementu:Jeśli zamiast tego chcesz
default_value
(np.None
) Zostać zwrócony:Zauważ, że w tym przypadku potrzebujesz dodatkowej pary nawiasów wokół wyrażenia generatora - są one potrzebne, gdy wyrażenie generatora nie jest jedynym argumentem.
Widzę, że większość odpowiedzi zdecydowanie ignoruje
next
wbudowane, więc zakładam, że z jakiegoś tajemniczego powodu są one w 100% skoncentrowane na wersjach 2.5 i starszych - nie wspominając o problemie z wersją Pythona (ale potem nie widzę tej wzmianki w odpowiedzi, które wspominają onext
wbudowanym, dlatego uważam, że konieczne jest udzielenie odpowiedzi osobiście - przynajmniej w ten sposób rejestruje się problem „poprawnej wersji” ;-).W wersji 2.5
.next()
metoda iteratorów natychmiast się podnosi,StopIteration
jeśli iterator natychmiast się kończy - tj. W twoim przypadku użycia, jeśli żaden element iteracji nie spełnia warunku. Jeśli cię to nie obchodzi (tzn. Wiesz, że musi istnieć co najmniej jeden zadowalający element), po prostu użyj.next()
(najlepiej na geneksie, linii dlanext
wbudowanego w Pythonie 2.6 i lepszych).Jeśli zrobić opieki, rzeczy owijania w funkcji, jak po raz pierwszy wskazano w Q wydaje się najlepiej, a jednocześnie realizacja funkcji, którą zaproponował jest dobrze, można alternatywnie użyć
itertools
, afor...: break
pętlę, lub genexp lubtry/except StopIteration
jako ciała danej funkcji , jak sugerują różne odpowiedzi. Żadna z tych alternatyw nie ma dużej wartości dodanej, więc wybrałbym bardzo prostą wersję, którą pierwszy raz zaproponowałeś.źródło
StopIteration
gdy nie znaleziono elementuStopIteration
jest naprawdę ładna. Lepiej użyj metody.Jako funkcja wielokrotnego użytku, udokumentowana i przetestowana
Wersja z domyślnym argumentem
@zorf zasugerował wersję tej funkcji, w której możesz mieć predefiniowaną wartość zwracaną, jeśli iterowalny jest pusty lub nie ma elementów pasujących do warunku:
źródło
StopIteration
jest kanonicznym wyjątkiem „poza elementami” w pythonie. Nie widzę problemu z wyrzuceniem go. Prawdopodobnie użyłbym wartości domyślnej „Brak”, którą można przekazać jako domyślny parametr funkcji.Cholerne wyjątki!
Uwielbiam tę odpowiedź . Ponieważ jednak
next()
zgłaszamStopIteration
wyjątek, gdy nie ma żadnych elementów, użyłbym następującego fragmentu kodu, aby uniknąć wyjątku:Na przykład,
Podniesie
StopIteration
wyjątek;źródło
Podobnie do używania
ifilter
, możesz użyć wyrażenia generatora:W obu przypadkach prawdopodobnie chcesz złapać
StopIteration
, na wypadek gdyby żadne elementy nie spełniały Twojego warunku.Technicznie rzecz biorąc, przypuszczam, że możesz zrobić coś takiego:
Pozwoliłoby to uniknąć konieczności wykonania
try/except
bloku. Ale wydaje się to niejasne i obraźliwe dla składni.źródło
for foo in genex: break
to tylko sposób na zrobienie tegofoo = next(genex)
bez wyraźnego przypisania zadania, z wyjątkiem, który zostałby zgłoszony, gdyby operacja nie miała sensu zostać zmiażdżona. Skończyło się na tym, że kod błędu zamiast łapać wyjątek, jest zwykle złą rzeczą w Pythonie.Najbardziej wydajnym sposobem w Pythonie 3 jest jeden z poniższych (na podobnym przykładzie):
W stylu „rozumienia” :
OSTRZEŻENIE : Wyrażenie działa również z Python 2, ale w tym przykładzie użyto
range
zwracającego obiekt iterowalny w Python 3 zamiast listy takiej jak Python 2 (jeśli chcesz zbudować iterowalną w Python 2 użyjxrange
).Zauważ, że wyrażenie to unika konstruowania listy w wyrażeniu ze zrozumieniem
next([i for ...])
, co spowodowałoby utworzenie listy ze wszystkimi elementami przed filtrowaniem elementów i spowodowałoby przetworzenie wszystkich opcji, zamiast zatrzymywać iterację razi == 1000
.W stylu „funkcjonalnym” :
OSTRZEŻENIE : To nie działa w Pythonie 2, a nawet zamienia się
range
zxrange
powodu, któryfilter
tworzy listę zamiast iteratora (nieefektywnego) inext
funkcja działa tylko z iteratorami.Domyślna wartość
Jak wspomniano w innych odpowiedziach, musisz dodać dodatkowy parametr do funkcji,
next
jeśli chcesz uniknąć wyjątku zgłaszanego, gdy warunek nie jest spełniony.styl „funkcjonalny” :
styl „rozumienia” :
W tym stylu musisz otoczyć wyrażenie zrozumienia,
()
aby uniknąćSyntaxError: Generator expression must be parenthesized if not sole argument
:źródło
Napisałbym to
źródło
i > 3
powinienem byćx > 3
w twoim przykładzieitertools
Moduł zawiera funkcję filtra dla iteratorów. Pierwszy element filtrowanego iteratora można uzyskać, wywołującnext()
go:źródło
i
)filter
i (i
)map
mogą mieć sens w przypadkach, w których stosowane funkcje już istnieją, ale w takiej sytuacji bardziej sensowne jest użycie wyrażenia generatora.Dla starszych wersji Pythona, w których nie ma następnej wbudowanej:
źródło
Używając
można sprawdzić stan na wartość pierwszego elementu the_iterable i uzyskania jego indeksu bez konieczności oceny wszystkich elementów w the_iterable .
Pełne wyrażenie do użycia to
Tutaj first_index przyjmuje wartość pierwszej wartości zidentyfikowanej w omówionym powyżej wyrażeniu.
źródło
To pytanie ma już świetne odpowiedzi. Dodam tylko dwa centy, ponieważ wylądowałem tutaj, próbując znaleźć rozwiązanie mojego problemu, który jest bardzo podobny do PO.
Jeśli chcesz znaleźć INDEKS pierwszego elementu spełniającego kryteria przy użyciu generatorów, możesz po prostu:
źródło
Możesz także użyć tej
argwhere
funkcji w Numpy. Na przykład:i) Znajdź pierwsze „l” w „helloworld”:
ii) Znajdź pierwszą liczbę losową> 0,1
iii) Znajdź ostatnią liczbę losową> 0,1
źródło
W Pythonie 3:
W Pythonie 2.6:
EDYCJA: Myślałem, że to oczywiste, ale najwyraźniej nie: zamiast tego
None
możesz przekazać funkcję (lub alambda
) ze sprawdzeniem warunku:źródło
Oneliner:
Jeśli nie masz pewności, że jakikolwiek element będzie poprawny zgodnie z kryteriami, powinieneś dołączyć to,
try/except
ponieważ[0]
może to podnieść anIndexError
.źródło