[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