"Es más fácil pedir perdón que permiso": comprobar si un iterador tiene un elemento siguiente no es pedir permiso. Hay situaciones en las que desea probar la existencia de un elemento siguiente sin consumirlo. Aceptaría la solución try catch si hubiera un unnext()método para volver a colocar el primer elemento después de haber comprobado que existe llamando next().
Giorgio
15
@Giorgio, no hay forma de saber si existe otro elemento sin ejecutar el código que lo genera (no sabes si el generador se ejecutará yieldo no). Por supuesto, no es difícil escribir un adaptador que almacene el resultado next()y proporcione has_next()y move_next().
avakar
55
La misma idea podría usarse para implementar el hasNext()método (para producir, almacenar en caché y devolver verdadero en caso de éxito o falso en caso de error). Entonces ambos hasNext()y next()dependerían de un getNext()método subyacente común y un elemento en caché. Realmente no veo por next()qué no debería estar en la biblioteca estándar si es tan fácil implementar un adaptador que lo proporciona.
Giorgio
3
@LarsH: ¿Te refieres, por ejemplo, a un iterador que lee de un archivo que se puede cambiar mientras se lee? Estoy de acuerdo en que esto puede ser un problema (que afecta a cualquier biblioteca que proporcione next()y hasNext()método, no solo una hipotética biblioteca de Python). Entonces sí, next()y se hasNext()vuelve complicado si el contenido de la secuencia que se escanea depende de cuándo se leen los elementos.
Giorgio el
239
Hay una alternativa al StopIterationuso next(iterator, default_value).
Por ejemplo:
>>> a = iter('hi')>>>print next(a,None)
h
>>>print next(a,None)
i
>>>print next(a,None)None
Por lo tanto, puede detectar Noneu otro valor preespecificado para el final del iterador si no desea la forma de excepción.
si usa None como "centinela", es mejor asegurarse de que su iterador no tenga Nones. también se puede hacer sentinel = object()y next(iterator, sentinel)y probar con is.
Sam Boosalis
1
siguiendo a @samboosalis, preferiría usar un unittest.mock.sentinelobjeto incorporado que le permita escribir un mensaje explícito next(a, sentinel.END_OF_ITERATION)y luegoif next(...) == sentinel.END_OF_ITERATION
ClementWalter
esto es más bonito que la excepción
datdinhquoc
El problema es que, de esta manera, también CONSUMES el siguiente valor del iterador. hasNext en Java no consume el siguiente valor.
Alan Franzoni
39
Si realmente necesita una has-nextfuncionalidad (porque solo está transcribiendo fielmente un algoritmo de una implementación de referencia en Java, por ejemplo, o porque está escribiendo un prototipo que deberá transcribirse fácilmente a Java cuando esté terminado), es fácil obténgalo con una pequeña clase de envoltura. Por ejemplo:
class hn_wrapper(object):def __init__(self, it):
self.it = iter(it)
self._hasnext =Nonedef __iter__(self):return self
def next(self):if self._hasnext:
result = self._thenext
else:
result = next(self.it)
self._hasnext =Nonereturn result
def hasnext(self):if self._hasnext isNone:try: self._thenext = next(self.it)exceptStopIteration: self._hasnext =Falseelse: self._hasnext =Truereturn self._hasnext
ahora algo como
x = hn_wrapper('ciao')while x.hasnext():print next(x)
emite
c
i
a
o
según sea necesario.
Tenga en cuenta que el uso de next(sel.it)como incorporado requiere Python 2.6 o superior; si está utilizando una versión anterior de Python, use self.it.next()en su lugar (y de manera similar para next(x)el uso de ejemplo). [[Puede pensar razonablemente que esta nota es redundante, ya que Python 2.6 ha existido por más de un año, pero la mayoría de las veces cuando uso las características de Python 2.6 en una respuesta, algunos comentaristas u otros se sienten obligados a señalar que son características 2.6, por lo que estoy tratando de evitar tales comentarios por una vez ;-)]]
"transcribir fielmente un algoritmo desde una implementación de referencia en Java" es la peor razón para necesitar un has_nextmétodo. El diseño de Python hace imposible, por ejemplo, usar filterpara verificar si una matriz contiene un elemento que coincide con un predicado dado. La arrogancia y la miopía de la comunidad de Python es asombrosa.
Jonathan Cast
respuesta agradable, estoy copiando esto para ilustrativa de algún patrón diseño tomado de código Java
madtyn
Estoy con Python3 y este código me daTypeError: iter() returned non-iterator
madtyn
1
@JonathanCast no estoy seguro de seguir. En Python, normalmente usaría mapy en anylugar de filter, pero podría usar SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELu olvidar ay SENTINELsimplemente usar try: excepty capturar el StopIteration.
juanpa.arrivillaga
13
Además de todas las menciones de StopIteration, el bucle "for" de Python simplemente hace lo que desea:
>>> it = iter("hello")>>>for i in it:...print i
...
h
e
l
l
o
Siempre me pregunté por qué Python tiene todos esos métodos __ xxx __. Parecen tan feos.
mP.
66
Pregunta legítima! Por lo general, es la sintaxis de los métodos que están expuestos por una función incorporada (por ejemplo, len, en realidad llama a len ). Dicha función integrada no existe para length_hint, pero en realidad es una propuesta pendiente (PEP424).
fulmicoton
1
@mP. estas funciones están ahí, porque a veces son necesarias. Son intencionalmente feos, porque se consideran como método de último recurso: si los usa, sabe que hace algo no pitónico y potencialmente peligroso (que también podría dejar de funcionar en cualquier momento).
Arne Babenhauserheide
Me gusta __init__y __main__? En mi humilde opinión, es un desastre, no importa si tratas de justificarlo.
user1363990
5
hasNextalgo se traduce a la StopIterationexcepción, por ejemplo:
>>> it = iter("hello")>>> it.next()'h'>>> it.next()'e'>>> it.next()'l'>>> it.next()'l'>>> it.next()'o'>>> it.next()Traceback(most recent call last):File"<stdin>", line 1,in<module>StopIteration
El caso de uso que me llevó a buscar esto es el siguiente
def setfrom(self,f):"""Set from iterable f"""
fi = iter(f)for i in range(self.n):try:
x = next(fi)exceptStopIteration:
fi = iter(f)
x = next(fi)
self.a[i]= x
donde hasnext () está disponible, uno podría hacer
def setfrom(self,f):"""Set from iterable f"""
fi = iter(f)for i in range(self.n):ifnot hasnext(fi):
fi = iter(f)# restart
self.a[i]= next(fi)
que para mí es más limpio Obviamente, puede solucionar los problemas definiendo las clases de utilidad, pero lo que sucede es que tiene una proliferación de veinte soluciones alternativas casi equivalentes diferentes, cada una con sus peculiaridades, y si desea reutilizar el código que utiliza soluciones alternativas diferentes, debe tenga múltiples casi equivalentes en su única aplicación, o busque y reescriba el código para usar el mismo enfoque. La máxima de "hazlo una vez y hazlo bien" falla gravemente.
Además, el iterador en sí mismo necesita tener una verificación interna 'hasnext' para ejecutarse para ver si necesita generar una excepción. Esta comprobación interna se oculta, por lo que debe probarse intentando obtener un elemento, capturando la excepción y ejecutando el controlador si se produce. Esto es innecesario para ocultar IMO.
La forma sugerida es StopIteration . Por favor vea el ejemplo de Fibonacci desde tutorialspoint
#!usr/bin/python3import sys
def fibonacci(n):#generator function
a, b, counter =0,1,0whileTrue:if(counter > n):returnyield a
a, b = b, a + b
counter +=1
f = fibonacci(5)#f is iterator objectwhileTrue:try:print(next(f), end=" ")exceptStopIteration:
sys.exit()
La forma en que resolví mi problema es mantener el recuento de la cantidad de objetos iterados, hasta ahora. Quería iterar sobre un conjunto utilizando llamadas a un método de instancia. Como sabía la longitud del conjunto y la cantidad de elementos contados hasta ahora, efectivamente tuve un hasNextmétodo.
Por supuesto, el ejemplo es de juguete, pero se entiende la idea. Esto no funcionará en casos donde no hay forma de obtener la longitud del iterable, como un generador, etc.
Respuestas:
No, no existe tal método. El final de la iteración se indica mediante una excepción. Ver la documentación .
fuente
unnext()
método para volver a colocar el primer elemento después de haber comprobado que existe llamandonext()
.yield
o no). Por supuesto, no es difícil escribir un adaptador que almacene el resultadonext()
y proporcionehas_next()
ymove_next()
.hasNext()
método (para producir, almacenar en caché y devolver verdadero en caso de éxito o falso en caso de error). Entonces amboshasNext()
ynext()
dependerían de ungetNext()
método subyacente común y un elemento en caché. Realmente no veo pornext()
qué no debería estar en la biblioteca estándar si es tan fácil implementar un adaptador que lo proporciona.next()
yhasNext()
método, no solo una hipotética biblioteca de Python). Entonces sí,next()
y sehasNext()
vuelve complicado si el contenido de la secuencia que se escanea depende de cuándo se leen los elementos.Hay una alternativa al
StopIteration
usonext(iterator, default_value)
.Por ejemplo:
Por lo tanto, puede detectar
None
u otro valor preespecificado para el final del iterador si no desea la forma de excepción.fuente
sentinel = object()
ynext(iterator, sentinel)
y probar conis
.unittest.mock.sentinel
objeto incorporado que le permita escribir un mensaje explícitonext(a, sentinel.END_OF_ITERATION)
y luegoif next(...) == sentinel.END_OF_ITERATION
Si realmente necesita una
has-next
funcionalidad (porque solo está transcribiendo fielmente un algoritmo de una implementación de referencia en Java, por ejemplo, o porque está escribiendo un prototipo que deberá transcribirse fácilmente a Java cuando esté terminado), es fácil obténgalo con una pequeña clase de envoltura. Por ejemplo:ahora algo como
emite
según sea necesario.
Tenga en cuenta que el uso de
next(sel.it)
como incorporado requiere Python 2.6 o superior; si está utilizando una versión anterior de Python, useself.it.next()
en su lugar (y de manera similar paranext(x)
el uso de ejemplo). [[Puede pensar razonablemente que esta nota es redundante, ya que Python 2.6 ha existido por más de un año, pero la mayoría de las veces cuando uso las características de Python 2.6 en una respuesta, algunos comentaristas u otros se sienten obligados a señalar que son características 2.6, por lo que estoy tratando de evitar tales comentarios por una vez ;-)]]fuente
has_next
método. El diseño de Python hace imposible, por ejemplo, usarfilter
para verificar si una matriz contiene un elemento que coincide con un predicado dado. La arrogancia y la miopía de la comunidad de Python es asombrosa.TypeError: iter() returned non-iterator
map
y enany
lugar defilter
, pero podría usarSENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINEL
u olvidar aySENTINEL
simplemente usartry: except
y capturar elStopIteration
.Además de todas las menciones de StopIteration, el bucle "for" de Python simplemente hace lo que desea:
fuente
Pruebe el método __length_hint __ () desde cualquier objeto iterador:
fuente
__init__
y__main__
? En mi humilde opinión, es un desastre, no importa si tratas de justificarlo.hasNext
algo se traduce a laStopIteration
excepción, por ejemplo:StopIteration
docs: http://docs.python.org/library/exceptions.html#exceptions.StopIterationfuente
Puede
tee
usar el iteradoritertools.tee
, y verificarStopIteration
en el iterador de teed.fuente
No. El concepto más similar es probablemente una excepción StopIteration.
fuente
Creo que Python solo tiene next () y, según el documento, arroja una excepción si no hay más elementos.
http://docs.python.org/library/stdtypes.html#iterator-types
fuente
El caso de uso que me llevó a buscar esto es el siguiente
donde hasnext () está disponible, uno podría hacer
que para mí es más limpio Obviamente, puede solucionar los problemas definiendo las clases de utilidad, pero lo que sucede es que tiene una proliferación de veinte soluciones alternativas casi equivalentes diferentes, cada una con sus peculiaridades, y si desea reutilizar el código que utiliza soluciones alternativas diferentes, debe tenga múltiples casi equivalentes en su única aplicación, o busque y reescriba el código para usar el mismo enfoque. La máxima de "hazlo una vez y hazlo bien" falla gravemente.
Además, el iterador en sí mismo necesita tener una verificación interna 'hasnext' para ejecutarse para ver si necesita generar una excepción. Esta comprobación interna se oculta, por lo que debe probarse intentando obtener un elemento, capturando la excepción y ejecutando el controlador si se produce. Esto es innecesario para ocultar IMO.
fuente
La forma sugerida es StopIteration . Por favor vea el ejemplo de Fibonacci desde tutorialspoint
fuente
La forma en que resolví mi problema es mantener el recuento de la cantidad de objetos iterados, hasta ahora. Quería iterar sobre un conjunto utilizando llamadas a un método de instancia. Como sabía la longitud del conjunto y la cantidad de elementos contados hasta ahora, efectivamente tuve un
hasNext
método.Una versión simple de mi código:
Por supuesto, el ejemplo es de juguete, pero se entiende la idea. Esto no funcionará en casos donde no hay forma de obtener la longitud del iterable, como un generador, etc.
fuente