what used print generators generadores for are python python-3.x generator generator-expression

used - next() no juega bien con ninguno/todos en python



python generator to list (2)

El problema no está en utilizar all , es que tiene una expresión generadora como parámetro para all . El StopIteration se propaga a la expresión del generador, que en realidad no sabe dónde se originó, por lo que hace lo habitual y termina la iteración.

Puede ver esto reemplazando su función de error con algo que provoca el error directamente:

def error2(): raise StopIteration >>> all(error2() for i in range(2)) True

La última pieza del rompecabezas es saber qué hace all con una secuencia vacía:

>>> all([]) True

Si va a usar el next directamente, debe estar preparado para capturar StopIteration usted mismo.

Edición: Es bueno ver que los desarrolladores de Python consideran esto como un error y están tomando medidas para cambiarlo en 3.7.

Corrí un error hoy que surgió porque estaba usando next() para extraer un valor, y ''no encontrado'' emite una StopIteration .

Normalmente, eso detendría el programa, pero la función que usaba a next se llamaba dentro de una iteración all() , por lo que all acabó antes y devolvió True .

¿Es este un comportamiento esperado? ¿Hay guías de estilo que ayuden a evitar este tipo de cosas?

Ejemplo simplificado:

def error(): return next(i for i in range(3) if i==10) error() # fails with StopIteration all(error() for i in range(2)) # returns True


Si bien este es el comportamiento predeterminado en las versiones de Python hasta 3.6 inclusive, se considera que es un error en el idioma y está programado para cambiar en Python 3.7 para que se genere una excepción.

Como dice PEP 479 :

La interacción de los generadores y StopIteration es actualmente algo sorprendente y puede ocultar errores ocultos. Una excepción inesperada no debería dar como resultado un comportamiento alterado sutilmente, sino que debería causar un rastreo ruidoso y fácilmente depurado. Actualmente, StopIteration accidentalmente dentro de una función del generador se interpretará como el final de la iteración por la construcción de bucle que impulsa el generador.

Desde Python 3.5 en adelante, es posible cambiar el comportamiento predeterminado al programado para 3.7. Este codigo

# gs_exc.py from __future__ import generator_stop def error(): return next(i for i in range(3) if i==10) all(error() for i in range(2))

... plantea la siguiente excepción:

Traceback (most recent call last): File "gs_exc.py", line 8, in <genexpr> all(error() for i in range(2)) File "gs_exc.py", line 6, in error return next(i for i in range(3) if i==10) StopIteration The above exception was the direct cause of the following exception: Traceback (most recent call last): File "gs_exc.py", line 8, in <module> all(error() for i in range(2)) RuntimeError: generator raised StopIteration

En Python 3.5 y 3.6 sin la importación de __future__ , __future__ una advertencia. Por ejemplo:

# gs_warn.py def error(): return next(i for i in range(3) if i==10) all(error() for i in range(2))

$ python3.5 -Wd gs_warn.py gs_warn.py:6: PendingDeprecationWarning: generator ''<genexpr>'' raised StopIteration all(error() for i in range(2))

$ python3.6 -Wd gs_warn.py gs_warn.py:6: DeprecationWarning: generator ''<genexpr>'' raised StopIteration all(error() for i in range(2))