hasNext in Python-Iteratoren?

Antworten:

106

Nein, es gibt keine solche Methode. Das Ende der Iteration wird durch eine Ausnahme angezeigt. Siehe die Dokumentation .

Avakar
quelle
71
"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.

Derrick Zhang
quelle
69
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 = None
  def __iter__(self): return self
  def next(self):
    if self._hasnext:
      result = self._thenext
    else:
      result = next(self.it)
    self._hasnext = None
    return result
  def hasnext(self):
    if self._hasnext is None:
      try: self._thenext = next(self.it)
      except StopIteration: self._hasnext = False
      else: self._hasnext = True
    return 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 ;-)]]

Alex Martelli
quelle
9
"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
Brian Clapper
quelle
7

Probieren Sie die Methode __length_hint __ () für jedes Iteratorobjekt aus:

iter(...).__length_hint__() > 0
juj
quelle
5
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
miku
quelle
4

Sie können teeden Iterator mit itertools.teeund StopIterationauf dem Teed-Iterator überprüfen .

Sykora
quelle
3

Nein. Das ähnlichste Konzept ist höchstwahrscheinlich eine StopIteration-Ausnahme.

James Thompson
quelle
10
Welche Python verwendet Ausnahmen für den Kontrollfluss? Klingt ziemlich naft.
mP.
5
Rechts: Ausnahmen sollten verwendet werden, um Fehler zu behandeln und nicht um den normalen Kontrollfluss zu definieren.
Giorgio
1

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)
        except StopIteration:
            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):
        if not 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.

John Allsup
quelle
1
Für diesen Anwendungsfall können Sie itertools.cycle
eaglebrain
0

Vorgeschlagener Weg ist StopIteration . Bitte beachten Sie das Fibonacci-Beispiel von tutorialspoint

#!usr/bin/python3

import sys
def fibonacci(n): #generator function
   a, b, counter = 0, 1, 0
   while True:
      if (counter > n): 
         return
      yield a
      a, b = b, a + b
      counter += 1
f = fibonacci(5) #f is iterator object

while True:
   try:
      print (next(f), end=" ")
   except StopIteration:
      sys.exit()
Ramin Darvishov
quelle
-2

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.

Eine einfache Version meines Codes:

class Iterator:
    # s is a string, say
    def __init__(self, s):
        self.s = set(list(s))
        self.done = False
        self.iter = iter(s)
        self.charCount = 0

    def next(self):
        if self.done:
            return None
        self.char = next(self.iter)
        self.charCount += 1
        self.done = (self.charCount < len(self.s))
        return self.char

    def hasMore(self):
        return not self.done

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.

Forumulator
quelle