Si range () es un generador en Python 3.3, ¿por qué no puedo llamar a next () en un rango?

84

Quizás haya sido víctima de información errónea en la web, pero creo que es más probable que haya entendido mal algo. Según lo que he aprendido hasta ahora, range () es un generador, y los generadores se pueden usar como iteradores. Sin embargo, este código:

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

me da este error:

TypeError: 'range' object is not an iterator

¿Que me estoy perdiendo aqui? Esperaba que esto imprimiera 0 y avanzara al siguiente valor en myrange. Soy nuevo en Python, así que acepte mis disculpas por la pregunta bastante básica, pero no pude encontrar una buena explicación en ningún otro lugar.

Jeff
fuente
2
Consulte stackoverflow.com/q/13054057/395760 para ver la distinción entre iteradores y cosas sobre las que puede iterar en un forbucle.
1
¿Sería correcto decir que los generadores son iterables, pero no iteradores?
Jeff
4
@Jeff Iterables son objetos que iterse pueden usar para obtener un iterador. Los iteradores son objetos que se pueden iterar mediante el uso de next. Generadores es una categoría de iteradores (funciones generadoras y expresiones generadoras). Al menos eso es lo que creo ...
Oleh Prypin

Respuestas:

109

rangees una clase de objetos iterables inmutables. Su comportamiento de iteración se puede comparar con lists: no se puede llamar nextdirectamente a ellos; tienes que obtener un iterador usando iter.

Entonces no, rangeno es un generador.

Puede estar pensando, "¿por qué no lo hicieron directamente iterable"? Bueno, los ranges tienen algunas propiedades útiles que no serían posibles de esa manera:

  • Son inmutables, por lo que se pueden utilizar como claves de diccionario.
  • Tienen las start, stopy los stepatributos (desde Python 3.3), county indexlos métodos y apoyan in, leny __getitem__las operaciones.
  • Puede iterar sobre el mismo rangevarias veces.

>>> 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
Oleh Prypin
fuente
11
Otra característica range__contains__5 in range(10) => True
interesante
Gracias por la respuesta; esto tiene sentido ahora. Lo único que quiero aclarar antes de aceptar su respuesta es la nota en cursiva aproximadamente un tercio del camino hacia abajo en esta página, que dice que "en Python 3 range () es un generador". ¿Es esto simplemente incorrecto?
Jeff
3
@Jeff Estrictamente hablando, sí, está mal. El autor de la nota probablemente quiso decir que en Python 3 rangees vago (en comparación con Python 2, donde es solo una función que devuelve una lista).
Oleh Prypin
6
Además: range(0,10,3)[3]y 9 in range(0,10,3). El rango es una lista bastante perezosa.
Lennart Regebro
2
@ user3079275 "iterable directamente" es un nombre inapropiado, que en realidad significa "iterador". Los iteradores tienen un estado interno y, por lo tanto, son mutables por definición. "Iterable" es un objeto, ya sea mutable o no, que puede producir un iterador. Incluso los objetos mutables no suelen ser iteradores en sí mismos, sino que producen iteradores de manera reutilizable (por ejemplo, puede iterar sobre la misma lista en dos lugares diferentes de forma independiente, utilizando dos iteradores).
Oleh Prypin