Was sind "Iterable", "Iterator" und "Iteration" in Python? Wie sind sie definiert?
Antworten
Zu viele Anzeigen?Iteration ist ein allgemeiner Begriff für die Entnahme eines jeden Teils von etwas, eines nach dem anderen. Jedes Mal, wenn Sie eine explizite oder implizite Schleife verwenden, um eine Gruppe von Elementen zu durchlaufen, ist das eine Iteration.
In Python, iterierbar y Iterator haben bestimmte Bedeutungen.
Eine iterierbar ist ein Objekt, das eine __iter__
Methode, die eine Iterator oder die eine __getitem__
Methode, die sequentielle Indizes von Null an annehmen kann (und eine IndexError
wenn die Indizes nicht mehr gültig sind). Also eine iterierbar ist ein Objekt, mit dem Sie eine Iterator von.
Eine Iterator ist ein Objekt mit einer next
(Python 2) oder __next__
(Python 3) Methode.
Wann immer Sie eine for
Schleife, oder map
oder ein Listenverständnis, usw. in Python, die next
Methode wird automatisch aufgerufen, um jedes Element aus der Iterator und durchläuft somit den Prozess der Iteration .
Ein guter Ort, um mit dem Lernen zu beginnen, ist die Abschnitt Iteratoren des Tutorials y el Iterator-Typen auf der Seite mit den Standardtypen . Nachdem Sie die Grundlagen verstanden haben, versuchen Sie die Iteratoren im Abschnitt Funktionale Programmierung des HOWTO .
Hier ist die Erklärung, die ich im Python-Unterricht verwende:
Ein ITERABLE ist:
- alles, was in einer Schleife durchlaufen werden kann (z. B. eine Zeichenkette oder eine Datei) oder
- alles, was auf der rechten Seite einer for-Schleife erscheinen kann:
for x in iterable: ...
o - alles, was Sie aufrufen können mit
iter()
die einen ITERATOR zurückgibt:iter(obj)
o - ein Objekt, das definiert
__iter__
die einen neuen ITERATOR zurückgibt, oder es kann eine__getitem__
Methode, die für die indizierte Suche geeignet ist.
Ein ITERATOR ist ein Objekt:
- mit einem Zustand, der sich merkt, wo er sich während der Iteration befindet,
- mit einer
__next__
Methode, die:- gibt den nächsten Wert in der Iteration zurück
- aktualisiert den Status, so dass er auf den nächsten Wert zeigt
- signalisiert, wenn dies geschieht, indem er
StopIteration
- und das ist selbstaufrufbar (was bedeutet, dass es eine
__iter__
Methode, die Folgendes zurückgibtself
).
Notes :
- Le site
__next__
Methode in Python 3 wird buchstabiertnext
in Python 2, und - Die eingebaute Funktion
next()
ruft diese Methode für das übergebene Objekt auf.
Zum Beispiel:
>>> s = 'cat' # s is an ITERABLE
# s is a str object that is immutable
# s has no state
# s has a __getitem__() method
>>> t = iter(s) # t is an ITERATOR
# t has state (it starts by pointing at the "c"
# t has a next() method and an __iter__() method
>>> next(t) # the next() function returns the next value and advances the state
'c'
>>> next(t) # the next() function returns the next value and advances
'a'
>>> next(t) # the next() function returns the next value and advances
't'
>>> next(t) # next() raises StopIteration to signal that iteration is complete
Traceback (most recent call last):
...
StopIteration
>>> iter(t) is t # the iterator is self-iterable
Die obigen Antworten sind großartig, aber wie die meisten, die ich gesehen habe, betonen sie nicht die Auszeichnung genug für Leute wie mich.
Außerdem neigen die Leute dazu, "zu pythonisch" zu werden, indem sie Definitionen wie "X ist ein Objekt, das eine __foo__()
Methode" vor. Solche Definitionen sind richtig - sie beruhen auf der Philosophie des "Duck-Typing", aber die Konzentration auf die Methoden gerät in den Hintergrund, wenn man versucht, das Konzept in seiner Einfachheit zu verstehen.
Ich füge also meine Version hinzu.
In natürlicher Sprache,
- Iteration ist der Prozess, bei dem ein Element nach dem anderen in einer Reihe von Elementen genommen wird.
In Python,
-
iterierbar ist ein Objekt, das, nun ja, iterierbar ist, was einfach ausgedrückt bedeutet, dass es in einer Iteration verwendet werden kann, z.B. mit einer
for
Schleife. Und wie? Durch die Verwendung von Iterator . Ich werde es weiter unten erklären. -
... während Iterator ist ein Objekt, das definiert wie man es tatsächlich macht die Iteration - insbesondere was ist die nächste Element. Deshalb muss es Folgendes haben
next()
Methode.
Iteratoren sind selbst auch iterierbar, mit dem Unterschied, dass ihre __iter__()
Methode gibt das gleiche Objekt zurück ( self
), unabhängig davon, ob seine Elemente durch frühere Aufrufe von next()
.
Was denkt der Python-Interpreter also, wenn er sieht for x in obj:
Aussage?
Schau, ein
for
Schleife. Sieht aus wie ein Job für einen Iterator... Besorgen wir uns einen. ... Da ist diesobj
also fragen wir ihn."Mr.
obj
Haben Sie Ihren Iterator?" (... Anrufeiter(obj)
, die dieobj.__iter__()
der fröhlich einen glänzenden neuen Iterator ausgibt_i
.)OK, das war einfach... Dann lasst uns mit der Iteration beginnen. (
x = _i.next()
...x = _i.next()
...)
Da Mr. obj
diesen Test bestanden hat (indem eine bestimmte Methode einen gültigen Iterator zurückgibt), belohnen wir ihn mit einem Adjektiv: Sie können ihn jetzt "iterable Mr." nennen. obj
".
In einfachen Fällen ist es jedoch normalerweise nicht von Vorteil, Iterator und Iterable getrennt zu haben. Sie definieren also nur einer Objekt, das auch sein eigener Iterator ist. (Python kümmert sich nicht wirklich darum, dass _i
ausgehändigt von obj
war nicht besonders glänzend, sondern nur die obj
selbst).
Das ist der Grund, warum ich in den meisten Beispielen, die ich gesehen habe (und was mich immer wieder verwirrt hat), zu sehen:
class IterableExample(object):
def __iter__(self):
return self
def next(self):
pass
anstelle von
class Iterator(object):
def next(self):
pass
class Iterable(object):
def __iter__(self):
return Iterator()
Es gibt jedoch Fälle, in denen es von Vorteil ist, wenn der Iterator von der Iterable getrennt ist, z. B. wenn Sie eine Reihe von Elementen, aber mehrere "Cursors" haben möchten. Wenn Sie zum Beispiel mit "aktuellen" und "zukünftigen" Elementen arbeiten wollen, können Sie für beide getrennte Iteratoren haben. Oder mehrere Threads, die auf eine große Liste zugreifen: jeder kann seinen eigenen Iterator haben, um alle Elemente zu durchlaufen. Siehe @Raymond's y @glglgl's Antworten oben.
Stellen Sie sich vor, was Sie tun könnten:
class SmartIterableExample(object):
def create_iterator(self):
# An amazingly powerful yet simple way to create arbitrary
# iterator, utilizing object state (or not, if you are fan
# of functional), magic and nuclear waste--no kittens hurt.
pass # don't forget to add the next() method
def __iter__(self):
return self.create_iterator()
Notes :
-
Ich wiederhole es noch einmal: Iterator ist nicht iterierbar . Iterator kann nicht verwendet werden als eine "Quelle" in
for
Schleife. Wasfor
Schleife in erster Linie benötigt ist__iter__()
(das etwas zurückgibt mitnext()
). -
Ja, natürlich,
for
ist nicht die einzige Iterationsschleife, daher gilt das oben Gesagte auch für einige andere Konstrukte (while
...). -
Iterator's
next()
kann StopIteration auslösen, um die Iteration zu beenden. Das muss nicht sein, aber es kann für immer iterieren oder andere Mittel verwenden. -
In dem obigen "Gedankengang",
_i
nicht wirklich existiert. Ich habe mir diesen Namen ausgedacht. -
Es gibt eine kleine Änderung in Python 3.x:
next()
Methode (nicht die eingebaute) jetzt muss aufgerufen werden__next__()
. Ja, das hätte von Anfang an so sein sollen. -
Man kann es sich auch so vorstellen: iterable hat die Daten, iterator zieht das nächste Element
Haftungsausschluss: Ich bin kein Entwickler eines Python-Interpreters, also weiß ich nicht wirklich, was der Interpreter "denkt". Die obigen Überlegungen sind lediglich eine Demonstration dessen, wie ich das Thema aus anderen Erklärungen, Experimenten und realen Erfahrungen eines Python-Neulings verstehe.
Ein iterable ist ein Objekt, das eine __iter__()
Methode. Sie kann möglicherweise mehrmals wiederholt werden, z. B. list()
s und tuple()
s.
Ein Iterator ist ein Objekt, das eine Iteration durchführt. Er wird zurückgegeben von einem __iter__()
Methode, gibt sich selbst über ihre eigene __iter__()
Methode und hat eine next()
Methode ( __next__()
in 3.x).
Iteration ist der Prozess des Aufrufs dieser next()
bzw. __next__()
bis es aufsteigt StopIteration
.
Beispiel:
>>> a = [1, 2, 3] # iterable
>>> b1 = iter(a) # iterator 1
>>> b2 = iter(a) # iterator 2, independent of b1
>>> next(b1)
1
>>> next(b1)
2
>>> next(b2) # start over, as it is the first call to b2
1
>>> next(b1)
3
>>> next(b1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> b1 = iter(a) # new one, start over
>>> next(b1)
1
Hier ist mein Spickzettel:
sequence
+
|
v
def __getitem__(self, index: int):
+ ...
| raise IndexError
|
|
| def __iter__(self):
| + ...
| | return <iterator>
| |
| |
+--> or <-----+ def __next__(self):
+ | + ...
| | | raise StopIteration
v | |
iterable | |
+ | |
| | v
| +----> and +-------> iterator
| ^
v |
iter(<iterable>) +----------------------+
|
def generator(): |
+ yield 1 |
| generator_expression +-+
| |
+-> generator() +-> generator_iterator +-+
Quiz: Erkennst du, wie...
- jeder Iterator eine Iterable ist?
- eines Containerobjekts
__iter__()
Methode als Generator implementiert werden kann? - eine Iterable, die eine
__next__
Methode nicht unbedingt ein Iterator ist?
Antworten:
-
Jeder Iterator muss eine
__iter__
Methode. Unter__iter__
reicht aus, um eine Iterabilität zu sein. Daher ist jeder Iterator eine Iterable. -
Wenn
__iter__
aufgerufen wird, sollte einen Iterator zurückgeben (return <iterator>
im obigen Diagramm). Der Aufruf eines Generators gibt einen Generator-Iterator zurück, der ein Typ von Iterator ist.class Iterable1: def __iter__(self): # a method (which is a function defined inside a class body) # calling iter() converts iterable (tuple) to iterator return iter((1,2,3)) class Iterable2: def __iter__(self): # a generator for i in (1, 2, 3): yield i class Iterable3: def __iter__(self): # with PEP 380 syntax yield from (1, 2, 3) # passes assert list(Iterable1()) == list(Iterable2()) == list(Iterable3()) == [1, 2, 3]
-
Hier ist ein Beispiel:
class MyIterable: def __init__(self): self.n = 0 def __getitem__(self, index: int): return (1, 2, 3)[index] def __next__(self): n = self.n = self.n + 1 if n > 3: raise StopIteration return n # if you can iter it without raising a TypeError, then it's an iterable. iter(MyIterable()) # but obviously `MyIterable()` is not an iterator since it does not have # an `__iter__` method. from collections.abc import Iterator assert isinstance(MyIterable(), Iterator) # AssertionError
- See previous answers
- Weitere Antworten anzeigen