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