[Tutor] Yielding from a with block

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu May 28 12:03:58 CEST 2015


On 28 May 2015 at 09:16, Peter Otten <__peter__ at web.de> wrote:
> ...but the obvious route is of course
>
>> It's usually fairly trivial to rearrange things so that this doesn't
>> happen:
>>
>> def wrap_header_footer(fin):
>>     yield htbegin
>>     for linelist in csv.reader(fin):
>>         yield from fprint(linelist)
>>     yield htend
>
> which in this case also has the advantage of better separation of concerns
> (I'd probably move the csv.reader() out of the generator, too).

Agreed. In practise you would at least want to be able to pass the
filename in and then it's almost always better to be able to pass in a
file object.

> PS: I'm still looking for a fairly elegant rewrite of the problematic
>
> def lines(files):
>     for file in files:
>         with open(files) as f:
>             yield from f

This was mentioned in the previous thread but a fileinput.input object
can be used as a contextmanager so:

import fileinput

filenames = 'f.py', 'wrap.py'

with fileinput.input(filenames) as joined:
    for line in joined:
        print(line, end='')

The behaviour of fileinput in the corner case of an empty file list
(reading from stdin) is annoying in this usage though.

You can also just wrap your lines generator in a context manager:

from contextlib import contextmanager

@contextmanager
def catfiles(filenames):
    def lines():
        for filename in filenames:
            with open(filename) as fin:
                yield from fin
    gen = lines()
    try:
        yield gen
    finally:
        gen.close()

filenames = 'f.py', 'wrap.py'

with catfiles(filenames) as joined:
    for line in joined:
        print(line.upper(), end='')


--
Oscar


More information about the Tutor mailing list