Является ли pythonic использовать генераторы для записи заголовка и тела файла?

Если бы я написал файл с этим контентом:

#You have been defeated!
#It 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!

Неужели это было бы очень непитоновым делать это с генератором, использующим send? Я никогда не видел, чтобы генераторы использовали это в другом месте.

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 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()

Я спрашиваю, потому что вышеописанный метод был бы очень полезен для меня, а не для открытия нескольких разных потоков файлов в стек контекстной библиотеки. Мне не пришлось бы использовать модуль contextlib вообще, если я пишу свои файлы следующим образом.

Я никогда не задавал такой вопрос раньше, и я не знаю, принадлежит ли оно в stackoverflow или нет.

Ответ 1

Мне нравится творчество вашего решения, но мое субъективное мнение состояло в том, что использование contextlib.ExitStack() будет выглядеть более чистым, более читаемым, чем использование генератора так как каждый генератор должен быть загрунтован с помощью generator.send(None) и явно закрыт.


Кстати, (хотя я думаю, что contextlib приведет к более короткому, более читаемому коду), write можно было бы немного упростить:

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

Обратите внимание, что вам нужно только один line = (yield) вместо двух.

Кроме того, вместо заливки генератора с помощью generator.send(None) вы можете использовать декоратор coroutine:

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

Это общепринятая идиома (PEP0342, беседа Дэвида Безли) для превращения генератора в сопрограммная. Таким образом, украшение вашего генератора будет также способствовать рекламе, что write является сопрограммой.

Ответ 2

Я думаю, что этот вопрос несколько субъективен, но я считаю, что "Pythonic" также означает его простоту. И для вашего конкретного случая это может быть что-то вроде строк

open("blah.txt", 'w').write("""\
#You have been defeated!
#It 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!
""")

Я думаю, ваш фактический случай отличается, хотя...

Ответ 3

Вы код не короче и не понятнее, чем

file='holygrail.txt'
header="#You have been defeated!\n#It 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')

поэтому я не уверен, в чем преимущество.

Ответ 4

Точка сопрограмм - это сохранение внутреннего состояния между вызовами .send().

Coroutines часто используются для реализации "потребительского" шаблона (я редко использую их для переноса рабочих листов xlwt: мне нужно отслеживать количество строк для их очистки). Без какого-либо состояния вы можете использовать простую функцию без какого-либо состояния (или .write() для объекта файла)