Как я должен обращаться с дескрипторами файлов при возврате генератора?

#python #functional-programming #generator

#python #функциональное программирование #генератор

Вопрос:

У меня есть эта функция в моем коде:

 def load_fasta(filename):
    f = open(filename)
    return (seq.group(0) for seq in re.finditer(r">[^>]*", f.read()))
  

Это оставит файл открытым на неопределенный срок, что не является хорошей практикой. Как мне закрыть файл, когда генератор исчерпан? Я думаю, я мог бы расширить выражение генератора в цикл for с помощью операторов yield, а затем закрыть файл после этого. Я стараюсь использовать функциональное программирование как можно чаще (просто в качестве учебного упражнения). Есть ли другой способ сделать это?

Комментарии:

1. На самом деле оператор with open(filename, [mode]) заботится об открытии и закрытии файла.

2. Если вы используете f.read() , здесь нет необходимости в генераторе!

3. Это потому, что f.read() считывает весь файл в память?

Ответ №1:

Используйте yield вместо одного выражения генератора.

 def load_fasta(filename):
    with open(filename) as f:
        for seq in re.finditer(r">[^>]*", f.read()):
            yield seq.group(0)

for thing in load_fasta(filename):
    ...
  

with Оператор закроет файл после завершения for цикла. Обратите внимание, что, поскольку вы все равно считываете весь файл в память, вы можете просто использовать

 def load_fasta(filename):
    with open(filename) as f:
        data = f.read()
    for seq in re.finditer(r">[^>]*", data):
        yield seq.group(0)
  

Комментарии:

1. Я думаю, вам следует использовать f.read() или перебирать строки, а затем вставлять их finditer() , что, если вы используете f.read() на самом деле, вы загружаете весь файл в память, поэтому генератор будет бесполезен

2. Да, я написал это, надеясь, что finditer это может использовать сам итератор вместо строки. Это невозможно, так что на самом деле это не лучше, чем просто возврат списка совпадений.

3. Есть ли способ сделать это, не загружая все в память?

4. Насколько мне известно, нет. Проблема в том, что, как правило, движку регулярных выражений может потребоваться выполнить обратное отслеживание, что означает, что если он считывает данные непосредственно из генератора, ему может потребоваться вернуться назад. Возможно, кто-то написал что-то onlinefinditer , что выполняет собственную буферизацию, чтобы минимизировать объем данных, хранящихся в памяти, но в стандартной библиотеке нет ничего подобного.

5. Есть ли способ сделать это без регулярных выражений? Поскольку все, что мне действительно нужно сделать, это разделить файл на символы ‘>’.