"Es ist einfacher, um Vergebung zu bitten als um Erlaubnis."
118
"Es ist einfacher, um Vergebung als um Erlaubnis zu bitten.": Wenn Sie überprüfen, ob ein Iterator ein nächstes Element hat, werden Sie nicht um Erlaubnis gebeten. Es gibt Situationen, in denen Sie die Existenz eines nächsten Elements testen möchten, ohne es zu verbrauchen. Ich würde die try catch-Lösung akzeptieren, wenn es eine unnext()Methode gäbe , um das erste Element zurückzusetzen, nachdem ich durch Aufrufen überprüft habe, ob es existiert next().
Giorgio
15
@Giorgio, es gibt keine Möglichkeit zu wissen, ob ein anderes Element vorhanden ist, ohne den Code auszuführen, der es generiert (Sie wissen nicht, ob der Generator ausgeführt wird yieldoder nicht). Es ist natürlich nicht schwierig, einen Adapter zu schreiben, der das Ergebnis von next()und liefert has_next()und liefert move_next().
Avakar
5
Dieselbe Idee könnte verwendet werden, um die hasNext()Methode zu implementieren (um bei Erfolg true zu erzeugen, zwischenzuspeichern und zurückzugeben oder bei einem Fehler false zurückzugeben). Dann beides hasNext()und next()würde von einer gemeinsamen zugrunde liegenden getNext()Methode und einem zwischengespeicherten Element abhängen . Ich verstehe wirklich nicht, warum next()es nicht in der Standardbibliothek sein sollte, wenn es so einfach ist, einen Adapter zu implementieren, der es bereitstellt.
Giorgio
3
@LarsH: Du meinst zB einen Iterator, der aus einer Datei liest, die beim Lesen geändert werden kann? Ich bin damit einverstanden, dass dies ein Problem sein kann (das sich auf jede Bibliotheksbereitstellung next()und hasNext()-methode auswirkt , nicht nur auf eine hypothetische Python-Bibliothek). Also ja, next()und hasNext()wird schwierig, wenn der Inhalt des gescannten Streams davon abhängt, wann Elemente gelesen werden.
Giorgio
238
Es gibt eine Alternative zur StopIterationVerwendung next(iterator, default_value).
Zum Beispiel:
>>> a = iter('hi')>>>print next(a,None)
h
>>>print next(a,None)
i
>>>print next(a,None)None
Sie können also einen Noneoder einen anderen vordefinierten Wert für das Ende des Iterators erkennen, wenn Sie die Ausnahmemethode nicht möchten.
Wenn Sie None als "Sentinel" verwenden, stellen Sie am besten sicher, dass Ihr Iterator keine Nones hat. Sie könnten auch tun sentinel = object()und next(iterator, sentinel)und Test mit is.
Sam Boosalis
1
Nach @samboosalis würde ich lieber ein eingebautes unittest.mock.sentinelObjekt verwenden, mit dem Sie ein explizites next(a, sentinel.END_OF_ITERATION)und dannif next(...) == sentinel.END_OF_ITERATION
ClementWalter
Dies ist schöner als die Ausnahme
datdinhquoc
39
Wenn Sie wirklich eine Funktionalität benötigenhas-next (weil Sie beispielsweise nur einen Algorithmus aus einer Referenzimplementierung in Java originalgetreu transkribieren oder weil Sie einen Prototyp schreiben, der nach Abschluss einfach in Java transkribiert werden muss), ist dies einfach Erhalten Sie es mit einer kleinen Wrapper-Klasse. Beispielsweise:
class hn_wrapper(object):def __init__(self, it):
self.it = iter(it)
self._hasnext =Nonedef __iter__(self):return self
def next(self):if self._hasnext:
result = self._thenext
else:
result = next(self.it)
self._hasnext =Nonereturn result
def hasnext(self):if self._hasnext isNone:try: self._thenext = next(self.it)exceptStopIteration: self._hasnext =Falseelse: self._hasnext =Truereturn self._hasnext
jetzt so etwas wie
x = hn_wrapper('ciao')while x.hasnext():print next(x)
emittiert
c
i
a
o
nach Bedarf.
Beachten Sie, dass für die Verwendung next(sel.it)als integriertes Programm Python 2.6 oder besser erforderlich ist. Wenn Sie eine ältere Version von Python verwenden, verwenden Sie self.it.next()stattdessen (und ähnlich wie next(x)im Beispiel). [[Sie könnten vernünftigerweise denken, dass dieser Hinweis überflüssig ist, da Python 2.6 bereits seit über einem Jahr verfügbar ist - aber meistens, wenn ich Python 2.6-Funktionen in einer Antwort verwende, fühlen sich einige Kommentatoren oder andere verpflichtet, darauf hinzuweisen dass es sich um 2.6-Features handelt, daher versuche ich, solchen Kommentaren einmal zuvorzukommen ;-)]]
"Ein Algorithmus aus einer Referenzimplementierung in Java originalgetreu transkribieren" ist der schlimmste Grund, eine has_nextMethode zu benötigen . Das Design von Python macht es beispielsweise unmöglich zu filterüberprüfen, ob ein Array ein Element enthält, das mit einem bestimmten Prädikat übereinstimmt. Die Arroganz und Kurzsichtigkeit der Python-Community ist atemberaubend.
Jonathan Cast
nette Antwort, ich
kopiere
Ich bin mit Python3 und dieser Code gibt mirTypeError: iter() returned non-iterator
madtyn
1
@ JonathanCast nicht sicher, ob ich folge. In Python würden Sie normalerweise mapund anyanstelle von verwenden filter, aber Sie könnten a verwenden SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELoder vergessen SENTINELund einfach das verwenden try: exceptund abfangen StopIteration.
juanpa.arrivillaga
13
Zusätzlich zu allen Erwähnungen von StopIteration macht die Python "for" -Schleife einfach das, was Sie wollen:
>>> it = iter("hello")>>>for i in it:...print i
...
h
e
l
l
o
Ich habe mich immer gefragt, warum Python auf der Erde all diese __ xxx __ Methoden hat. Sie scheinen so hässlich.
mP.
6
Berechtigte Frage! Normalerweise ist es die Syntax für Methoden, die von einer eingebauten Funktion verfügbar gemacht werden (z. B. len, ruft tatsächlich len auf ). Eine solche eingebaute Funktion existiert nicht für length_hint, aber es handelt sich tatsächlich um einen ausstehenden Vorschlag (PEP424).
Fulmicoton
1
@mP. Diese Funktionen sind vorhanden, da sie manchmal benötigt werden. Sie sind absichtlich hässlich, weil sie als Methode des letzten Auswegs angesehen werden: Wenn Sie sie verwenden, wissen Sie, dass Sie etwas tun, das nicht pythonisch und potenziell gefährlich ist (was möglicherweise auch zu jedem Zeitpunkt nicht mehr funktioniert).
Arne Babenhauserheide
Wie __init__und __main__? Imho, es ist ein bisschen chaotisch, egal du versuchst es zu rechtfertigen.
user1363990
5
hasNextübersetzt etwas in die StopIterationAusnahme, zB:
>>> it = iter("hello")>>> it.next()'h'>>> it.next()'e'>>> it.next()'l'>>> it.next()'l'>>> it.next()'o'>>> it.next()Traceback(most recent call last):File"<stdin>", line 1,in<module>StopIteration
Der Anwendungsfall, der mich dazu veranlasst hat, danach zu suchen, ist der folgende
def setfrom(self,f):"""Set from iterable f"""
fi = iter(f)for i in range(self.n):try:
x = next(fi)exceptStopIteration:
fi = iter(f)
x = next(fi)
self.a[i]= x
wo hasnext () verfügbar ist, könnte man tun
def setfrom(self,f):"""Set from iterable f"""
fi = iter(f)for i in range(self.n):ifnot hasnext(fi):
fi = iter(f)# restart
self.a[i]= next(fi)
was für mich sauberer ist. Natürlich können Sie Probleme umgehen, indem Sie Dienstprogrammklassen definieren. In diesem Fall gibt es jedoch eine Vielzahl von zwanzig verschiedenen, fast gleichwertigen Problemumgehungen mit jeweils eigenen Macken. Wenn Sie Code wiederverwenden möchten, der unterschiedliche Problemumgehungen verwendet, müssen Sie dies auch tun Sie können mehrere nahezu gleichwertige Elemente in Ihrer einzelnen Anwendung verwenden oder Code durchlesen und neu schreiben, um denselben Ansatz zu verwenden. Die Maxime "Mach es einmal und mach es gut" schlägt schlecht fehl.
Darüber hinaus muss der Iterator selbst eine interne 'hasnext'-Prüfung durchführen, um festzustellen, ob eine Ausnahme ausgelöst werden muss. Diese interne Prüfung wird dann ausgeblendet, sodass sie getestet werden muss, indem versucht wird, ein Element abzurufen, die Ausnahme abzufangen und den Handler auszuführen, wenn er ausgelöst wird. Dies ist unnötig, IMO zu verstecken.
Vorgeschlagener Weg ist StopIteration . Bitte beachten Sie das Fibonacci-Beispiel von tutorialspoint
#!usr/bin/python3import sys
def fibonacci(n):#generator function
a, b, counter =0,1,0whileTrue:if(counter > n):returnyield a
a, b = b, a + b
counter +=1
f = fibonacci(5)#f is iterator objectwhileTrue:try:print(next(f), end=" ")exceptStopIteration:
sys.exit()
Die Art und Weise, wie ich mein Problem gelöst habe, besteht darin, die Anzahl der bisher iterierten Objekte beizubehalten. Ich wollte eine Menge mit Aufrufen einer Instanzmethode durchlaufen. Da ich die Länge des Sets und die Anzahl der bisher gezählten Gegenstände kannte, hatte ich effektiv einehasNext Methode.
Natürlich ist das Beispiel ein Spielzeug, aber Sie bekommen die Idee. Dies funktioniert nicht in Fällen, in denen es nicht möglich ist, die Länge des Iterables zu ermitteln, wie z. B. bei einem Generator usw.
Antworten:
Nein, es gibt keine solche Methode. Das Ende der Iteration wird durch eine Ausnahme angezeigt. Siehe die Dokumentation .
quelle
unnext()
Methode gäbe , um das erste Element zurückzusetzen, nachdem ich durch Aufrufen überprüft habe, ob es existiertnext()
.yield
oder nicht). Es ist natürlich nicht schwierig, einen Adapter zu schreiben, der das Ergebnis vonnext()
und lieferthas_next()
und liefertmove_next()
.hasNext()
Methode zu implementieren (um bei Erfolg true zu erzeugen, zwischenzuspeichern und zurückzugeben oder bei einem Fehler false zurückzugeben). Dann beideshasNext()
undnext()
würde von einer gemeinsamen zugrunde liegendengetNext()
Methode und einem zwischengespeicherten Element abhängen . Ich verstehe wirklich nicht, warumnext()
es nicht in der Standardbibliothek sein sollte, wenn es so einfach ist, einen Adapter zu implementieren, der es bereitstellt.next()
undhasNext()
-methode auswirkt , nicht nur auf eine hypothetische Python-Bibliothek). Also ja,next()
undhasNext()
wird schwierig, wenn der Inhalt des gescannten Streams davon abhängt, wann Elemente gelesen werden.Es gibt eine Alternative zur
StopIteration
Verwendungnext(iterator, default_value)
.Zum Beispiel:
Sie können also einen
None
oder einen anderen vordefinierten Wert für das Ende des Iterators erkennen, wenn Sie die Ausnahmemethode nicht möchten.quelle
sentinel = object()
undnext(iterator, sentinel)
und Test mitis
.unittest.mock.sentinel
Objekt verwenden, mit dem Sie ein explizitesnext(a, sentinel.END_OF_ITERATION)
und dannif next(...) == sentinel.END_OF_ITERATION
Wenn Sie wirklich eine Funktionalität benötigen
has-next
(weil Sie beispielsweise nur einen Algorithmus aus einer Referenzimplementierung in Java originalgetreu transkribieren oder weil Sie einen Prototyp schreiben, der nach Abschluss einfach in Java transkribiert werden muss), ist dies einfach Erhalten Sie es mit einer kleinen Wrapper-Klasse. Beispielsweise:jetzt so etwas wie
emittiert
nach Bedarf.
Beachten Sie, dass für die Verwendung
next(sel.it)
als integriertes Programm Python 2.6 oder besser erforderlich ist. Wenn Sie eine ältere Version von Python verwenden, verwenden Sieself.it.next()
stattdessen (und ähnlich wienext(x)
im Beispiel). [[Sie könnten vernünftigerweise denken, dass dieser Hinweis überflüssig ist, da Python 2.6 bereits seit über einem Jahr verfügbar ist - aber meistens, wenn ich Python 2.6-Funktionen in einer Antwort verwende, fühlen sich einige Kommentatoren oder andere verpflichtet, darauf hinzuweisen dass es sich um 2.6-Features handelt, daher versuche ich, solchen Kommentaren einmal zuvorzukommen ;-)]]quelle
has_next
Methode zu benötigen . Das Design von Python macht es beispielsweise unmöglich zufilter
überprüfen, ob ein Array ein Element enthält, das mit einem bestimmten Prädikat übereinstimmt. Die Arroganz und Kurzsichtigkeit der Python-Community ist atemberaubend.TypeError: iter() returned non-iterator
map
undany
anstelle von verwendenfilter
, aber Sie könnten a verwendenSENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINEL
oder vergessenSENTINEL
und einfach das verwendentry: except
und abfangenStopIteration
.Zusätzlich zu allen Erwähnungen von StopIteration macht die Python "for" -Schleife einfach das, was Sie wollen:
quelle
Probieren Sie die Methode __length_hint __ () für jedes Iteratorobjekt aus:
quelle
__init__
und__main__
? Imho, es ist ein bisschen chaotisch, egal du versuchst es zu rechtfertigen.hasNext
übersetzt etwas in dieStopIteration
Ausnahme, zB:StopIteration
Dokumente: http://docs.python.org/library/exceptions.html#exceptions.StopIterationquelle
Sie können
tee
den Iterator mititertools.tee
undStopIteration
auf dem Teed-Iterator überprüfen .quelle
Nein. Das ähnlichste Konzept ist höchstwahrscheinlich eine StopIteration-Ausnahme.
quelle
Ich glaube, Python hat nur next () und laut Dokument gibt es eine Ausnahme, wenn es keine weiteren Elemente gibt.
http://docs.python.org/library/stdtypes.html#iterator-types
quelle
Der Anwendungsfall, der mich dazu veranlasst hat, danach zu suchen, ist der folgende
wo hasnext () verfügbar ist, könnte man tun
was für mich sauberer ist. Natürlich können Sie Probleme umgehen, indem Sie Dienstprogrammklassen definieren. In diesem Fall gibt es jedoch eine Vielzahl von zwanzig verschiedenen, fast gleichwertigen Problemumgehungen mit jeweils eigenen Macken. Wenn Sie Code wiederverwenden möchten, der unterschiedliche Problemumgehungen verwendet, müssen Sie dies auch tun Sie können mehrere nahezu gleichwertige Elemente in Ihrer einzelnen Anwendung verwenden oder Code durchlesen und neu schreiben, um denselben Ansatz zu verwenden. Die Maxime "Mach es einmal und mach es gut" schlägt schlecht fehl.
Darüber hinaus muss der Iterator selbst eine interne 'hasnext'-Prüfung durchführen, um festzustellen, ob eine Ausnahme ausgelöst werden muss. Diese interne Prüfung wird dann ausgeblendet, sodass sie getestet werden muss, indem versucht wird, ein Element abzurufen, die Ausnahme abzufangen und den Handler auszuführen, wenn er ausgelöst wird. Dies ist unnötig, IMO zu verstecken.
quelle
Vorgeschlagener Weg ist StopIteration . Bitte beachten Sie das Fibonacci-Beispiel von tutorialspoint
quelle
Die Art und Weise, wie ich mein Problem gelöst habe, besteht darin, die Anzahl der bisher iterierten Objekte beizubehalten. Ich wollte eine Menge mit Aufrufen einer Instanzmethode durchlaufen. Da ich die Länge des Sets und die Anzahl der bisher gezählten Gegenstände kannte, hatte ich effektiv eine
hasNext
Methode.Eine einfache Version meines Codes:
Natürlich ist das Beispiel ein Spielzeug, aber Sie bekommen die Idee. Dies funktioniert nicht in Fällen, in denen es nicht möglich ist, die Länge des Iterables zu ermitteln, wie z. B. bei einem Generator usw.
quelle