Подтвердить что ты не робот

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 МБ (~ 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(), даже если это одни и те же данные? Это нормальное поведение или что-то не так в моем коде? Выходной файл кажется одинаковым для обоих случаев, поэтому я знаю, что потери данных отсутствуют.

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Это ясно?