2009-12-27 16 views
91

Haben nicht Python-Iteratoren eine Methode?hasNext in Python-Iteratoren?

+0

Related: [Wie weiß ich, ob ein Generator von Anfang an leer ist?] (Http: // stackoverflow.com/questions/661603/how-do-i-know-if-a-generator-is-empty-from-the-start) – user2314737

Antwort

64

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

+50

"Es ist einfacher um Vergebung zu bitten als um Erlaubnis." –

+66

"Es ist einfacher, nach Vergebung zu fragen als nach Erlaubnis.": Die Überprüfung, ob ein Iterator ein nächstes Element hat, verlangt nicht nach einer Erlaubnis. 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 überprüft habe, dass es existiert, indem ich 'next()' aufruft. – Giorgio

+9

@Giorgio, es gibt keine Möglichkeit zu wissen, ob ein anderes Element existiert, ohne den Code auszuführen, der es generiert (Sie wissen nicht, ob der Generator "yield" ausführen wird oder nicht). Es ist natürlich nicht schwierig, einen Adapter zu schreiben, der das Ergebnis von 'next() speichert und' has_next() 'und' move_next() 'liefert. – avakar

3

Nein, die am ähnlichsten Konzept ist höchstwahrscheinlich ein StopIteration exception.

+7

Welche Python verwendet Ausnahmen für den Kontrollfluss? Klingt ziemlich gut. –

+4

Rechts: Ausnahmen sollten verwendet werden, um Fehler zu behandeln, nicht um den normalen Ablauf der Kontrolle zu definieren. – Giorgio

5

hasNext übersetzt etwas nach StopIteration Ausnahme, 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 
28

Wenn Sie wirklich brauche ein has-next Funktionalität (weil Sie nur treu einen Algorithmus von einer Referenzimplementierung in Java, sagen, oder, weil Sie einen Prototyp schreiben, der wird müssen einfach in Java geschrieben werden, wenn es fertig ist), ist es einfach, es zu erhalten mit einer kleinen Wrapperklasse. Zum Beispiel:

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 die Verwendung von next(sel.it) als integrierte Python 2.6 oder besser erfordert; Wenn Sie eine ältere Version von Python verwenden, verwenden Sie stattdessen self.it.next() (und analog für next(x) in der Beispielverwendung). [Sie könnten vernünftigerweise denken, dass diese Anmerkung redundant ist, da Python 2.6 schon seit über einem Jahr existiert - aber meistens, wenn ich Python 2.6 Features in einer Antwort verwende, fühlt sich irgendein Kommentator oder andere verpflichtet, darauf hinzuweisen dass sie sind 2.6-Funktionen, damit solche Kommentare zu verhindern einmal ;-)]]

+1

"treu einen Algorithmus von einer Referenzimplementierung in Java transkribieren" ist der schlechteste Grund, eine 'has_next' Methode zu benötigen. Das Design von Python macht es unmöglich, beispielsweise 'filter' zu verwenden, um zu überprüfen, ob ein Array ein Element enthält, das einem gegebenen Prädikat entspricht. Die Arroganz und Kurzsichtigkeit der Python-Community ist atemberaubend. –

+0

nette Antwort, ich kopiere dies für die Illustration eines Design-Muster aus Java-Code – madtyn

+0

Ich bin mit Python3 und dieser Code gibt mir 'TypeError: iter() zurückgegeben non-iterator' – madtyn

8

Neben alle Erwähnungen von StopIteration, der Python „für“ Schleife einfach nicht, was Sie wollen ich versuche:

>>> it = iter("hello") 
>>> for i in it: 
...  print i 
... 
h 
e 
l 
l 
o 
3

können Sie tee den Iterator, itertools.tee, und prüfen, ob StopIteration auf der teed Iterator.

8

die __length_hint __() -Methode von jedem Iteratorobjekt Versuchen:

iter(...).__length_hint__() > 0 
+4

Ich habe mich immer gefragt, warum auf der Erde Python hat all diese __ xxx __ Methoden? Sie scheinen so hässlich. –

+6

Berechtigte Frage! Normalerweise ist es die Syntax für Methoden, die von einer eingebauten Funktion (z. B. len, tatsächlich ruft __len__) ausgesetzt sind. 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 da, weil sie manchmal benötigt werden. Sie sind absichtlich hässlich, weil sie als Methode des letzten Ausweges betrachtet werden: Wenn Sie sie verwenden, wissen Sie, dass Sie etwas Nicht-Pythisches und potenziell Gefährliches tun (das auch irgendwann aufhören kann zu arbeiten). –

120

zum StopIteration eine Alternative gibt es von next(iterator, default_value) verwenden.

Für exapmle:

>>> a = iter('hi') 
>>> print next(a, None) 
h 
>>> print next(a, None) 
i 
>>> print next(a, None) 
None 

So können Sie erkennen, für None oder anderen vorgegebenen Wert für Ende des Iterators, wenn Sie die Ausnahme Art und Weise nicht wollen.

+43

Wenn Sie None als "Sentinel" verwenden, stellen Sie sicher, dass Ihr Iterator keine Nones hat. Sie könnten auch 'sentinel = object()' und 'next (iterator, sentinel)' machen und mit 'is' testen. –

1

Der Anwendungsfall, der mich dafür zu suchen, führen die folgenden

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, ein

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) 

tun konnte, um mir die sauberer ist. Offensichtlich können Sie Probleme umgehen, indem Sie Hilfsklassen definieren. Was dann passiert, ist, dass Sie eine Vielzahl von fast 20 verschiedenen Workarounds mit ihren Macken haben. Wenn Sie Code mit verschiedenen Workarounds wiederverwenden möchten, müssen Sie beides tun Sie können in Ihrer einzelnen Anwendung mehrere nahezu gleichwertige Elemente verwenden oder Code durchlesen und neu schreiben, um denselben Ansatz zu verwenden. Die Maxime "Tu es einmal und tu es gut" versagt schlecht.

Darüber hinaus muss der Iterator selbst eine interne 'Hasnext'-Prüfung ausführen, um zu sehen, 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 ein unnötiges Verbergen von IMO.

0

guter Ansatz für eine solche Frage/Probleme überprüfen, was wir in dir (Objekt/Methode/Iterator/Typ/Klasse/...)

Sie werden sehen, dass dir(iterator) Rückkehr __length_hint__

und iterator.__length_hint__() haben ist bis zum Ende der Iteration positiv.

das ist es.

+0

'__length_hint__' kann nicht garantiert werden: https://www.python.org/dev/peps/pep-0424/. –

0

Ich mag diese:

While(True): 
    try: 
     # Do something with the next value 
     iterator.next() 
    except StopIteration: 
     break 
+6

Warum nicht einfach 'for ... in' verwenden? – bfontaine

0

Die Art, wie ich mein Problem die Zählung der Anzahl von Objekten, iteriert so weit zu halten gelöst ist. Ich wollte über einen Satz mit Aufrufen einer Instanzmethode iterieren. Da ich die Länge des Sets und die Anzahl der bisher gezählten Artikel kannte, hatte ich effektiv eine Methode.

Eine einfache Version meines Code:

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 Spielzeug ein, aber Sie bekommen die Idee. Dies funktioniert nicht in Fällen, in denen es keine Möglichkeit gibt, die Iterationslänge zu erhalten, wie ein Generator usw.

Verwandte Themen