2012-09-28 19 views
16

No puedo entender el método send. Entiendo que se usa para operar el generador. Pero la sintaxis está aquí: generator.send(value).Python 3: enviar método de generadores

que de alguna manera no se puede coger por qué el valor debe ser el resultado de la corriente yield expresión. Preparé un ejemplo:

def gen(): 
    for i in range(10): 
     X = yield i 
     if X == 'stop': 
      break 
     print("Inside the function " + str(X)) 

m = gen() 
print("1 Outside the function " + str(next(m)) + '\n') 
print("2 Outside the function " + str(next(m)) + '\n') 
print("3 Outside the function " + str(next(m)) + '\n') 
print("4 Outside the function " + str(next(m)) + '\n') 
print('\n') 
print("Outside the function " + str(m.send(None)) + '\n') # Start generator 
print("Outside the function " + str(m.send(77)) + '\n') 
print("Outside the function " + str(m.send(88)) + '\n') 
#print("Outside the function " + str(m.send('stop')) + '\n') 
print("Outside the function " + str(m.send(99)) + '\n') 
print("Outside the function " + str(m.send(None)) + '\n') 

El resultado es:

1 Outside the function 0 

Inside the function None 
2 Outside the function 1 

Inside the function None 
3 Outside the function 2 

Inside the function None 
4 Outside the function 3 



Inside the function None 
Outside the function 4 

Inside the function 77 
Outside the function 5 

Inside the function 88 
Outside the function 6 

Inside the function 99 
Outside the function 7 

Inside the function None 
Outside the function 8 

Bueno, francamente, se me asombroso.

  1. En la documentación se puede leer que cuando se ejecuta una instrucción yield, el estado del generador se congela y el valor de expression_list se devuelve a la persona que llama next ‘s. Bueno, parece que no ha sucedido. ¿Por qué podemos ejecutar la declaración if y la función print dentro de gen()?
  2. ¿Cómo puedo entender por qué X dentro y fuera de la función es diferente? Ok. Supongamos que send(77) transmite 77 en m. Bueno, la expresión yield se convierte en 77. Entonces, ¿qué es X = yield i? ¿Y cómo 77 dentro de la función se convierte en 5 cuando ocurre afuera?
  3. ¿Por qué la primera cadena de resultados no refleja nada de lo que sucede dentro del generador?

De todos modos, ¿podría usted de alguna manera comentar estas declaraciones send y yield?

Respuesta

40

Cuando usa send y la expresión yield en un generador, lo está tratando como una corutina; un hilo de ejecución separado que puede ejecutarse secuencialmente intercalado pero no en paralelo con su llamador.

Cuando la persona que llama ejecuta R = m.send(a), coloca el objeto a en la ranura de entrada del generador, transfiere el control al generador y espera una respuesta. El generador recibe el objeto a como resultado de X = yield i y se ejecuta hasta que alcanza otra expresión yield, p. Y = yield j. Luego pone j en su ranura de salida, transfiere el control a la persona que llama y espera hasta que se reanude nuevamente. La persona que llama recibe j como resultado de R = m.send(a), y se ejecuta hasta que choca con otra S = m.send(b) declaración, y así sucesivamente.

R = next(m) es lo mismo que R = m.send(None); Es poner None en la ranura de entrada del generador, por lo que si el generador comprueba el resultado de X = yield i entonces X habrá None.

Como metáfora, considere un dumb waiter:

Dumb waiter

Cuando el servidor recibe un pedido de un cliente, se puso la almohadilla en el montaplatos, send a la cocina, y esperar por el escotilla para el plato:

R = kitchen.send("Ham omelette, side salad") 

el chef (que ha estado esperando por la escotilla) recoge la orden, se prepara el plato, yield s al restaurante, y espera al siguiente orden:

next_order = yield [HamOmelette(), SideSalad()] 

El servidor (que ha estado esperando por la escotilla) saca el plato para el cliente y vuelve con otro orden, etc.

Debido a que tanto el servidor y chef de espera junto a la escotilla después de send ing una orden o yield ing un plato, solo hay una persona haciendo cualquier cosa al mismo tiempo, es decir, el proceso es de un solo hilo. Ambos lados pueden usar el flujo de control normal, ya que la maquinaria del generador (el camarero tonto) se ocupa de la ejecución del entrelazado.

+0

se ejecuta al mismo tiempo pero no en paralelo. – jfs

+0

@ J.F.Sebastian Creo que "concurrentemente" significa en paralelo? – ecatmur

+2

en la programación "paralelo" implica "concurrente" pero lo opuesto en general no es cierto. [Diferencia entre programación simultánea y programación paralela] (http://stackoverflow.com/q/1897993/4279) – jfs

4
def gen(): 
    i = 1 
    while True: 
     i += 1 
     x = yield i 
     print(x) 

m = gen() 
next(m) 
next(m) 
m.send(4) 

resultado

None 
4 

vistazo a los códigos más sencillos anteriores.
Creo que lo que lleva a su confusión es 'x = yield i' statment, esta declaración no dice que se aceptó el valor del método send() asignado a i entonces me asignó x. En cambio, el valor i se devuelve mediante la declaración de rendimiento al generador, x se asigna mediante el método send(). Una instrucción hace dos cosas al mismo tiempo.

9

La parte más confusa debe ser esta línea X = yield i, especialmente cuando llama al send() en el generador. En realidad lo único que necesita saber es:

en el nivel léxico: next() es igual a send(None)

en el nivel del intérprete: X = yield i equivale a continuación las líneas (El orden es importante) :

yield i 
# won't continue until next() or send() is called 
# and this is also the entry point of next() or send() 
X = the_input_of_send 

y, las 2 líneas de comentario es la razón exacta, por qué tenemos que ca ll send(None) por primera vez, debido a que el generador volverá i (rendimiento i) antes asignar el valor a X

+0

¡Gran respuesta! Explicación simple y fácil. Identificaste muy bien el punto débil 'X = rendimiento i'. votaría 10 veces –

Cuestiones relacionadas