Question

If I was to write a file with this content:

#You have been defeated!
#It's merely a flesh wound!
We are the knights who say Ni!
We are the knights who say Ni!
We are the knights who say Ni!

Would it then be very non-pythonic to do it with a generator using send? I have never seen generators used like this elsewhere.

def write(file, header):

    with open(file,'w') as f:
        f.write(header)
        line = (yield)
        while True:
            f.write(line)
            line = (yield)

    return

file='holygrail.txt'
header="#You have been defeated!\n#It's merely a flesh wound!\n"
generator = write(file,header)
generator.send(None)
for i in range(3):
    generator.send('We are the knights who say Ni!\n')
generator.close()

I am asking, because the method above would be hugely beneficial to me instead of opening multiple different file streams in a contextlib stack. I would not have to use the contextlib module at all, if I write my files like this.

I have never asked a question like this before, and I don't know, whether it belongs on stackoverflow or not.

Was it helpful?

Solution

I like the creativity of your solution, but my subjective opinion would be that using contextlib.ExitStack() will look cleaner, be more readable, than using the generator since each generator would need to be primed with generator.send(None) and explicitly closed.


By the way, (even though I think contextlib will lead to shorter, more readable code), write could be simplified a little bit:

def write(file, header):
    with open(file, 'w') as f:
        f.write(header)
        while True:
            line = (yield)
            f.write(line)
    return

Note you only need one line = (yield) instead of two.

Also, instead of priming the generator with generator.send(None) you could use the coroutine decorator:

def coroutine(func):
    """ http://www.python.org/dev/peps/pep-0342/ """
    def wrapper(*args, **kw):
        gen = func(*args, **kw)
        gen.send(None)
        return gen
    return wrapper

This is a commonly understood idiom (PEP0342, David Beazley talk) for turning a generator into a coroutine. So decorating your generator with it would also serve the purpose of advertising that write is a coroutine.

OTHER TIPS

I think this question is somewhat subjective, but I believe "Pythonic" also means keeping it simple. And for your particular case, that would probably something along the lines of

open("blah.txt", 'w').write("""\
#You have been defeated!
#It's merely a flesh wound!
We are the knights who say Ni!
We are the knights who say Ni!
We are the knights who say Ni!
""")

I guess your actual case is different though...

You're code is no shorter, and is no clearer, than

file='holygrail.txt'
header="#You have been defeated!\n#It's merely a flesh wound!\n"
with open(file, w) as fh:
    fh.write(header)
    for i in range(3):
        fh.write('We are the knights who say Ni!\n')

so I'm not sure what the benefit is.

The point of coroutines is in saving internal state between .send() calls.

Coroutines are often used to implement "consumer" pattern (I'm seldom using them to wrap xlwt worksheets: I need to track number of rows to flush them). Without any state you can use a simple function without any state (or .write() method of file object)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top