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
>>>