2016-08-03 5 views
0

Ich versuche mehr über __iter__ in Python 3 zu verstehen. Aus irgendeinem Grund __getitem__ ist besser von mir als __iter__ verstanden. Ich denke, ich bekomme irgendwie die entsprechende nächste Implementierung gefolgt mit __iter__.Äquivalenter Code von __getitem__ in __iter__

ich diesen folgenden Code haben:

class Item: 
    def __getitem__(self,pos): 
     return range(0,30,10)[pos] 

item1= Item() 
print (f[1]) # 10 
for i in item1: 
    print (i) # 0 10 20 

Ich verstehe den Code oben, aber dann wieder, wie schreibe ich den entsprechenden Code __iter__ und __next__() mit?

class Item: 
    def __iter__(self): 
     return self 
     #Lost here 
    def __next__(self,pos): 
     #Lost here 

Ich verstehe, wenn eine Python __getitem__ Methode sieht, versucht er über das Objekt Iterieren durch das Verfahren mit dem ganzzahligen Index mit Aufruf 0 beginnen.

+0

auf einer realen Liste Versuchen. Die Funktion 'mylist .__ iter__' gibt einen Generator zurück. Sie können einige Schritte durch diesen Generator durchführen, indem Sie 'my_generator.next()' einige Male aufrufen. Ich würde es jedoch nicht als gleichwertig bezeichnen, da der Zugriff auf Listenelemente eine konstante Zeit ist, während die Iteration bis zu einem bestimmten Zeitpunkt eine lineare Zeit ist. –

+0

Was ist mit 'def __iter __ (self): return (x für x im Bereich (0,30,10))'? –

+0

@machineyearning Ich muss auch einen Index übergeben. (self, pos) – SeasonalShot

Antwort

5

Im Allgemeinen ist ein sehr guter Ansatz, __iter__ einen Generator von yield Werte zu machen. Dies könnte weniger intuitiv sein, aber es ist geradlinig; Sie liefern nur die Ergebnisse zurück Sie wollen und __next__ wird dann automatisch für Sie bereitgestellt:

class Item: 
    def __iter__(self): 
     for item in range(0, 30, 10): 
      yield item 

Dies ist nur die Kraft der yield verwendet den gewünschten Effekt zu erzielen, wenn Python Anrufe __iter__ auf Ihrem Objekt, erwartet er eine iterator zurück (dh ein Objekt, das __next__ Anrufe unterstützt), wird ein Generator nur, dass jedes Elements der Herstellung wie in Ihrer Generatorfunktion definiert (dh __iter__ in diesem Fall), wenn __next__ genannt wird:

>>> i = iter(Item())  
>>> print(i) # generator, supports __next__ 
<generator object __iter__ at 0x7f6aeaf9e6d0> 
>>> next(i) 
0 
>>> next(i) 
10 
>>> next(i) 
20 

Jetzt erhalten Sie den gleichen Effekt wie __getitem__. Der Unterschied besteht darin, dass kein index in geben wird, müssen Sie manuell durchlaufen sie, um das Ergebnis zu erhalten:

>>> for i in Item(): 
... print(i)  
0 
10 
20 

Davon abgesehen, gibt es zwei Alternativen für die Erstellung eines Objekts, das Iteration unterstützt.

Einmal Looping: Stellen Sie Artikel ein Iterator

durch die Definition __next__Item ein Iterator Stellen und self von __iter__ in diesem Fall zurückkehrt, da Sie yield die __iter__ Methode gibt self und __next__ die nicht verwenden Griffe Logik der Rückkehr Werte:

class Item: 

    def __init__(self): 
     self.val = 0 

    def __iter__(self): 
     return self 

    def __next__(self): 
     if self.val > 2: raise StopIteration 
     res = range(0, 30, 10)[self.val] 
     self.val += 1 
     return res 

Dies nutzt auch einen Hilfs val die resu zu erhalten lt aus dem Bereich und prüfen, ob wir noch laufen werden sollen (wenn nicht, wir erhöhen StopIteration):

>>> for i in Item(): 
... print(i) 
0 
10 
20 

Das Problem bei diesem Ansatz ist, dass es eine einmalige Fahrt nach einmaligem Durchlaufen, die self.val Punkte 3 und Iteration kann nicht erneut durchgeführt werden. (mit yield löst dieses Problem). (Ja, Sie könnten gehen und val auf 0 setzen, aber das ist nur hinterhältig.)

Viele Male Schleife: Erstellen Sie benutzerdefinierte Iterator-Objekt.

Der zweite Ansatz ist speziell für Ihre Item Klassenobjekt eine benutzerdefinierte Iterator zu verwenden und sie von Item.__iter__ statt self zurück:

class Item: 
    def __iter__(self): 
     return IterItem() 


class IterItem: 
    def __init__(self): 
     self.val = 0 
    def __iter__(self): 
     return self 
    def __next__(self): 
     if self.val > 2: raise StopIteration 
     res = range(0, 30, 10)[self.val] 
     self.val += 1 
     return res 

wird nun geliefert jedes Mal, wenn Sie eine neue benutzerdefinierte Iterator durchlaufen und Sie können unterstützt mehrere Iterationen über Item Objekte.

+1

Ich mag deine umfassende Antwort und deinen rauchenden Skelettavatar. –

0

Iter gibt einen Iterator, vor allem ein Generator als @machineyearning bei den Kommentaren gesagt, mit du das nächste Mal über das Objekt iterieren können, siehe das Beispiel:

class Item: 
    def __init__(self): 
     self.elems = range(10) 
     self.current = 0 

    def __iter__(self): 
     return (x for x in self.elems) 

    def __next__(self): 
     if self.current >= len(self.elems): 
      self.current = 0 
      raise StopIteration 
     return self.elems[self.current] 

>>> i = Item() 
>>> a = iter(i) 
>>> for x in a: 
...  print x 
...  
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
>>> for x in i: 
...  print x 
...  
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
+4

Es ist nicht angemessen, eine '__next__'-Methode zu verwenden, wenn Ihre' __iter__' -Methode etwas anderes zurückgibt als 'self'. Wenn Sie sowohl einen generatorbasierten "__iter__" als auch einen "__next__" haben, dann sind Sie sich nicht sicher, ob Sie ein Iterator sein sollen oder nicht. – Blckknght