2009-12-27 9 views
91

¿Los iteradores de Python no tienen un método hasNext?hasNext en iteradores de Python?

+0

Relacionado: [¿Cómo puedo saber si un generador está vacío desde el principio?] (Http: // stackoverflow.com/questions/661603/how-doi-i-know-if-a-generator-is-empty-from-the-start) – user2314737

Respuesta

64

No, no hay tal método. El final de la iteración está indicado por una excepción. Vea el documentation.

+50

"Es más fácil pedir perdón que permiso". –

+66

"Es más fácil pedir perdón que permiso": comprobar si un iterador tiene un elemento siguiente no está pidiendo 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 método 'unnext()' para devolver el primer elemento después de haber comprobado que existe al invocar 'next()'. – Giorgio

+9

@Giorgio, no hay forma de saber si existe otro elemento sin ejecutar el código que lo genera (no se sabe si el generador ejecutará 'yield' o no). Por supuesto, no es difícil escribir un adaptador que almacena el resultado de 'next()' y proporciona 'has_next()' y 'move_next()'. – avakar

3

No. El concepto más similar es más probable una StopIteration exception.

+7

¿Qué Python usa excepciones para el flujo de control? Suena muy bien hecho. –

+4

Derecha: se deben usar excepciones para manejar errores, no para definir el flujo normal de control. – Giorgio

5

hasNext traduce tanto a la StopIteration excepció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 
28

Si realmente necesita a has-next funcionalidad (porque simplemente está transcribiendo fielmente un algoritmo de una implementación de referencia en Java, por ejemplo, o porque está escribiendo un prototipo que necesitará debe transcribirse fácilmente a Java cuando esté terminado), es fácil de obtener con una pequeña clase de envoltura. Por ejemplo:

class hn_wrapper(object): 
    def __init__(self, it): 
    self.it = iter(it) 
    self._hasnext = None 
    def __iter__(self): return self 
    def next(self): 
    if self._hasnext: 
     result = self._thenext 
    else: 
     result = next(self.it) 
    self._hasnext = None 
    return result 
    def hasnext(self): 
    if self._hasnext is None: 
     try: self._thenext = next(self.it) 
     except StopIteration: self._hasnext = False 
     else: self._hasnext = True 
    return self._hasnext 

ahora algo así 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 un built-in 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) en el uso de ejemplo). [[Es razonable pensar que esta nota es redundante, ya que Python 2.6 ha estado presente 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 comentadores u otros se sienten obligados a señalar que son 2.6 características, por lo que estoy tratando de evitar este tipo de comentarios, por una vez ;-)]]

+1

"transcribir fielmente un algoritmo de una implementación de referencia en Java" es la peor razón para necesitar un método 'has_next'. El diseño de Python hace que sea imposible, por ejemplo, usar 'filter' para verificar si una matriz contiene un elemento que coincida con un predicado dado. La arrogancia y la falta de visión de la comunidad de Python es asombrosa. –

+0

nice answer, estoy copiando esto para la ilustración de algún patrón de diseño tomado del código Java – madtyn

+0

Estoy con Python3 y este código me da 'TypeError: iter() devuelto non-iterator' – madtyn

8

Además de todas las menciones de StopIteration, el pitón bucle "for" simplemente hace lo que quiere:

>>> it = iter("hello") 
>>> for i in it: 
...  print i 
... 
h 
e 
l 
l 
o 
3

Usted puede tee utilizando el iterador, itertools.tee, y comprobar si hay StopIteration en el iterador juntado con te.

8

probar el método de __length_hint __() de cualquier objeto iterador:

iter(...).__length_hint__() > 0 
+4

Siempre me pregunté por qué en la Tierra Python tiene todos esos métodos __ xxx __? Parecen tan feos. –

+6

¡Pregunta legítima! Por lo general, es la sintaxis de los métodos expuestos por una función integrada (por ejemplo, len, en realidad está llamando __len__). Tal función incorporada 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 un método de último recurso: si los usas, sabes que haces algo no pitónico y potencialmente peligroso (que también podría dejar de funcionar en cualquier punto). –

120

Hay una alternativa a la StopIteration mediante el uso de next(iterator, default_value).

Para exapmle:

>>> a = iter('hi') 
>>> print next(a, None) 
h 
>>> print next(a, None) 
i 
>>> print next(a, None) 
None 

para que pueda detectar por None u otro valor especificado previamente para el final del iterador si no desea que la forma es una excepción.

+43

si usa None como "centinela", asegúrese de que su iterador no tenga Nones. también podría hacer 'sentinel = object()' y 'next (iterator, sentinel)' y probar con 'is'. –

1

El caso de uso que me llevan a buscar este es el siguiente

def setfrom(self,f): 
    """Set from iterable f""" 
    fi = iter(f) 
    for i in range(self.n): 
     try: 
      x = next(fi) 
     except StopIteration: 
      fi = iter(f) 
      x = next(fi) 
     self.a[i] = x 

donde está disponible hasNext(), se podría hacer

def setfrom(self,f): 
    """Set from iterable f""" 
    fi = iter(f) 
    for i in range(self.n): 
     if not hasnext(fi): 
      fi = iter(f) # restart 
     self.a[i] = next(fi) 

que para mí es más limpio. Obviamente, puede solucionar los problemas mediante la definición de clases de utilidad, pero lo que sucede es que tiene una proliferación de veintitantas soluciones diferentes casi equivalentes, cada una con sus peculiaridades, y si desea reutilizar el código que utiliza diferentes soluciones, tiene que tener múltiples equivalentes casi equivalentes en su única aplicación, o ir a buscar y reescribir el código para usar el mismo enfoque. La máxima 'hazlo una vez y hazlo bien' falla mucho.

Además, el iterador en sí necesita tener una verificación interna 'hasnext' para ejecutar si necesita generar una excepción. Esta comprobación interna se oculta entonces para que se deba probar tratando de obtener un artículo, capturando la excepción y ejecutando el controlador si se lanza. Esto es innecesario ocultar IMO.

0

buen enfoque para tales preguntas/problemas es comprobar lo que tenemos en dir (objeto/método/repetidor/tipo/clase/...)

verá que dir(iterator) retorno __length_hint__

y iterator.__length_hint__() es positivo hasta el final de la iteración.

eso es todo.

+0

'__length_hint__' no garantiza la precisión: https://www.python.org/dev/peps/pep-0424/. –

0

me gusta esto:

While(True): 
    try: 
     # Do something with the next value 
     iterator.next() 
    except StopIteration: 
     break 
+6

¿Por qué no simplemente usar 'for ... in'? – bfontaine

0

La forma he resuelto mi problema es mantener el recuento del número de objetos repiten a lo largo, hasta el momento. Quería iterar sobre un conjunto usando llamadas a un método de instancia. Como sabía la longitud del conjunto y el número de elementos contados hasta ahora, efectivamente tenía un método hasNext.

Una versión simple de mi código:

class Iterator: 
    # s is a string, say 
    def __init__(self, s): 
     self.s = set(list(s)) 
     self.done = False 
     self.iter = iter(s) 
     self.charCount = 0 

    def next(self): 
     if self.done: 
      return None 
     self.char = next(self.iter) 
     self.charCount += 1 
     self.done = (self.charCount < len(self.s)) 
     return self.char 

    def hasMore(self): 
     return not self.done 

Por supuesto, el ejemplo es uno de juguete, pero se entiende la idea. Esto no funcionará en los casos donde no hay forma de obtener la longitud del iterable, como un generador, etc.