2011-01-19 14 views
110

Mam funkcję generatora tak:jak wybrać tylko jeden przedmiot z generatora (w python)?

def myfunct(): 
    ... 
    yield result 

zwykły sposób, aby wywołać tę funkcję będzie:

for r in myfunct(): 
    dostuff(r) 

moje pytanie, czy istnieje sposób, aby tylko jeden element z generatora kiedy lubię? Na przykład, chciałbym zrobić coś takiego:

while True: 
    ... 
    if something: 
     my_element = pick_just_one_element(myfunct()) 
     dostuff(my_element) 
    ... 

Odpowiedz

161

Tworzenie generator korzystając

g = myfunct() 

Everytime chcesz element, użyj

g.next() 

lub

next(g) 

Jeśli generator wyjdzie, podniesie się StopIteration. Można też złapać ten wyjątek, jeśli jest to konieczne, lub użyj default argument next():

next(g, default_value) 
+3

Uwaga: spowoduje to jedynie zatrzymanie podczas próby użycia g.next() po dostarczeniu ostatniego elementu g. – Wilduck

+15

'next (gen, default)' może być również użyte w celu uniknięcia wyjątku 'StopIteration'. Na przykład 'next (g, None)' dla generatora łańcuchów albo przyniesie łańcuch, albo None po zakończeniu iteracji. – Attila

+4

w Pythonie 3000, następny() to __next __() –

-1

Uważam, że jedynym sposobem jest uzyskać listę z iteracyjnej następnie uzyskać żądany element z tej listy.

l = list(myfunct()) 
l[4] 
+0

Odpowiedź Svena jest prawdopodobnie lepsza, ale po prostu zostawię to tutaj, o ile bardziej odpowiada twoim potrzebom. – keegan3d

+22

Zanim to zrobisz, upewnij się, że masz skończony generator. – Seth

+5

Niestety, ma to złożoność długości iteratora, podczas gdy problem jest oczywiście O (1). –

2

Nie sądzę, że istnieje wygodny sposób pobrania arbitralnej wartości z generatora. Generator zapewni metodę next() do przechodzenia przez siebie, ale pełna sekwencja nie jest tworzona natychmiast, aby zaoszczędzić pamięć. Taka jest funkcjonalna różnica między generatorem a listą.

0
generator = myfunct() 
while True: 
    my_element = generator.next() 

upewnić się złapać wyjątek rzucony po ostatnim elemencie jest podejmowana

+0

Nie dotyczy Pythona 3, zobacz doskonałe [odpowiedź kxr] (http://stackoverflow.com/a/35370041/260122). – clacke

12

do zbierania tylko jeden element zastosowania generatora break w for rachunku lub list(itertools.islice(gen, 1))

Według swoim przykładzie (litterally) można zrobić tak:

while True: 
    ... 
    if something: 
     for my_element in myfunct(): 
      dostuff(my_element) 
      break 
     else: 
      do_generator_empty() 

Jeśli chcesz "dostać jus t jeden element z [wcześniej wytworzone] generatora ilekroć like "(I przypuszczać 50% ów pierwotnego zamiaru i najczęściej zamiaru), a następnie:

gen = myfunct() 
while True: 
    ... 
    if something: 
     for my_element in gen: 
      dostuff(my_element) 
      break 
     else: 
      do_generator_empty() 

ten sposób wyraźnie stosowanie generator.next() może być uniknięto, a obsługa końcówek wejściowych nie wymaga (tajemniczej) obsługi wyjątków ani dodatkowych domyślnych porównań wartości.

Sekcja instrukcji else: z for jest potrzebna tylko wtedy, gdy chcesz zrobić coś specjalnego w przypadku końca generatora.

Uwaga na next()/.next():

W Python3 metoda .next() został przemianowany na .__next__() dobrego powodu: jej uznać niskopoziomowe (PEP 3114). Przed wersją Python 2.6 wbudowana funkcja next() nie istniała. Omówiono nawet przeniesienie next() do modułu operator (co byłoby mądre), ze względu na rzadką potrzebę i wątpliwą inflację wbudowanych nazw.

Bez domyślnych ustawień nadal jest bardzo niskopoziomowa - rzucając tajemniczo StopIteration jak bolt z błękitu w normalnym kodzie aplikacji. I używanie next() z domyślnym wartownikiem - który najlepiej powinien być jedyną opcją dla next() bezpośrednio w builtins - jest ograniczone i często daje powód do nieparzystej logiki nie-pytonowej/odczytu.

Dolna linia: użycie next() powinno być bardzo rzadkie - podobnie jak korzystanie z funkcji modułu operator. Korzystanie z funkcji Iterator płynnie jest naturalnym sposobem korzystania z iteratorów na poziomie aplikacji - i zawsze jest to możliwe. next() jest niskim poziomem, dodatkową koncepcją, nieoczywistą - jak pokazuje pytanie tego wątku. Chociaż np. używanie break w for jest konwencjonalne.

+0

To zbyt dużo pracy dla uzyskania pierwszego elementu wyniku listy. Często nie potrzebuję tego, żeby być leniwym, ale nie mam wyboru w py3. Czy nie ma czegoś podobnego do 'mySeq.head'? – javadba

Powiązane problemy