Python writelines () и write () огромная разница во времени

я работал над сценарием, который читал папку с файлами (каждый размером от 20 МБ до 100 МБ), изменяет некоторые данные в каждой строке и записывает обратно в копию файла.

with open(inputPath, 'r+') as myRead:
     my_list = myRead.readlines()
     new_my_list = clean_data(my_list)
with open(outPath, 'w+') as myWrite:
     tempT = time.time()
     myWrite.writelines('n'.join(new_my_list) + 'n')
     print(time.time() - tempT)
print(inputPath, 'Cleaning Complete.')

при запуске этого кода с файлом 90 MB (~900,000 строк) он напечатал 140 секунд как время, необходимое для записи в файл. Здесь я использовал writelines(). Поэтому я искал различные способы улучшить скорость записи файлов, и в большинстве статей, которые я читал, он сказал write() и writelines() не должно показывать никакой разницы, так как я пишу одну объединенную строку. Я также проверил время, затраченное только на следующее утверждение:

new_string = 'n'.join(new_my_list) + 'n'

и это заняло всего 0,4 секунды, поэтому большое время заняло не из-за создания списка. Просто попробовать write() я попробовал этот код:

with open(inputPath, 'r+') as myRead:
     my_list = myRead.readlines()
     new_my_list = clean_data(my_list)
with open(outPath, 'w+') as myWrite:
     tempT = time.time()
     myWrite.write('n'.join(new_my_list) + 'n')
     print(time.time() - tempT)
print(inputPath, 'Cleaning Complete.')

и он напечатал 2,5 секунды. Почему существует такая большая разница во времени записи файла для write() и writelines() даже если это те же данные? Заключаться в следующем нормальное поведение или что-то не так в моем коде? Выходной файл кажется одинаковым для обоих случаев, поэтому я знаю, что нет потери данных.

3 ответов


file.writelines() ждет типа Iterable строк. Затем он переходит к циклу и вызову file.write() для каждой строки в массиве. В Python метод делает следующее:

def writelines(self, lines)
    for line in lines:
        self.write(line)

вы передаете одну большую строку, и строка также является итерацией строк. При итерации вы получаете отдельные символы, строки длины 1. Таким образом, в действительности вы делаете len(data) отдельные вызовы file.write(). И это медленно, потому что вы создаете запись буфер по одному символу за раз.

не передавайте ни одной строки в file.writelines(). Вместо этого передайте список или кортеж или другой iterable.

вы можете отправить отдельные строки с добавленной новой строкой в выражении генератора, например:

 myWrite.writelines(line + '\n' for line in new_my_list)

теперь, если вы могли бы сделать clean_data() a генератор, давая очищенные линии, вы можете передавать данные из входного файла, через генератор очистки данных и в выходной файл без используя больше памяти, чем требуется для буферов чтения и записи, и сколько состояния необходимо для очистки строк:

with open(inputPath, 'r+') as myRead, open(outPath, 'w+') as myWrite:
    myWrite.writelines(line + '\n' for line in clean_data(myRead))

кроме того, я бы рассмотрел обновление clean_data() излучать строк с переносами строк.


в качестве дополнения к ответу Martijn лучшим способом было бы избежать создания списка с помощью join в первую очередь

просто передайте понимание генератора writelines, добавив новую строку в конце: нет ненужного выделения памяти и нет цикла (кроме понимания)

myWrite.writelines("{}\n".format(x) for x in my_list)

' write(arg) ' метод ожидает строку в качестве аргумента. Поэтому, как только он позвонит, он будет напрямую писать. это причина намного быстрее. где, как будто вы используете writelines() метод, он ожидает список строк в качестве итератора. поэтому, даже если вы отправляете данные в writelines, Он предполагает, что он получил итератор и пытается перебрать его. поэтому, поскольку это итератор, потребуется некоторое время, чтобы повторить и написать его.

Это понятно ?