Python: rendimiento y asignación de rendimiento

¿Cómo funciona este código, que involucra la asignación y el operador de rendimiento? Los resultados son bastante confusos.

def test1(x): for i in x: _ = yield i yield _ def test2(x): for i in x: _ = yield i r1 = test1([1,2,3]) r2 = test2([1,2,3]) print list(r1) print list(r2) 

Salida:

 [1, None, 2, None, 3, None] [1, 2, 3] 

La syntax de asignación (“expresión de rendimiento”) le permite tratar el generador como una coroutina rudimentaria.

Primero se propuso en PEP 342 y se documenta aquí: https://docs.python.org/2/reference/expressions.html#yield-expressions

El código de cliente que está trabajando con el generador puede comunicar datos al generador usando su método send() . Esos datos son accesibles a través de la syntax de asignación.

send() también se repetirá, por lo que en realidad incluye una llamada next() .

Usando su ejemplo, esto es lo que sería usar la funcionalidad de la mensajería:

 >>> def test1(x): ... for i in x: ... _ = yield i ... yield _ ... >>> l = [1,2,3] >>> gen_instance = test1(l) >>> #First send has to be a None >>> print gen_instance.send(None) 1 >>> print gen_instance.send("A") A >>> print gen_instance.send("B") 2 >>> print gen_instance.send("C") C >>> print gen_instance.send("D") 3 >>> print gen_instance.send("E") E >>> print gen_instance.send("F") Traceback (most recent call last): File "", line 1, in  StopIteration 

Tenga en cuenta que algunos de los envíos se pierden debido al segundo yield en cada iteración de bucle que no captura los datos enviados.

EDITAR: Olvidé de explicar la cantidad que se produjo en tu ejemplo.

De https://docs.python.org/2/reference/expressions.html#generator.next :

Cuando una función de generador se reanuda con un método next (), la expresión de rendimiento actual siempre se evalúa como Ninguna.

next() se usa cuando se usa la syntax de iteración.

 _ = yield i yield _ 

Primero, se yield el valor referenciado por i , por ejemplo, 1 . Entonces produce el valor devuelto por la operación de yield , que es None . Lo hace en cada iteración del bucle.

 for i in x: _ = yield i 

Esto simplemente yield el valor al que hace referencia i , por ejemplo, 1 , luego pasa a la siguiente iteración del bucle, produciendo 2 , luego 3 .

A diferencia de la return , la palabra clave de yield se puede utilizar en una expresión:

 x = return 0 # SyntaxError x = yield 0 # perfectly fine 

Ahora, cuando el intérprete ve un yield , generará el valor indicado. Sin embargo, cuando lo hace, esa operación devuelve el valor None , al igual que mylist.append(0) o print('hello') return el valor None . Cuando asigna ese resultado a una referencia como _ , está guardando ese None .

Entonces, en el primer fragmento, estás generando un objeto, luego guardas el “resultado” de esa operación de yield , que es None , y luego das ese None . En el segundo fragmento de código, genera un objeto, luego guarda el “resultado” de esa operación de yield , pero nunca yield ese resultado , por lo que None no aparece en la salida.

Tenga en cuenta que el yield no siempre devolverá None , esto es solo lo que envió al generador con send() . Como eso no era nada en este caso, no obtienes None . Ver esta respuesta para más información en send() .

Para ampliar la respuesta de TigerhawkT3, la razón por la que la operación de rendimiento está devolviendo None en su código es porque la list(r1) no está enviando nada al generador. Prueba esto:

 def test1(x): for i in x: _ = yield i yield _ r1 = test1([1, 2, 3]) for x in r1: print(' x', x) print('send', r1.send('hello!')) 

Salida:

  x 1 send hello! x 2 send hello! x 3 send hello! 

Aquí hay un ejemplo un tanto manufacturado donde el envío de valores a un generador podría ser útil:

 def changeable_count(start=0): current = start while True: changed_current = yield current if changed_current: current = changed_current else: current += 1 counter = changeable_count(10) for x in range(20): print(next(counter), end=' ') print() print() print('Sending 51, printing return value:', counter.send(51)) print() for x in range(20): print(next(counter), end=' ') print() print() print('Sending 42, NOT printing return value') print() counter.send(42) for x in range(20): print(next(counter), end=' ') print() 

Salida:

 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Sending 51, printing return value: 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 Sending 42, NOT printing return value 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62