Korrigieren Sie mich, wenn ich falsch liege, aber wenn Sie eine wirklich generische Lösung für einen Generator finden könnten, wäre dies gleichbedeutend mit dem Setzen von Haltepunkten in den Ertragsanweisungen und der Fähigkeit, "zurückzutreten". Würde das bedeuten, den Stack-Frame nach Erträgen zu klonen und bei StopIteration wiederherzustellen?
Nun, ich denke, stellen Sie StopIteration wieder her oder nicht, aber zumindest würde StopIteration Ihnen sagen, dass es leer war. Ja, ich brauche Schlaf ...
4
Ich glaube ich weiß warum er das will. Wenn Sie eine Webentwicklung mit Vorlagen durchführen und den Rückgabewert an eine Vorlage wie Cheetah oder etwas []anderes übergeben , ist die leere Liste praktisch Falsey, sodass Sie eine If-Überprüfung durchführen und ein spezielles Verhalten für etwas oder nichts ausführen können. Generatoren sind auch dann wahr, wenn sie keine Elemente liefern.
Jpsimons
Hier ist mein Anwendungsfall ... Ich verwende glob.iglob("filepattern")ein vom Benutzer bereitgestelltes Platzhaltermuster und möchte den Benutzer warnen, wenn das Muster nicht mit Dateien übereinstimmt. Natürlich kann ich das auf verschiedene Arten umgehen, aber es ist nützlich, sauber testen zu können, ob der Iterator leer war oder nicht.
Die einfache Antwort auf Ihre Frage: Nein, es gibt keinen einfachen Weg. Es gibt viele Umgehungsmöglichkeiten.
Es sollte wirklich keinen einfachen Weg geben, aufgrund dessen, was Generatoren sind: einen Weg, eine Folge von Werten auszugeben, ohne die Folge im Speicher zu halten . Es gibt also keine Rückwärtsdurchquerung.
Sie können eine has_next-Funktion schreiben oder sie sogar als Methode mit einem ausgefallenen Dekorateur auf einen Generator übertragen, wenn Sie möchten.
fair genug, das macht Sinn. Ich wusste, dass es keine Möglichkeit gibt, die Länge eines Generators zu ermitteln, dachte aber, ich hätte möglicherweise eine Möglichkeit verpasst, herauszufinden, ob er anfänglich überhaupt etwas erzeugen wird.
Dan
1
Oh, und als Referenz habe ich versucht, meinen eigenen "Fancy Decorator" -Vorschlag umzusetzen. SCHWER. Anscheinend funktioniert copy.deepcopy bei Generatoren nicht.
David Berger
47
Ich bin mir nicht sicher, ob ich zustimmen kann, dass es keinen einfachen Weg geben sollte. In der Informatik gibt es viele Abstraktionen, die darauf ausgelegt sind, eine Folge von Werten auszugeben, ohne die Folge im Speicher zu halten, die es dem Programmierer jedoch ermöglichen, zu fragen, ob es einen anderen Wert gibt, ohne ihn aus der "Warteschlange" zu entfernen, falls vorhanden. Es gibt so etwas wie einen einzelnen Blick voraus, ohne dass eine "Rückwärtsdurchquerung" erforderlich ist. Das heißt nicht, dass ein Iterator-Design eine solche Funktion bereitstellen muss, aber es ist sicher nützlich. Vielleicht protestieren Sie mit der Begründung, dass sich der erste Wert nach dem Blick ändern könnte?
LarsH
9
Ich protestiere mit der Begründung, dass eine typische Implementierung erst dann einen Wert berechnet, wenn er benötigt wird. Man könnte die Schnittstelle dazu zwingen, aber das könnte für leichtgewichtige Implementierungen nicht optimal sein.
David Berger
6
@ S.Lott Sie müssen nicht die gesamte Sequenz generieren, um zu wissen, ob die Sequenz leer ist oder nicht. Der Speicherplatz eines Elements ist ausreichend - siehe meine Antwort.
Mark Ransom
98
Vorschlag:
def peek(iterable):try:
first = next(iterable)exceptStopIteration:returnNonereturn first, itertools.chain([first], iterable)
Verwendung:
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.
Ich verstehe es nicht ganz, das erste Element zweimal zurückzugeben return first, itertools.chain([first], rest).
NJZK2
6
@ njzk2 Ich wollte eine "Peek" -Operation (daher der Funktionsname). Wiki "Peek ist eine Operation, die den Wert des oberen
Teils
Dies funktioniert nicht, wenn der Generator so ausgelegt ist, dass er Keine liefert. def gen(): for pony in range(4): yield None if pony == 2 else pony
Paul
4
@Paul Schauen Sie sich die Rückgabewerte genau an. Wenn der Generator fertig ist - dh nicht zurückkehrt None, sondern anhebt StopIteration- ist das Ergebnis der Funktion None. Ansonsten ist es ein Tupel, was nicht ist None.
Fund Monica Klage
Dies hat mir bei meinem aktuellen Projekt sehr geholfen. Ich habe ein ähnliches Beispiel im Code für Pythons Standardbibliotheksmodul 'mailbox.py' gefunden. 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
Eine einfache Möglichkeit besteht darin, den optionalen Parameter für next () zu verwenden, der verwendet wird, wenn der Generator erschöpft (oder leer) ist. Beispielsweise:
Nein. Dies ist für jeden Generator falsch, bei dem der erste Wert nicht wahr ist.
Mehtunguh
7
Verwenden Sie ein object()statt class, um es eine Zeile kürzer zu machen : _exhausted = object(); if next(iterable, _exhausted) is _exhausted:
Messa
13
next(generator, None) is not None
Oder ersetzen Sie, Noneaber welcher Wert auch immer Sie wissen, er befindet sich nicht in Ihrem Generator.
Bearbeiten : Ja, dies überspringt 1 Element im Generator. Oft überprüfe ich jedoch, ob ein Generator nur zu Validierungszwecken leer ist, und verwende ihn dann nicht wirklich. Oder sonst mache ich so etwas wie:
def foo(self):if next(self.my_generator(),None)isNone:raiseException("Not initiated")for x in self.my_generator():...
Das heißt, dies funktioniert, wenn Ihr Generator von einer Funktion stammt , wie in generator().
Warum ist das nicht die beste Antwort? Falls der Generator zurückkehrt None?
Sait
8
Wahrscheinlich, weil Sie dadurch gezwungen sind, den Generator tatsächlich zu verbrauchen, anstatt nur zu testen, ob er leer ist.
Bfontaine
3
Es ist schlecht, weil Sie in dem Moment, in dem Sie das nächste Mal anrufen (Generator, Keine), 1 Element überspringen, wenn es verfügbar ist
Nathan Do
Richtig, Sie werden das erste Element Ihres Gens vermissen und auch Ihr Gen verbrauchen, anstatt zu testen, ob es leer ist.
AJ
12
Der beste Ansatz, IMHO, wäre es, einen speziellen Test zu vermeiden. In den meisten Fällen ist die Verwendung eines Generators der Test:
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)
Wenn das nicht gut genug ist, können Sie trotzdem einen expliziten Test durchführen. Zu diesem Zeitpunkt thingwird der zuletzt generierte Wert enthalten. Wenn nichts generiert wurde, ist es undefiniert - es sei denn, Sie haben die Variable bereits definiert. Sie könnten den Wert von überprüfen thing, aber das ist ein bisschen unzuverlässig. Setzen Sie stattdessen einfach ein Flag innerhalb des Blocks und überprüfen Sie es anschließend:
ifnot thing_generated:print"Avast, ye scurvy dog!"
Diese Lösung wird versuchen, den gesamten Generator zu verbrauchen, wodurch er für unendliche Generatoren unbrauchbar wird.
Viktor Stískala
@ ViktorStískala: Ich verstehe deinen Standpunkt nicht. Es wäre dumm zu testen, ob ein unendlicher Generator irgendwelche Ergebnisse liefert.
vezult
Ich wollte darauf hinweisen, dass Ihre Lösung eine Unterbrechung in der for-Schleife enthalten könnte, da Sie die anderen Ergebnisse nicht verarbeiten und es für sie nutzlos ist, sie zu generieren. range(10000000)ist ein endlicher Generator (Python 3), aber Sie müssen nicht alle Elemente durchgehen, um herauszufinden, ob er etwas generiert.
Viktor Stískala
1
@ ViktorStískala: Verstanden. Mein Punkt ist jedoch folgender: Im Allgemeinen möchten Sie tatsächlich am Generatorausgang arbeiten. Wenn in meinem Beispiel nichts generiert wird, wissen Sie es jetzt. Andernfalls bearbeiten Sie den generierten Ausgang wie vorgesehen - "Die Verwendung des Generators ist der Test". Keine Notwendigkeit für spezielle Tests oder sinnlosen Verbrauch der Generatorleistung. Ich habe meine Antwort bearbeitet, um dies zu verdeutlichen.
Vezult
8
Ich hasse eine zweite Lösung zu bieten, vor allem ein , dass ich mich nicht verwenden würde, aber, wenn Sie unbedingt hat , dies zu tun und zu konsumieren , ohne den Generator, wie in anderen Antworten:
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)
Jetzt mag ich diese Lösung wirklich nicht, weil ich glaube, dass Generatoren nicht so verwendet werden sollen.
Mir ist klar, dass dieser Beitrag zu diesem Zeitpunkt 5 Jahre alt ist, aber ich habe ihn gefunden, als ich nach einer idiomatischen Methode gesucht habe, und habe meine Lösung nicht veröffentlicht. Also für die Nachwelt:
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)
Wie viele Kommentatoren sicher betonen werden, ist dies natürlich hackig und funktioniert überhaupt nur in bestimmten begrenzten Situationen (in denen die Generatoren beispielsweise frei von Nebenwirkungen sind). YMMV.
Dadurch wird der genGenerator für jedes Element nur einmal aufgerufen , sodass Nebenwirkungen kein allzu schlimmes Problem darstellen. Es wird jedoch eine Kopie von allem gespeichert, was über b, aber nicht über aus dem Generator gezogen wurde. Die aAuswirkungen auf den Speicher ähneln also dem Ausführen list(gen)und Überprüfen.
Matthias Fripp
Es gibt zwei Probleme. 1. Dieses itertool erfordert möglicherweise einen erheblichen zusätzlichen Speicher (abhängig davon, wie viele temporäre Daten gespeichert werden müssen). Wenn ein Iterator die meisten oder alle Daten verwendet, bevor ein anderer Iterator startet, ist es im Allgemeinen schneller, list () anstelle von tee () zu verwenden. 2. Tee-Iteratoren sind nicht threadsicher. Ein RuntimeError kann ausgelöst werden, wenn gleichzeitig Iteratoren verwendet werden, die vom gleichen tee () -Aufruf zurückgegeben werden, selbst wenn die ursprüngliche Iterable threadsicher ist.
AJ
3
Entschuldigen Sie den offensichtlichen Ansatz, aber der beste Weg wäre:
for item in my_generator:print item
Jetzt haben Sie festgestellt, dass der Generator leer ist, während Sie ihn verwenden. Natürlich wird das Element niemals angezeigt, wenn der Generator leer ist.
Dies passt möglicherweise nicht genau zu Ihrem Code, aber dafür ist die Redewendung des Generators gedacht: Iterieren. Vielleicht können Sie Ihren Ansatz geringfügig ändern oder Generatoren überhaupt nicht verwenden.
Oder ... könnte der Fragesteller einen Hinweis geben, warum man versuchen würde, einen leeren Generator zu entdecken?
S.Lott
Meinten Sie "nichts wird angezeigt, da der Generator leer ist"?
SilentGhost
S.Lott. Genau. Ich kann nicht verstehen warum. Aber ich denke, selbst wenn es einen Grund gab, könnte das Problem besser gelöst werden, stattdessen jeden Gegenstand zu verwenden.
Ali Afshar
1
Dies teilt dem Programm nicht mit, ob der Generator leer war.
Ethan Furman
3
Alles, was Sie tun müssen, um festzustellen, ob ein Generator leer ist, ist zu versuchen, das nächste Ergebnis zu erhalten. Natürlich, wenn Sie nicht bereit sind dieses Ergebnis , müssen speichern, um es später erneut zurückzugeben.
Hier ist eine Wrapper-Klasse, die einem vorhandenen Iterator hinzugefügt werden kann, um einen __nonzero__Test hinzuzufügen , sodass Sie mit einem einfachen Befehl sehen können, ob der Generator leer ist if. Es kann wahrscheinlich auch in einen Dekorateur verwandelt werden.
Dies geht in die richtige Richtung. Es sollte so geändert werden, dass Sie so weit wie möglich nach vorne schauen und so viele Ergebnisse wie nötig speichern können. Im Idealfall können beliebige Elemente auf den Kopf des Streams gedrückt werden. Ein Pushable-Iterator ist eine sehr nützliche Abstraktion, die ich oft benutze.
Sfkleach
@sfkleach Ich sehe keine Notwendigkeit, dies für mehrere Peek-Aheads zu komplizieren. Es ist so wie es ist sehr nützlich und beantwortet die Frage. Auch wenn dies eine alte Frage ist, sieht sie immer noch gelegentlich aus. Wenn Sie also Ihre eigene Antwort hinterlassen möchten, könnte es jemand nützlich finden.
Mark Ransom
Mark hat völlig Recht, dass seine Lösung die Frage beantwortet, was der entscheidende Punkt ist. Ich hätte es besser formulieren sollen. Was ich damit meinte war, dass Pushable-Iteratoren mit unbegrenztem Pushback eine Redewendung sind, die ich als äußerst nützlich empfunden habe und deren Implementierung wohl noch einfacher ist. Wie vorgeschlagen werde ich den Variantencode posten.
sfkleach
2
Auf Aufforderung von Mark Ransom finden Sie hier eine Klasse, mit der Sie jeden Iterator umschließen können, damit Sie einen Blick nach vorne werfen, Werte zurück in den Stream verschieben und nach Leerzeichen suchen können. Es ist eine einfache Idee mit einer einfachen Implementierung, die ich in der Vergangenheit als sehr praktisch empfunden habe.
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
Am Ende des Generators StopIterationwird ausgelöst, da in Ihrem Fall das Ende sofort erreicht ist, wird eine Ausnahme ausgelöst. Normalerweise sollten Sie jedoch nicht prüfen, ob der nächste Wert vorhanden ist.
Sie können auch Folgendes tun:
>>> gen =(i for i in[])>>>ifnot list(gen):print('empty generator')
Was eigentlich den ganzen Generator verbraucht. Leider ist aus der Frage nicht klar, ob dies wünschenswert oder unerwünscht ist.
S.Lott
wie jede andere Art, den Generator zu "berühren", nehme ich an.
SilentGhost
Mir ist klar, dass dies alt ist, aber die Verwendung von 'list ()' kann nicht der beste Weg sein. Wenn die generierte Liste nicht leer, sondern tatsächlich groß ist, ist dies unnötig verschwenderisch
Chris_Rands
1
Wenn Sie wissen müssen, bevor Sie den Generator verwenden, gibt es keinen einfachen Weg. Wenn Sie warten , bis können nach der Generator verwendet haben, gibt es eine einfache Art und Weise:
was_empty =Truefor some_item in some_generator:
was_empty =False
do_something_with(some_item)if was_empty:
handle_already_empty_generator_case()
Wickeln Sie den Generator einfach mit itertools.chain ein , setzen Sie etwas, das das Ende der , als zweite Iterable ein und überprüfen Sie dies einfach.
Verwenden Sie diese eog = object()Option, anstatt davon auszugehen, dass float('-inf')sie in der Iterable niemals auftreten wird.
Bfontaine
@bfontaine Gute Idee
smac89
1
In meinem Fall musste ich wissen, ob eine Vielzahl von Generatoren gefüllt war, bevor ich sie an eine Funktion weitergab, die die Elemente zusammenführte, d zip(...). H. Die Lösung ist ähnlich, aber unterschiedlich genug von der akzeptierten Antwort:
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"
Mein spezielles Problem hat die Eigenschaft, dass die Iterables entweder leer sind oder genau die gleiche Anzahl von Einträgen haben.
Ich fand nur diese Lösung auch für leere Iterationen geeignet.
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)
Oder wenn Sie hierfür keine Ausnahme verwenden möchten, versuchen Sie es
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)
In der markierten Lösung ist es nicht möglich, sie für leere Generatoren wie zu verwenden
Hier ist mein einfacher Ansatz, mit dem ich immer wieder einen Iterator zurückgebe, während ich überprüfe, ob etwas ausgegeben wurde. Ich überprüfe nur, ob die Schleife ausgeführt wird:
n =0for key, value in iterator:
n+=1yield key, value
if n ==0:print("nothing found in iterator)
break
Hier ist ein einfacher Dekorator, der den Generator umschließt, sodass er None zurückgibt, wenn er leer ist. Dies kann nützlich sein, wenn Ihr Code wissen muss, ob der Generator etwas erzeugt, bevor er ihn durchläuft.
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
Verwendung:
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')
Ein Beispiel, wo dies nützlich ist, ist das Templating von Code - dh jinja2
Dadurch wird die Generatorfunktion zweimal aufgerufen, sodass die Startkosten des Generators zweimal anfallen. Dies kann erheblich sein, wenn die Generatorfunktion beispielsweise eine Datenbankabfrage ist.
Ian Goldby
0
Mit islice müssen Sie nur bis zur ersten Iteration prüfen, ob sie leer ist.
Wir können "any ()" nicht für alles Generator verwenden. Ich habe gerade versucht, es mit einem Generator zu verwenden, der mehrere Datenrahmen enthält. Ich habe die Meldung "Der Wahrheitswert eines DataFrame ist nicht eindeutig." auf jedem (my_generator_of_df)
probitaille
any(generator)funktioniert, wenn Sie wissen, dass der Generator Werte generiert, in die umgewandelt werden kann bool- die grundlegenden Datentypen (z. B. int, string) funktionieren. any(generator)ist False, wenn der Generator leer ist oder wenn der Generator nur falsche Werte hat - wenn ein Generator beispielsweise 0, '' (leere Zeichenfolge) und False generieren soll, ist er immer noch False. Dies könnte das beabsichtigte Verhalten sein oder auch nicht, solange Sie sich dessen bewusst sind :)
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
Der von dieser Funktion zurückgegebene Iterator entspricht dem ursprünglichen Argument, das als Argument übergeben wurde.
[]
anderes übergeben , ist die leere Liste praktisch Falsey, sodass Sie eine If-Überprüfung durchführen und ein spezielles Verhalten für etwas oder nichts ausführen können. Generatoren sind auch dann wahr, wenn sie keine Elemente liefern.glob.iglob("filepattern")
ein vom Benutzer bereitgestelltes Platzhaltermuster und möchte den Benutzer warnen, wenn das Muster nicht mit Dateien übereinstimmt. Natürlich kann ich das auf verschiedene Arten umgehen, aber es ist nützlich, sauber testen zu können, ob der Iterator leer war oder nicht.Antworten:
Die einfache Antwort auf Ihre Frage: Nein, es gibt keinen einfachen Weg. Es gibt viele Umgehungsmöglichkeiten.
Es sollte wirklich keinen einfachen Weg geben, aufgrund dessen, was Generatoren sind: einen Weg, eine Folge von Werten auszugeben, ohne die Folge im Speicher zu halten . Es gibt also keine Rückwärtsdurchquerung.
Sie können eine has_next-Funktion schreiben oder sie sogar als Methode mit einem ausgefallenen Dekorateur auf einen Generator übertragen, wenn Sie möchten.
quelle
Vorschlag:
Verwendung:
quelle
return first, itertools.chain([first], rest)
.def gen(): for pony in range(4): yield None if pony == 2 else pony
None
, sondern anhebtStopIteration
- ist das Ergebnis der FunktionNone
. Ansonsten ist es ein Tupel, was nicht istNone
.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
Eine einfache Möglichkeit besteht darin, den optionalen Parameter für next () zu verwenden, der verwendet wird, wenn der Generator erschöpft (oder leer) ist. Beispielsweise:
Bearbeiten: Das in mehtunguhs Kommentar erwähnte Problem wurde behoben.
quelle
object()
stattclass
, um es eine Zeile kürzer zu machen :_exhausted = object()
;if next(iterable, _exhausted) is _exhausted:
next(generator, None) is not None
Oder ersetzen Sie,
None
aber welcher Wert auch immer Sie wissen, er befindet sich nicht in Ihrem Generator.Bearbeiten : Ja, dies überspringt 1 Element im Generator. Oft überprüfe ich jedoch, ob ein Generator nur zu Validierungszwecken leer ist, und verwende ihn dann nicht wirklich. Oder sonst mache ich so etwas wie:
Das heißt, dies funktioniert, wenn Ihr Generator von einer Funktion stammt , wie in
generator()
.quelle
None
?Der beste Ansatz, IMHO, wäre es, einen speziellen Test zu vermeiden. In den meisten Fällen ist die Verwendung eines Generators der Test:
Wenn das nicht gut genug ist, können Sie trotzdem einen expliziten Test durchführen. Zu diesem Zeitpunkt
thing
wird der zuletzt generierte Wert enthalten. Wenn nichts generiert wurde, ist es undefiniert - es sei denn, Sie haben die Variable bereits definiert. Sie könnten den Wert von überprüfenthing
, aber das ist ein bisschen unzuverlässig. Setzen Sie stattdessen einfach ein Flag innerhalb des Blocks und überprüfen Sie es anschließend:quelle
range(10000000)
ist ein endlicher Generator (Python 3), aber Sie müssen nicht alle Elemente durchgehen, um herauszufinden, ob er etwas generiert.Ich hasse eine zweite Lösung zu bieten, vor allem ein , dass ich mich nicht verwenden würde, aber, wenn Sie unbedingt hat , dies zu tun und zu konsumieren , ohne den Generator, wie in anderen Antworten:
Jetzt mag ich diese Lösung wirklich nicht, weil ich glaube, dass Generatoren nicht so verwendet werden sollen.
quelle
Mir ist klar, dass dieser Beitrag zu diesem Zeitpunkt 5 Jahre alt ist, aber ich habe ihn gefunden, als ich nach einer idiomatischen Methode gesucht habe, und habe meine Lösung nicht veröffentlicht. Also für die Nachwelt:
Wie viele Kommentatoren sicher betonen werden, ist dies natürlich hackig und funktioniert überhaupt nur in bestimmten begrenzten Situationen (in denen die Generatoren beispielsweise frei von Nebenwirkungen sind). YMMV.
quelle
gen
Generator für jedes Element nur einmal aufgerufen , sodass Nebenwirkungen kein allzu schlimmes Problem darstellen. Es wird jedoch eine Kopie von allem gespeichert, was überb
, aber nicht über aus dem Generator gezogen wurde. Diea
Auswirkungen auf den Speicher ähneln also dem Ausführenlist(gen)
und Überprüfen.Entschuldigen Sie den offensichtlichen Ansatz, aber der beste Weg wäre:
Jetzt haben Sie festgestellt, dass der Generator leer ist, während Sie ihn verwenden. Natürlich wird das Element niemals angezeigt, wenn der Generator leer ist.
Dies passt möglicherweise nicht genau zu Ihrem Code, aber dafür ist die Redewendung des Generators gedacht: Iterieren. Vielleicht können Sie Ihren Ansatz geringfügig ändern oder Generatoren überhaupt nicht verwenden.
quelle
Alles, was Sie tun müssen, um festzustellen, ob ein Generator leer ist, ist zu versuchen, das nächste Ergebnis zu erhalten. Natürlich, wenn Sie nicht bereit sind dieses Ergebnis , müssen speichern, um es später erneut zurückzugeben.
Hier ist eine Wrapper-Klasse, die einem vorhandenen Iterator hinzugefügt werden kann, um einen
__nonzero__
Test hinzuzufügen , sodass Sie mit einem einfachen Befehl sehen können, ob der Generator leer istif
. Es kann wahrscheinlich auch in einen Dekorateur verwandelt werden.So würden Sie es verwenden:
Beachten Sie, dass Sie jederzeit auf Leere prüfen können, nicht nur zu Beginn der Iteration.
quelle
Auf Aufforderung von Mark Ransom finden Sie hier eine Klasse, mit der Sie jeden Iterator umschließen können, damit Sie einen Blick nach vorne werfen, Werte zurück in den Stream verschieben und nach Leerzeichen suchen können. Es ist eine einfache Idee mit einer einfachen Implementierung, die ich in der Vergangenheit als sehr praktisch empfunden habe.
quelle
Ich bin gerade auf diesen Thread gefallen und habe festgestellt, dass eine sehr einfache und leicht zu lesende Antwort fehlt:
Wenn wir keinen Artikel konsumieren sollen, müssen wir den ersten Artikel erneut in den Generator injizieren:
Beispiel:
quelle
Am Ende des Generators
StopIteration
wird ausgelöst, da in Ihrem Fall das Ende sofort erreicht ist, wird eine Ausnahme ausgelöst. Normalerweise sollten Sie jedoch nicht prüfen, ob der nächste Wert vorhanden ist.Sie können auch Folgendes tun:
quelle
Wenn Sie wissen müssen, bevor Sie den Generator verwenden, gibt es keinen einfachen Weg. Wenn Sie warten , bis können nach der Generator verwendet haben, gibt es eine einfache Art und Weise:
quelle
Wickeln Sie den Generator einfach mit itertools.chain ein , setzen Sie etwas, das das Ende der , als zweite Iterable ein und überprüfen Sie dies einfach.
Ex:
Jetzt müssen Sie nur noch nach dem Wert suchen, den wir an das Ende der Iterable angehängt haben. Wenn Sie ihn lesen, bedeutet dies das Ende
quelle
eog = object()
Option, anstatt davon auszugehen, dassfloat('-inf')
sie in der Iterable niemals auftreten wird.In meinem Fall musste ich wissen, ob eine Vielzahl von Generatoren gefüllt war, bevor ich sie an eine Funktion weitergab, die die Elemente zusammenführte, d
zip(...)
. H. Die Lösung ist ähnlich, aber unterschiedlich genug von der akzeptierten Antwort:Definition:
Verwendung:
Mein spezielles Problem hat die Eigenschaft, dass die Iterables entweder leer sind oder genau die gleiche Anzahl von Einträgen haben.
quelle
Ich fand nur diese Lösung auch für leere Iterationen geeignet.
Oder wenn Sie hierfür keine Ausnahme verwenden möchten, versuchen Sie es
In der markierten Lösung ist es nicht möglich, sie für leere Generatoren wie zu verwenden
quelle
Dies ist eine alte und beantwortete Frage, aber wie noch niemand zuvor gezeigt hat, geht es los:
Hier können Sie mehr lesen
quelle
Hier ist mein einfacher Ansatz, mit dem ich immer wieder einen Iterator zurückgebe, während ich überprüfe, ob etwas ausgegeben wurde. Ich überprüfe nur, ob die Schleife ausgeführt wird:
quelle
Hier ist ein einfacher Dekorator, der den Generator umschließt, sodass er None zurückgibt, wenn er leer ist. Dies kann nützlich sein, wenn Ihr Code wissen muss, ob der Generator etwas erzeugt, bevor er ihn durchläuft.
Verwendung:
Ein Beispiel, wo dies nützlich ist, ist das Templating von Code - dh jinja2
quelle
Mit islice müssen Sie nur bis zur ersten Iteration prüfen, ob sie leer ist.
quelle
Was ist mit any ()? Ich benutze es mit Generatoren und es funktioniert gut. Hier erklärt ein Typ ein wenig darüber
quelle
any(generator)
funktioniert, wenn Sie wissen, dass der Generator Werte generiert, in die umgewandelt werden kannbool
- die grundlegenden Datentypen (z. B. int, string) funktionieren.any(generator)
ist False, wenn der Generator leer ist oder wenn der Generator nur falsche Werte hat - wenn ein Generator beispielsweise 0, '' (leere Zeichenfolge) und False generieren soll, ist er immer noch False. Dies könnte das beabsichtigte Verhalten sein oder auch nicht, solange Sie sich dessen bewusst sind :)Verwenden Sie die Peek- Funktion in Cytoolz.
Der von dieser Funktion zurückgegebene Iterator entspricht dem ursprünglichen Argument, das als Argument übergeben wurde.
quelle
Ich habe es mit der Summenfunktion gelöst. Unten finden Sie ein Beispiel, das ich mit glob.iglob verwendet habe (das einen Generator zurückgibt).
* Dies wird wahrscheinlich nicht für RIESIGE Generatoren funktionieren, sollte aber für kleinere Listen gut funktionieren
quelle