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

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


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


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