python python-3.x yield

python - ¿Qué hace un rendimiento dentro de un rendimiento?



python-3.x yield (4)

Considere el siguiente código:

def mygen(): yield (yield 1) a = mygen() print(next(a)) print(next(a))

Los rendimientos de salida:

1 None

¿Qué hace exactamente el intérprete en el rendimiento "externo"?


Cualquier generador agota elementos hasta que se queda sin ellos.
En el ejemplo anidado de 2 niveles como a continuación, el primer next nos da el elemento de la mayoría de los rendimientos internos, que es 1, los siguientes rendimientos solo devuelve None , ya que no tiene elementos que devolver, si llama de nuevo, devolverá StopIteration

def mygen(): yield (yield 1) a = mygen() print(next(a)) print(next(a)) print(next(a))

Puede expandir este caso para incluir más rendimientos anidados, y verá que después de llamar a n next , se lanza la StopIteration , a continuación se muestra un ejemplo con 5 rendimientos anidados

def mygen(): yield ( yield ( yield ( yield (yield 1)))) a = mygen() print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a))

Tenga en cuenta que esta respuesta solo se basa en mi observación, y puede que no sea técnicamente correcta en los diez años, todas las actualizaciones y sugerencias son bienvenidas.


a es un objeto generador. La primera vez que se llama a next , el cuerpo se evalúa hasta la primera expresión de yield (es decir, la primera que se evalúa: la interna). Ese yield produce el valor 1 para el next retorno, luego se bloquea hasta la próxima entrada en el generador. Esto se produce en la segunda llamada a la next , que no envía ningún valor al generador. Como resultado, el primer yield (interno) se evalúa como None . Ese valor se utiliza como argumento para el yield externo, que se convierte en el valor de retorno de la segunda llamada a la next . Si llamara por tercera vez, obtendría una excepción de StopIteration .

Compare el uso del método de send (en lugar del next ) para cambiar el valor de retorno de la primera expresión de yield .

>>> a = mygen() >>> next(a) 1 >>> a.send(3) # instead of next(a) 3 >>> next(a) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration

Una forma más explícita de escribir el generador habría sido

def mygen(): x = yield 1 yield x a = mygen() print(a.send(None)) # outputs 1, from yield 1 print(a.send(5)) # makes yield 1 == 5, then gets 5 back from yield x print(a.send(3)) # Raises StopIteration, as there''s nothing after yield x

Antes de Python 2.5, la declaración de yield proporcionaba comunicación unidireccional entre un llamador y un generador; una llamada a next ejecutaría el generador hasta la siguiente declaración de yield , y el valor proporcionado por la palabra clave de yield serviría como el valor de retorno de next . El generador también se suspendería en el punto de la declaración de yield , a la espera de que se reanudara la próxima llamada.

En Python 2.5, la declaración de yield se reemplazó * con la expresión de yield , y los generadores adquirieron un método de send . send trabajos de send muy parecidos al next , excepto que puede tomar una discusión. (Para el resto de esto, suponga que la next(a) es equivalente a a.send(None) .) Un generador comienza la ejecución después de una llamada a send(None) , momento en el que se ejecuta hasta el primer yield , que devuelve un valor como antes. Ahora, sin embargo, la expresión se bloquea hasta la siguiente llamada para send , en cuyo punto la expresión de yield evalúa con respecto al argumento que se ha pasado para send . Un generador ahora puede recibir un valor cuando se reanuda.

* No ha sido reemplazado del todo; La respuesta de Kojiro entra en más detalles sobre la sutil diferencia entre una declaración de yield y yield expresión de yield .


yield tiene dos formas, expresiones y declaraciones . En su mayoría son iguales, pero la mayoría de las veces los veo en el formulario de statement , donde el resultado no se usaría.

def f(): yield a thing

Pero en la forma de expresión, el yield tiene un valor:

def f(): y = yield a thing

En tu pregunta, estás usando ambas formas:

def f(): yield ( # statement yield 1 # expression )

Cuando recorres el generador resultante, obtienes primero el resultado de la expresión de rendimiento interno

>>> x=f() >>> next(x) 1

En este punto, la expresión interna también ha producido un valor que la declaración externa puede usar

>>> next(x) >>> # None

y ahora has agotado el generador

>>> next(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration

Para comprender más acerca de las afirmaciones frente a las expresiones, hay buenas respuestas en otras preguntas de : ¿Cuál es la diferencia entre una expresión y una afirmación en Python?


>>> def mygen(): ... yield (yield 1) ... >>> a = mygen() >>> >>> a.send(None) 1 >>> a.send(5) 5 >>> a.send(2) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> >>> >>> >>> def mygen(): ... yield 1 ... >>> def mygen2(): ... yield (yield 1) ... >>> def mygen3(): ... yield (yield (yield 1)) ... >>> a = mygen() >>> a2 = mygen2() >>> a3 = mygen3() >>> >>> a.send(None) 1 >>> a.send(0) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> a2.send(None) 1 >>> a2.send(0) 0 >>> a2.send(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> a3.send(None) 1 >>> a3.send(0) 0 >>> a3.send(1) 1 >>> a3.send(2) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>

Cada otro rendimiento simplemente espera a que se transmita un valor, el generador no solo proporciona datos, sino que también los recibe.

>>> def mygen(): ... print(''Wait for first input'') ... x = yield # this is what we get from send ... print(x, ''is received'') ... >>> a = mygen() >>> a.send(None) Wait for first input >>> a.send(''bla'') bla is received Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>

yield da el siguiente valor cuando continúa si lo obtiene, y si no se usa para dar el siguiente valor, se usa para recibir el siguiente

>>> def mygen(): ... print(''Wait for first input'') ... x = yield # this is what we get from send ... yield x*2 # this is what we give ... >>> a = mygen() >>> a.send(None) Wait for first input >>> a.send(5) 10 >>>