[pyar] Diferencia entre iterar y yield, y el yield from

Joaquín Sorianello listas en joac.com.ar
Mie Mayo 17 16:46:58 ART 2017


La respuesta parece estar en el protocolo de generadores:

https://github.com/python/cpython/blob/b7c9150b68516878175e5373983189d6deea470c/Objects/genobject.c#L376

Cuando se llama a .close() en el generador (cuando, por ejemplo, lo
dealocas) y el generador tiene un yield from, se ejecuta este código:

if (yf) {
gen->gi_running = 1;
err = gen_close_iter(yf);
gen->gi_running = 0;
Py_DECREF(yf);
}

La funcion gen_close_iter
https://github.com/python/cpython/blob/b7c9150b68516878175e5373983189d6deea470c/Objects/genobject.c#L320

Chequea si el objeto desde el que estas haciendo yield from es un
generador o una corutina.
Si no lo es, se fija si tiene un metodo close y lo llama, en este
caso, cerrando el archivo.

Si no usas yield from, solamente se raisea GeneratorExit

Misterio resuelto :P (creo)


2017-05-17 15:46 GMT-03:00 Joaquín Sorianello <listas en joac.com.ar>:
> Cambiando un pelin tu segundo ejemplo:
>
> def crash(*args, **kwargs):
>     raise Exception
>
>
> class MyClass:
>     def __init__(self):
>         self.fh = open("foo.txt")
>         self.fh.close = crash
>
>     def __iter__(self):
>         yield from self.fh
>
> mc = MyClass()
> for line in mc:
>     print("L", line, end='')
>     if line == '33\n':
>         break
>
> print("--")
>
> for line in mc:
>     print("L", line, end='')
>
>
> el output es:
>
> L 11
> L 22
> L 33
> Exception ignored in: <generator object MyClass.__iter__ at 0x7f5b2bec2518>
> Traceback (most recent call last):
>   File "example2.py", line 12, in __iter__
>     yield from self.fh
>   File "example2.py", line 3, in crash
>     raise Exception
> Exception:
> --
> L 44
> L 55
> L 66
>
> Algo está pasando cuando salis del for con el iterador...
>
> Fijate que si lo haces explicito:
>
>
> class MyClass:
>     def __init__(self):
>         self.fh = open("foo.txt")
>
>     def __iter__(self):
>         yield from self.fh
>
> mc = MyClass()
> iterator = iter(mc)
> for line in iterator:
>     print("L", line, end='')
>     if line == '33\n':
>         break
>
> print("--")
>
> for line in iterator:
>     print("L", line, end='')
>
> Todo camina bien...
>
> L 11
> L 22
> L 33
> --
> L 44
> L 55
> L 66
>
> Claramente hay algo raro cuando se dealoca el iterador:
>
> class MyClass:
>     def __init__(self):
>         self.fh = open("foo.txt")
>
>     def __iter__(self):
>         yield from self.fh
>
> mc = MyClass()
> iterator = iter(mc)
> for line in iterator:
>     print("L", line, end='')
>     if line == '33\n':
>         break
> del(iterator)
> print("--")
>
> for line in mc:
>     print("L", line, end='')
>
>
> El problema de nuevo:
>
> L 11
> L 22
> L 33
> --
> Traceback (most recent call last):
>   File "example2.py", line 19, in <module>
>     for line in mc:
>   File "example2.py", line 7, in __iter__
>     yield from self.fh
> ValueError: I/O operation on closed file.
>
> La respuesta, imagino está en las tripas de la implementacion en C de python.
>
> Saludos!
>
>
>
> 2017-05-17 14:23 GMT-03:00 Facundo Batista <facundobatista en gmail.com>:
>> Jugando con algo que se preguntó en el canal de IRC, llegué a este código:
>>
>>
>> class MyClass:
>>     def __init__(self):
>>         self.fh = open("foo.txt")
>>
>>     def __iter__(self):
>>         for x in self.fh:
>>             yield x
>>
>> mc = MyClass()
>> for line in mc:
>>     print("L", line, end='')
>>     if line == '33\n':
>>         break
>>
>> print("--")
>>
>> for line in mc:
>>     print("L", line, end='')
>>
>>
>> Teniendo en cuenta que...
>>
>> $ cat foo.txt
>> 11
>> 22
>> 33
>> 44
>> 55
>> 66
>>
>> Al ejecutar el código me sale:
>>
>> L 11
>> L 22
>> L 33
>> --
>> L 44
>> L 55
>> L 66
>>
>> ¡Perfecto!
>>
>> Pero ahora cambio el método __iter__ de la clase de arriba y dejo:
>>
>>     def __iter__(self):
>>         yield from self.fh
>>
>> Cuando ejecuto el código me sale:
>>
>> L 11
>> L 22
>> L 33
>> --
>> Traceback (most recent call last):
>>   File "tfoo.py", line 32, in <module>
>>     for line in mc:
>>   File "tfoo.py", line 22, in __iter__
>>     yield from self.fh
>> ValueError: I/O operation on closed file.
>>
>>
>> Entonces, ¿cuando se cerró el archivo? ¿por qué?
>>
>> Gracias! Slds.
>>
>> --
>> .    Facundo
>>
>> Blog: http://www.taniquetil.com.ar/plog/
>> PyAr: http://www.python.org/ar/
>> Twitter: @facundobatista
>> _______________________________________________
>> Lista de Correo de PyAr - Python Argentina - pyar en python.org.ar
>> Sitio web: http://www.python.org.ar/
>>
>> Para administrar la lista (o desuscribirse) entrar a http://listas.python.org.ar/listinfo/pyar
>>
>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de Argentina - http://www.usla.org.ar
>
>
>
> --
> Joaquín Sorianello
> A.K.A. Joac
> @_joac



-- 
Joaquín Sorianello
A.K.A. Joac
@_joac


Más información sobre la lista de distribución pyar