Wenn range () ein Generator in Python 3.3 ist, warum kann ich next () nicht für einen Bereich aufrufen?


84

Vielleicht bin ich Opfer von Fehlinformationen im Internet geworden, aber ich denke, es ist wahrscheinlicher, dass ich etwas falsch verstanden habe. Basierend auf dem, was ich bisher gelernt habe, ist range () ein Generator, und Generatoren können als Iteratoren verwendet werden. Dieser Code jedoch:

myrange = range(10)
print(next(myrange))

gibt mir diesen Fehler:

TypeError: 'range' object is not an iterator

Was vermisse ich hier? Ich hatte erwartet, dass dies 0 druckt und zum nächsten Wert in weitergeht myrange. Ich bin neu in Python. Bitte entschuldigen Sie die eher grundlegende Frage, aber ich konnte nirgendwo anders eine gute Erklärung finden.


2
Unter stackoverflow.com/q/13054057/395760 finden Sie die Unterscheidung zwischen Iteratoren und Dingen, über die Sie in einer forSchleife iterieren können .

1
Wäre es richtig zu sagen, dass Generatoren iterabel sind, aber keine Iteratoren?
Jeff

4
@ Jeff Iterables sind Objekte, itermit denen ein Iterator abgerufen werden kann. Iteratoren sind Objekte, die mithilfe von iteriert werden können next. Generatoren ist eine Kategorie von Iteratoren (Generatorfunktionen und Generatorausdrücke). Zumindest denke ich das ...
Oleh Prypin

Antworten:


109

rangeist eine Klasse unveränderlicher iterierbarer Objekte. Ihr Iterationsverhalten kann mit lists verglichen werden : Sie können sie nicht nextdirekt aufrufen ; Sie müssen einen Iterator mit verwenden iter.

Also nein, rangeist kein Generator.

Sie denken vielleicht: "Warum haben sie es nicht direkt iterierbar gemacht?" Nun, rangewir haben einige nützliche Eigenschaften, die auf diese Weise nicht möglich wären:

  • Sie sind unveränderlich und können daher als Wörterbuchschlüssel verwendet werden.
  • Sie haben die start, stopund stepAttribute (seit Python 3.3), countund indexMethoden , und sie unterstützen in, lenund __getitem__Operationen.
  • Sie können dasselbe rangemehrmals durchlaufen .

>>> myrange = range(1, 21, 2)
>>> myrange.start
1
>>> myrange.step
2
>>> myrange.index(17)
8
>>> myrange.index(18)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 18 is not in range
>>> it = iter(myrange)
>>> it
<range_iterator object at 0x7f504a9be960>
>>> next(it)
1
>>> next(it)
3
>>> next(it)
5

11
Ein weiteres nettes Merkmal von rangeObjekten ist, dass sie eine __contains__Methode haben , mit der getestet werden kann, ob ein Wert in einem Bereich liegt:5 in range(10) => True
Kindall

Danke für die Antwort; das macht jetzt Sinn. Das einzige, was ich klären möchte, bevor ich Ihre Antwort akzeptiere, ist der kursive Hinweis auf etwa einem Drittel dieser Seite, der besagt, dass "in Python 3 range () ein Generator ist". Ist das einfach falsch?
Jeff

3
@ Jeff Genau genommen ist es falsch. Der Verfasser des Vermerks wahrscheinlich dazu geführt, dass in Python 3 rangeist faul ( im Vergleich zu Python 2 , wo es nur eine Funktion , dass die Rendite einer Liste).
Oleh Prypin

6
Auch: range(0,10,3)[3]und 9 in range(0,10,3). Reichweite ist so ziemlich eine faule Liste.
Lennart Regebro

2
@ user3079275 "direkt iterierbar" ist eine falsche Bezeichnung und bedeutet eigentlich "Iterator". Iteratoren haben einen internen Zustand und sind daher per Definition veränderbar. "Iterierbar" ist ein Objekt, ob veränderlich oder nicht, das einen Iterator erzeugen kann. Selbst veränderbare Objekte sind normalerweise keine Iteratoren selbst, sondern erzeugen Iteratoren auf wiederverwendbare Weise (z. B. können Sie dieselbe Liste an zwei verschiedenen Stellen unabhängig voneinander mit zwei Iteratoren durchlaufen).
Oleh Prypin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.