2017-03-02 3 views
4

Also ich habe Iteratoren für eine Weile geschrieben, und ich dachte, dass ich sie verstanden habe. Aber ich habe heute Abend mit einigen Problemen zu kämpfen, und je mehr ich damit spiele, desto verwirrter werde ich.Das Iterator-Protokoll. Ist es dunkle Magie?

Ich dachte, dass Sie für einen Iterator implementieren mussten __iter__ und next (oder __next__). Und wenn Sie zuerst versuchten, über den Iterator zu iterieren, würde die __iter__ Methode aufgerufen werden, und dann würde next aufgerufen werden, bis eine StopIteration ausgelöst wurde.

Wenn ich diesen Code ausführen, obwohl

class Iter(object): 

    def __iter__(self): 
     return iter([2, 4, 6]) 

    def next(self): 
     for y in [1, 2, 3]: 
      return y 

iterable = Iter() 
for x in iterable: 
    print(x) 

Der Ausgang 2 4 6 ist. So wird __iter__ aufgerufen, aber nicht next. Das scheint mit der Dokumentation übereinzustimmen, die ich gefunden habe here. Aber dann wirft das eine Menge Fragen auf.

Spezifisch, was ist der Unterschied zwischen einem Containertyp und Iterator, wenn es nicht die Implementierung von next ist? Woher weiß ich vorher, in welcher Richtung meine Klasse behandelt wird? Und am wichtigsten, wenn ich eine Klasse schreiben möchte, wo meine next Methode aufgerufen wird, wenn ich for x in Iter() verwende, wie kann ich das tun?

Antwort

6

Eine Liste ist iterable, aber es ist nicht ein Iterator. Vergleichen und Kontrast:

>>> type([]) 
list 
>>> type(iter([])) 
list_iterator 

iter Aufruf auf einer Liste erstellt und gibt ein neues Iterator-Objekt für den Inhalt dieser Liste zu iterieren.

in Ihrem Objekt, kehren Sie einfach eine Liste Iterator, speziell einen Iterator über die Liste [2, 4, 6], so dass das Objekt weiß nichts über Elemente ergibt 1, 2, 3.

def __iter__(self): 
    return iter([2, 4, 6]) # <-- you're returning the list iterator, not your own 

Hier ist eine grundlegende Implementierung konform an das Iterator-Protokoll in Python 2, das die Dinge nicht verwirrt, indem es sich auf Listen-Iteratoren, Generatoren oder irgend etwas Besonderes verlässt.

class Iter(object): 

    def __iter__(self): 
     self.val = 0 
     return self 

    def next(self): 
     self.val += 1 
     if self.val > 3: 
      raise StopIteration 
     return self.val 
+0

Ich denke, ich komme wieder um. Daher sind alle Iteratoren iterabel, aber nicht alle Iteratoren sind Iteratoren. Und wenn ich über ein iterables iteriere, iteriere ich wirklich über alles, was seine iter-Methode zurückgibt. Was kann ein Iterator oder ein Generator? –

+0

Korrigieren. Alle Iteratoren sind iterierbar. Nicht alle Iterables sind Iteratoren (z. B. sind Listen iterierbar, aber sie sind keine Iteratoren, da sie "next" nicht implementieren).Die Iteration über ein Objekt wird auf alle Dinge ausgeführt, die zurückgegeben werden, indem 'iter() 'für das Objekt aufgerufen wird. Das * muss * einen Iterator zurückgeben (Generatoren sind eine Art Iterator). – wim

+0

Danke. Ich denke, das wusste ich zu Beginn der Nacht, aber ich dachte auch zu Beginn der Nacht, dachte aber auch an ein paar andere Dinge, die sehr, sehr falsch waren. –

2

Nach der Dokumentation, die Sie zu verknüpfen, eines __iter__ des Iteratormethode soll sich zurückzukehren. Daher ist Ihr Iterator überhaupt kein Iterator: Wenn for__iter__ aufruft, um den Iterator zu erhalten, geben Sie ihn iter([2,4,6]), aber Sie sollten ihn geben self.

(Auch ich glaube nicht, Ihre next Methode tut, was Sie beabsichtigen: es gibt 1 jedes Mal, es genannt wird, und wirft nie StopIteration Also, wenn Sie __iter__ fixiert, dann Iterator Iterator über einen unendlichen Strom wäre. von denen, anstatt über die endliche Liste [1, 2, 3]. Aber das ist ein Side-Problem.)

+0

Ja, ich verstehe, dass dieser Code Unsinn ist. Der Punkt, den ich mir selbst zu beweisen versuchte, ist, dass das Objekt, das iteriert wird, ist, was meine '__iter__' Methode zurückgibt, und dass meine' nächste' Methode überhaupt nie aufgerufen wird. Vielleicht bin ich nur müde und sollte ins Bett gehen, aber ich verstehe wirklich nicht, wie die "nächste" Methode jemals genannt wird. –

+0

@GreeTreePython: Die 'nächste' Methode, die aufgerufen wird, ist die' next' Methode des Iterators, die von '__iter__' zurückgegeben wird. – ruakh