Popraw mnie, jeśli się mylę, ale gdybyś mógł stworzyć naprawdę ogólne rozwiązanie dla dowolnego generatora, byłoby to równoważne ustawieniu punktów przerwania w instrukcjach yield i posiadaniu możliwości „cofania się”. Czy oznaczałoby to sklonowanie ramki stosu na podstawie plonów i przywrócenie ich w StopIteration?
Cóż, myślę, że przywróć im StopIteration lub nie, ale przynajmniej StopIteration powie ci, że jest pusty. Tak, potrzebuję snu ...
4
Myślę, że wiem, dlaczego on tego chce. Jeśli tworzysz strony internetowe za pomocą szablonów i przekazujesz wartość zwracaną do szablonu takiego jak Cheetah lub coś w tym stylu, pusta lista []jest wygodnie Falsey, więc możesz ją sprawdzić i wykonać specjalne zachowanie dla czegoś lub niczego. Generatory są prawdziwe, nawet jeśli nie dostarczają żadnych elementów.
jpsimons
Oto mój przypadek użycia ... Używam glob.iglob("filepattern")wzorca wieloznacznego dostarczonego przez użytkownika i chcę ostrzec użytkownika, jeśli wzorzec nie pasuje do żadnych plików. Jasne, że mogę to obejść na różne sposoby, ale warto mieć możliwość dokładnego sprawdzenia, czy iterator wyszedł pusty, czy nie.
Prosta odpowiedź na twoje pytanie: nie, nie ma prostego sposobu. Istnieje wiele obejść.
Naprawdę nie powinno być prostego sposobu, z powodu tego, czym są generatory: sposobem na wyjście sekwencji wartości bez przechowywania sekwencji w pamięci . Nie ma więc przechodzenia wstecz.
Możesz napisać funkcję has_next, a może nawet wrzucić ją do generatora jako metodę z fantazyjnym dekoratorem, jeśli chcesz.
w porządku, to ma sens. Wiedziałem, że nie ma sposobu na ustalenie długości generatora, ale pomyślałem, że mogłem przegapić sposób znalezienia, czy początkowo ma on w ogóle coś wygenerować.
Dan,
1
Aha, i dla porównania, spróbowałem zaimplementować własną sugestię „fantazyjnego dekoratora”. CIĘŻKO. Najwyraźniej copy.deepcopy nie działa na generatorach.
David Berger,
47
Nie jestem pewien, czy mogę się zgodzić z „nie powinno być prostego sposobu”. W informatyce istnieje wiele abstrakcji, które mają na celu wyprowadzenie sekwencji wartości bez przechowywania sekwencji w pamięci, ale które pozwalają programiście zapytać, czy istnieje inna wartość bez usuwania jej z „kolejki”, jeśli istnieje. Istnieje coś takiego jak pojedyncze zajrzenie do przodu bez konieczności „przechodzenia wstecz”. Nie oznacza to, że projekt iteratora musi zapewniać taką funkcję, ale z pewnością jest przydatny. Może sprzeciwiasz się temu, że pierwsza wartość może się zmienić po peek'u?
LarsH
9
Sprzeciwiam się tym, że typowa implementacja nawet nie oblicza wartości, dopóki nie jest potrzebna. Można by zmusić interfejs do zrobienia tego, ale może to być nieoptymalne dla lekkich implementacji.
David Berger,
6
@ S.Lott nie musisz generować całej sekwencji, aby wiedzieć, czy sekwencja jest pusta, czy nie. Wystarczy przechowywać jeden element - zobacz moją odpowiedź.
Mark Ransom
99
Sugestia:
def peek(iterable):try:
first = next(iterable)exceptStopIteration:returnNonereturn first, itertools.chain([first], iterable)
Stosowanie:
res = peek(mysequence)if res isNone:# sequence is empty. Do stuff.else:
first, mysequence = res
# Do something with first, maybe?# Then iterate over the sequence:for element in mysequence:# etc.
Nie do końca rozumiem sens zwracania pierwszego elementu dwukrotnie return first, itertools.chain([first], rest).
njzk2
6
@ njzk2 Chciałem wykonać operację „podglądu” (stąd nazwa funkcji). wiki "peek to operacja, która zwraca wartość wierzchołka kolekcji bez usuwania wartości z danych"
John Fouhy,
To nie zadziała, jeśli generator jest zaprojektowany tak, aby dawał Brak. def gen(): for pony in range(4): yield None if pony == 2 else pony
Paweł
4
@Paul Przyjrzyj się uważnie zwracanym wartościom. Jeśli generator jest gotowy - tj. Nie zwraca None, ale podnosi StopIteration- wynikiem funkcji jest None. W przeciwnym razie jest to krotka, która nie jest None.
Załóż pozew Moniki
To bardzo mi pomogło w moim obecnym projekcie. Znalazłem podobny przykład w kodzie dla modułu standardowej biblioteki Pythona „mailbox.py”. This method is for backward compatibility only. def next(self): """Return the next message in a one-time iteration.""" if not hasattr(self, '_onetime_keys'): self._onetime_keys = self.iterkeys() while True: try: return self[next(self._onetime_keys)] except StopIteration: return None except KeyError: continue
peer
29
Prostym sposobem jest użycie opcjonalnego parametru next (), który jest używany, gdy generator jest wyczerpany (lub pusty). Na przykład:
Nie. Jest to niepoprawne dla każdego generatora, w którym pierwsza uzyskana wartość jest nieprawdziwa.
mehtunguh
7
Użyj object()zamiast classzrobić to jedna linia krótsza: _exhausted = object(); if next(iterable, _exhausted) is _exhausted:
Messa,
13
next(generator, None) is not None
Lub wymień, Noneale nie ma jej w generatorze jakąkolwiek znaną Ci wartość .
Edycja : Tak, spowoduje to pominięcie 1 elementu w generatorze. Często jednak sprawdzam, czy generator jest pusty tylko do celów walidacji, a potem tak naprawdę go nie używam. Lub inaczej robię coś takiego:
def foo(self):if next(self.my_generator(),None)isNone:raiseException("Not initiated")for x in self.my_generator():...
Oznacza to, że działa to, jeśli twój generator pochodzi z funkcji , jak w generator().
Dlaczego to nie jest najlepsza odpowiedź? Na wypadek, gdyby generator zwrócił None?
Sait
8
Prawdopodobnie dlatego, że zmusza cię to do faktycznego zużycia generatora, zamiast po prostu testowania, czy jest pusty.
bfontaine
3
To jest złe, ponieważ w momencie, gdy zadzwonisz do następnego (generator, brak), pominiesz 1 element, jeśli jest dostępny
Nathan Do
Prawidłowo, przegapisz pierwszy element swojej generacji, a także zamierzasz konsumować gen, zamiast testować, czy jest pusty.
AJ
12
Najlepszym podejściem, IMHO, byłoby uniknięcie specjalnego testu. W większości przypadków użycie generatora jest testem:
thing_generated =False# Nothing is lost here. if nothing is generated, # the for block is not executed. Often, that's the only check# you need to do. This can be done in the course of doing# the work you wanted to do anyway on the generated output.for thing in my_generator():
thing_generated =True
do_work(thing)
Jeśli to nie wystarczy, nadal możesz przeprowadzić wyraźny test. W tym momencie thingbędzie zawierać ostatnią wygenerowaną wartość. Jeśli nic nie zostało wygenerowane, będzie niezdefiniowane - chyba że zdefiniowałeś już zmienną. Możesz sprawdzić wartość thing, ale to trochę niewiarygodne. Zamiast tego po prostu ustaw flagę w bloku i sprawdź ją później:
ifnot thing_generated:print"Avast, ye scurvy dog!"
To rozwiązanie będzie próbowało pochłonąć cały generator, czyniąc go bezużytecznym dla nieskończonych generatorów.
Viktor Stískala
@ ViktorStískala: Nie widzę twojego punktu widzenia. Byłoby głupotą sprawdzanie, czy nieskończony generator daje jakiekolwiek wyniki.
vezult
Chciałem zwrócić uwagę, że Twoje rozwiązanie może zawierać przerwę w pętli for, ponieważ nie przetwarzasz innych wyników i ich generowanie jest bezużyteczne. range(10000000)jest generatorem skończonym (Python 3), ale nie musisz przeglądać wszystkich elementów, aby dowiedzieć się, czy coś generuje.
Viktor Stískala
1
@ ViktorStískala: Zrozumiano. Chodzi mi jednak o to: ogólnie rzecz biorąc, faktycznie chcesz działać na wyjściu generatora. W moim przykładzie, jeśli nic nie jest generowane, teraz to wiesz. W przeciwnym razie operujesz na wygenerowanym wyjściu zgodnie z przeznaczeniem - „Korzystanie z generatora jest testem”. Nie ma potrzeby przeprowadzania specjalnych testów ani bezcelowego zużywania mocy generatora. Zredagowałem moją odpowiedź, aby to wyjaśnić.
vezult
8
Nienawidzę oferować drugiego rozwiązania, szczególnie takiego, którego sam bym nie użył, ale gdybyś absolutnie musiał to zrobić i nie zużywać generatora, jak w innych odpowiedziach:
def do_something_with_item(item):print item
empty_marker = object()try:
first_item = my_generator.next()exceptStopIteration:print'The generator was empty'
first_item = empty_marker
if first_item isnot empty_marker:
do_something_with_item(first_item)for item in my_generator:
do_something_with_item(item)
Teraz bardzo mi się to rozwiązanie nie podoba, bo uważam, że nie tak mają być używane generatory.
Zdaję sobie sprawę, że ten post ma w tym momencie 5 lat, ale znalazłem go, szukając idiomatycznego sposobu na zrobienie tego, i nie widziałem opublikowanego mojego rozwiązania. Więc dla potomnych:
import itertools
def get_generator():"""
Returns (bool, generator) where bool is true iff the generator is not empty.
"""
gen =(i for i in[0,1,2,3,4])
a, b = itertools.tee(gen)try:
a.next()exceptStopIteration:return(False, b)return(True, b)
Oczywiście, jak z pewnością zauważy wielu komentatorów, jest to zepsute i działa tylko w pewnych ograniczonych sytuacjach (na przykład, gdy generatory są wolne od efektów ubocznych). YMMV.
Spowoduje to wywołanie gengeneratora tylko raz dla każdego przedmiotu, więc skutki uboczne nie są problemem. Ale będzie przechowywać kopię wszystkiego, co zostało pobrane z generatora przez b, ale nie przez a, więc implikacje dotyczące pamięci są podobne do samego uruchomienia list(gen)i sprawdzenia tego.
Matthias Fripp
Ma dwa problemy. 1. To narzędzie itertool może wymagać znacznej ilości pamięci dyskowej (w zależności od tego, ile tymczasowych danych należy przechowywać). Ogólnie rzecz biorąc, jeśli jeden iterator używa większości lub wszystkich danych przed uruchomieniem innego iteratora, szybsze jest użycie list () zamiast tee (). 2. Iteratory tee nie są bezpieczne dla wątków. Błąd RuntimeError może zostać zgłoszony podczas jednoczesnego używania iteratorów zwracanych przez to samo wywołanie tee (), nawet jeśli oryginalna iteracja jest bezpieczna wątkowo.
AJ
3
Przepraszamy za oczywiste podejście, ale najlepiej byłoby zrobić:
for item in my_generator:print item
Teraz wykryłeś, że generator jest pusty, gdy go używasz. Oczywiście pozycja nigdy nie zostanie wyświetlona, jeśli generator jest pusty.
Może to nie pasować do twojego kodu, ale do tego służy idiom generatora: iteracja, więc być może możesz nieznacznie zmienić swoje podejście lub w ogóle nie używać generatorów.
Albo ... osoba pytająca mogłaby podpowiedzieć, dlaczego ktoś miałby próbować wykryć pusty generator?
S.Lott,
czy chodziło Ci o „nic nie będzie wyświetlane, ponieważ generator jest pusty”?
SilentGhost
S.Lott. Zgadzam się. Nie rozumiem dlaczego. Ale myślę, że nawet gdyby istniał powód, lepiej byłoby rozwiązać problem, aby zamiast tego użyć każdego przedmiotu.
Ali Afshar,
1
To nie mówi programowi, czy generator był pusty.
Ethan Furman
3
Wszystko, co musisz zrobić, aby sprawdzić, czy generator jest pusty, to spróbować uzyskać następny wynik. Oczywiście, jeśli nie jesteś gotowy do użycia tego wyniku, musisz go zapisać, aby ponownie go później zwrócić.
Oto klasa opakowania, którą można dodać do istniejącego iteratora w celu dodania __nonzero__testu, dzięki czemu można sprawdzić, czy generator jest pusty za pomocą prostego if. Prawdopodobnie można go również przekształcić w dekoratora.
To zmierza we właściwym kierunku. Powinien zostać zmodyfikowany, aby umożliwić podglądanie przed siebie tak daleko, jak chcesz, przechowując tyle wyników, ile potrzeba. Idealnie pozwoliłoby na wypychanie dowolnych przedmiotów na czoło strumienia. Iterator typu pushable to bardzo przydatna abstrakcja, której często używam.
sfkleach
@sfkleach Nie widzę potrzeby komplikowania tego przy wielokrotnym zajrzeniu w przód, jest to całkiem przydatne i odpowiada na pytanie. Mimo że jest to stare pytanie, od czasu do czasu pojawia się ono, więc jeśli chcesz zostawić własną odpowiedź, ktoś może uznać ją za przydatną.
Mark Ransom
Mark ma rację, że jego rozwiązanie odpowiada na pytanie, które jest kluczowe. Powinienem był to sformułować lepiej. Chodziło mi o to, że pushable-iteratory z nieograniczonym pushback to idiom, który uznałem za niezwykle przydatny, a implementacja jest prawdopodobnie jeszcze prostsza. Zgodnie z sugestią opublikuję kod wariantu.
sfkleach
2
Poproszona przez Mark Ransom, oto klasa, której możesz użyć do zawinięcia dowolnego iteratora, abyś mógł zerknąć przed siebie, wrzucić wartości z powrotem do strumienia i sprawdzić, czy są puste. To prosty pomysł z prostą implementacją, która była dla mnie bardzo przydatna w przeszłości.
classPushable:def __init__(self, iter):
self.source = iter
self.stored =[]def __iter__(self):return self
def __bool__(self):if self.stored:returnTruetry:
self.stored.append(next(self.source))exceptStopIteration:returnFalsereturnTruedef push(self, value):
self.stored.append(value)def peek(self):if self.stored:return self.stored[-1]
value = next(self.source)
self.stored.append(value)return value
def __next__(self):if self.stored:return self.stored.pop()return next(self.source)
>>> g=(i for i in[])>>> g,empty=is_empty_no_side_effects(g)>>> empty
True>>> g=(i for i in range(10))>>> g,empty=is_empty_no_side_effects(g)>>> empty
False>>> list(g)[0,1,2,3,4,5,6,7,8,9]
>>> gen =(i for i in[])>>> next(gen)Traceback(most recent call last):File"<pyshell#43>", line 1,in<module>
next(gen)StopIteration
Na końcu generator StopIterationjest podnoszony, ponieważ w twoim przypadku koniec jest osiągany natychmiast, zgłaszany jest wyjątek. Ale normalnie nie powinieneś sprawdzać istnienia następnej wartości.
inną rzeczą, którą możesz zrobić, jest:
>>> gen =(i for i in[])>>>ifnot list(gen):print('empty generator')
Co faktycznie zużywa cały generator. Niestety, nie jest jasne, czy jest to pożądane czy niepożądane zachowanie.
S.Lott,
jak każdy inny sposób „dotknięcia” generatora.
SilentGhost
Zdaję sobie sprawę, że to jest stare, ale użycie metody 'list ()' nie może być najlepszym sposobem, jeśli wygenerowana lista nie jest pusta, ale w rzeczywistości duża, jest to niepotrzebnie marnotrawstwo
Chris_Rands,
1
Jeśli chcesz wiedzieć, zanim użyjesz generatora, to nie, nie ma prostego sposobu. Jeśli możesz poczekać, aż po użyłeś generatora, istnieje prosty sposób:
was_empty =Truefor some_item in some_generator:
was_empty =False
do_something_with(some_item)if was_empty:
handle_already_empty_generator_case()
Po prostu zawiń generator za pomocą itertools.chain , umieść coś, co będzie reprezentować koniec iteracji jako drugą iterowalną, a następnie po prostu sprawdź to.
Użyj eog = object()zamiast zakładać, że float('-inf')nigdy nie wystąpi w iterowalnej.
bfontaine
@bfontaine Dobry pomysł
smac89
1
W moim przypadku musiałem wiedzieć, czy zapełniono wiele generatorów, zanim przekazałem je do funkcji, która scalała elementy, tj zip(...).. Rozwiązanie jest podobne, ale dostatecznie różne od przyjętej odpowiedzi:
def filter_empty(iterables):for iterable in iterables:
itr_has_items, iterable = has_items(iterable)if itr_has_items:yield iterable
def merge_iterables(iterables):
populated_iterables = filter_empty(iterables)for items in zip(*populated_iterables):# Use items for each "slice"
Mój szczególny problem ma tę właściwość, że elementy iteracyjne są albo puste, albo mają dokładnie taką samą liczbę wpisów.
Uważam, że tylko to rozwiązanie działa również dla pustych iteracji.
def is_generator_empty(generator):
a, b = itertools.tee(generator)try:
next(a)exceptStopIteration:returnTrue, b
returnFalse, b
is_empty, generator = is_generator_empty(generator)
Lub jeśli nie chcesz używać wyjątku do tego, spróbuj użyć
def is_generator_empty(generator):
a, b = itertools.tee(generator)for item in a:returnFalse, b
returnTrue, b
is_empty, generator = is_generator_empty(generator)
Oto prosty dekorator, który otacza generator, więc zwraca None, jeśli jest pusty. Może to być przydatne, jeśli Twój kod musi wiedzieć, czy generator coś wyprodukuje, zanim przejdzie przez niego w pętli.
def generator_or_none(func):"""Wrap a generator function, returning None if it's empty. """def inner(*args,**kwargs):# peek at the first item; return None if it doesn't existtry:
next(func(*args,**kwargs))exceptStopIteration:returnNone# return original generator otherwise first item will be missingreturn func(*args,**kwargs)return inner
Stosowanie:
import random
@generator_or_nonedef random_length_generator():for i in range(random.randint(0,10)):yield i
gen = random_length_generator()if gen isNone:print('Generator is empty')
Jednym z przykładów, w którym jest to przydatne, jest kod szablonów - np. Jinja2
Spowoduje to dwukrotne wywołanie funkcji generatora, co spowoduje dwukrotne poniesienie kosztów uruchomienia generatora. Może to być istotne, jeśli na przykład funkcja generatora jest zapytaniem do bazy danych.
Ian Goldby,
0
używając islice, wystarczy sprawdzić do pierwszej iteracji, aby stwierdzić, czy jest pusty.
z itertools import islice
def isempty (iterowalne):
lista zwrotów (islice (iterowalne, 1)) == []
Nie możemy użyć "any ()" dla wszystkich generatorów. Właśnie próbowałem użyć go z generatorem, który zawiera wiele ramek danych. Otrzymałem wiadomość: „Wartość prawdziwości ramki DataFrame jest niejednoznaczna”. na dowolny (my_generator_of_df)
probitaille
any(generator)działa, gdy wiesz, że generator wygeneruje wartości, na które można rzutować bool- działają podstawowe typy danych (np. int, string). any(generator)będzie False, gdy generator jest pusty lub gdy generator ma tylko fałszywe wartości - na przykład, jeśli generator ma generować 0, '' (pusty ciąg) i False, to nadal będzie False. To może być zamierzone zachowanie lub nie, o ile jesteś tego świadomy :)
from cytoolz import peek
from typing importTuple,Iterabledef is_empty_iterator(g:Iterable)->Tuple[Iterable, bool]:try:
_, g = peek(g)return g,FalseexceptStopIteration:return g,True
Iterator zwracany przez tę funkcję będzie równoważny z oryginalnym przekazanym jako argument.
[]
jest wygodnie Falsey, więc możesz ją sprawdzić i wykonać specjalne zachowanie dla czegoś lub niczego. Generatory są prawdziwe, nawet jeśli nie dostarczają żadnych elementów.glob.iglob("filepattern")
wzorca wieloznacznego dostarczonego przez użytkownika i chcę ostrzec użytkownika, jeśli wzorzec nie pasuje do żadnych plików. Jasne, że mogę to obejść na różne sposoby, ale warto mieć możliwość dokładnego sprawdzenia, czy iterator wyszedł pusty, czy nie.Odpowiedzi:
Prosta odpowiedź na twoje pytanie: nie, nie ma prostego sposobu. Istnieje wiele obejść.
Naprawdę nie powinno być prostego sposobu, z powodu tego, czym są generatory: sposobem na wyjście sekwencji wartości bez przechowywania sekwencji w pamięci . Nie ma więc przechodzenia wstecz.
Możesz napisać funkcję has_next, a może nawet wrzucić ją do generatora jako metodę z fantazyjnym dekoratorem, jeśli chcesz.
źródło
Sugestia:
Stosowanie:
źródło
return first, itertools.chain([first], rest)
.def gen(): for pony in range(4): yield None if pony == 2 else pony
None
, ale podnosiStopIteration
- wynikiem funkcji jestNone
. W przeciwnym razie jest to krotka, która nie jestNone
.This method is for backward compatibility only. def next(self): """Return the next message in a one-time iteration.""" if not hasattr(self, '_onetime_keys'): self._onetime_keys = self.iterkeys() while True: try: return self[next(self._onetime_keys)] except StopIteration: return None except KeyError: continue
Prostym sposobem jest użycie opcjonalnego parametru next (), który jest używany, gdy generator jest wyczerpany (lub pusty). Na przykład:
Edycja: poprawiono problem wskazany w komentarzu mehtunguh.
źródło
object()
zamiastclass
zrobić to jedna linia krótsza:_exhausted = object()
;if next(iterable, _exhausted) is _exhausted:
next(generator, None) is not None
Lub wymień,
None
ale nie ma jej w generatorze jakąkolwiek znaną Ci wartość .Edycja : Tak, spowoduje to pominięcie 1 elementu w generatorze. Często jednak sprawdzam, czy generator jest pusty tylko do celów walidacji, a potem tak naprawdę go nie używam. Lub inaczej robię coś takiego:
Oznacza to, że działa to, jeśli twój generator pochodzi z funkcji , jak w
generator()
.źródło
None
?Najlepszym podejściem, IMHO, byłoby uniknięcie specjalnego testu. W większości przypadków użycie generatora jest testem:
Jeśli to nie wystarczy, nadal możesz przeprowadzić wyraźny test. W tym momencie
thing
będzie zawierać ostatnią wygenerowaną wartość. Jeśli nic nie zostało wygenerowane, będzie niezdefiniowane - chyba że zdefiniowałeś już zmienną. Możesz sprawdzić wartośćthing
, ale to trochę niewiarygodne. Zamiast tego po prostu ustaw flagę w bloku i sprawdź ją później:źródło
range(10000000)
jest generatorem skończonym (Python 3), ale nie musisz przeglądać wszystkich elementów, aby dowiedzieć się, czy coś generuje.Nienawidzę oferować drugiego rozwiązania, szczególnie takiego, którego sam bym nie użył, ale gdybyś absolutnie musiał to zrobić i nie zużywać generatora, jak w innych odpowiedziach:
Teraz bardzo mi się to rozwiązanie nie podoba, bo uważam, że nie tak mają być używane generatory.
źródło
Zdaję sobie sprawę, że ten post ma w tym momencie 5 lat, ale znalazłem go, szukając idiomatycznego sposobu na zrobienie tego, i nie widziałem opublikowanego mojego rozwiązania. Więc dla potomnych:
Oczywiście, jak z pewnością zauważy wielu komentatorów, jest to zepsute i działa tylko w pewnych ograniczonych sytuacjach (na przykład, gdy generatory są wolne od efektów ubocznych). YMMV.
źródło
gen
generatora tylko raz dla każdego przedmiotu, więc skutki uboczne nie są problemem. Ale będzie przechowywać kopię wszystkiego, co zostało pobrane z generatora przezb
, ale nie przeza
, więc implikacje dotyczące pamięci są podobne do samego uruchomienialist(gen)
i sprawdzenia tego.Przepraszamy za oczywiste podejście, ale najlepiej byłoby zrobić:
Teraz wykryłeś, że generator jest pusty, gdy go używasz. Oczywiście pozycja nigdy nie zostanie wyświetlona, jeśli generator jest pusty.
Może to nie pasować do twojego kodu, ale do tego służy idiom generatora: iteracja, więc być może możesz nieznacznie zmienić swoje podejście lub w ogóle nie używać generatorów.
źródło
Wszystko, co musisz zrobić, aby sprawdzić, czy generator jest pusty, to spróbować uzyskać następny wynik. Oczywiście, jeśli nie jesteś gotowy do użycia tego wyniku, musisz go zapisać, aby ponownie go później zwrócić.
Oto klasa opakowania, którą można dodać do istniejącego iteratora w celu dodania
__nonzero__
testu, dzięki czemu można sprawdzić, czy generator jest pusty za pomocą prostegoif
. Prawdopodobnie można go również przekształcić w dekoratora.Oto jak tego użyjesz:
Zauważ, że możesz sprawdzić pustkę w dowolnym momencie, nie tylko na początku iteracji.
źródło
Poproszona przez Mark Ransom, oto klasa, której możesz użyć do zawinięcia dowolnego iteratora, abyś mógł zerknąć przed siebie, wrzucić wartości z powrotem do strumienia i sprawdzić, czy są puste. To prosty pomysł z prostą implementacją, która była dla mnie bardzo przydatna w przeszłości.
źródło
Właśnie wpadłem na ten wątek i zdałem sobie sprawę, że brakuje bardzo prostej i łatwej do odczytania odpowiedzi:
Jeśli nie zamierzamy konsumować żadnego przedmiotu, musimy ponownie wstrzyknąć pierwszy przedmiot do generatora:
Przykład:
źródło
Na końcu generator
StopIteration
jest podnoszony, ponieważ w twoim przypadku koniec jest osiągany natychmiast, zgłaszany jest wyjątek. Ale normalnie nie powinieneś sprawdzać istnienia następnej wartości.inną rzeczą, którą możesz zrobić, jest:
źródło
Jeśli chcesz wiedzieć, zanim użyjesz generatora, to nie, nie ma prostego sposobu. Jeśli możesz poczekać, aż po użyłeś generatora, istnieje prosty sposób:
źródło
Po prostu zawiń generator za pomocą itertools.chain , umieść coś, co będzie reprezentować koniec iteracji jako drugą iterowalną, a następnie po prostu sprawdź to.
Dawny:
Teraz pozostaje tylko sprawdzić tę wartość, którą dodaliśmy na końcu iterowalnej, kiedy ją przeczytasz, będzie to oznaczało koniec
źródło
eog = object()
zamiast zakładać, żefloat('-inf')
nigdy nie wystąpi w iterowalnej.W moim przypadku musiałem wiedzieć, czy zapełniono wiele generatorów, zanim przekazałem je do funkcji, która scalała elementy, tj
zip(...)
.. Rozwiązanie jest podobne, ale dostatecznie różne od przyjętej odpowiedzi:Definicja:
Stosowanie:
Mój szczególny problem ma tę właściwość, że elementy iteracyjne są albo puste, albo mają dokładnie taką samą liczbę wpisów.
źródło
Uważam, że tylko to rozwiązanie działa również dla pustych iteracji.
Lub jeśli nie chcesz używać wyjątku do tego, spróbuj użyć
W zaznaczonym rozwiązaniu nie ma możliwości zastosowania go do pustych generatorów typu
źródło
To jest stare pytanie, na które udzielono odpowiedzi, ale ponieważ nikt go wcześniej nie pokazał, oto ono:
Możesz przeczytać więcej tutaj
źródło
Oto moje proste podejście, którego używam, aby zwracać iterator podczas sprawdzania, czy coś zostało znalezione.Po prostu sprawdzam, czy pętla działa:
źródło
Oto prosty dekorator, który otacza generator, więc zwraca None, jeśli jest pusty. Może to być przydatne, jeśli Twój kod musi wiedzieć, czy generator coś wyprodukuje, zanim przejdzie przez niego w pętli.
Stosowanie:
Jednym z przykładów, w którym jest to przydatne, jest kod szablonów - np. Jinja2
źródło
używając islice, wystarczy sprawdzić do pierwszej iteracji, aby stwierdzić, czy jest pusty.
źródło
A co z użyciem any ()? Używam go z generatorami i działa dobrze. Tutaj jest facet, który trochę to wyjaśnia
źródło
any(generator)
działa, gdy wiesz, że generator wygeneruje wartości, na które można rzutowaćbool
- działają podstawowe typy danych (np. int, string).any(generator)
będzie False, gdy generator jest pusty lub gdy generator ma tylko fałszywe wartości - na przykład, jeśli generator ma generować 0, '' (pusty ciąg) i False, to nadal będzie False. To może być zamierzone zachowanie lub nie, o ile jesteś tego świadomy :)Użyj Peek funkcji w cytoolz.
Iterator zwracany przez tę funkcję będzie równoważny z oryginalnym przekazanym jako argument.
źródło
Rozwiązałem to za pomocą funkcji sumy. Zobacz poniżej przykład, którego użyłem z glob.iglob (który zwraca generator).
* To prawdopodobnie nie zadziała dla OGROMNYCH generatorów, ale powinno dobrze działać dla mniejszych list
źródło