#python #python-3.x
#python #python-3.x
Вопрос:
Я знаю общую разницу между строками чтения и строкой чтения файлового объекта. Но мне больше любопытно, чем их производительность отличается друг от друга, поэтому я провел тест здесь.
import timeit
with open('test.txt', 'w') as f:
f.writelines('n'.join("Just a test casetJust a test case2tJust a test case3" for i in range(1000000)))
def a1():
with open('test.txt', 'r') as f:
for text in f.readlines():
pass
def a2():
with open('test.txt', 'r') as f:
text = f.readline()
while text:
text = f.readline()
print(timeit.timeit(a1, number =100))
print(timeit.timeit(a2, number =100))
$python readline_vs_readlines.py
38.410646996984724
35.876863296027295
Но почему это так? Я думаю, что ввод-вывод занимает больше времени, поэтому, если вы читаете больше раз, а не считываете его в память за один раз, это занимает больше времени. Итак, из того, что я вижу здесь, почему мы readlines
все равно используем? Это стоит нам огромного объема памяти, если файл большой без увеличения скорости?
Комментарии:
1. Какова цель цикла for в первом тесте? Файл уже прочитан.
2. Я
while
подозреваю, что циклы получают переназначение для каждой строки.
Ответ №1:
На самом деле даже медленнее, когда readline
использовался в for
цикле:
import timeit
with open('test.txt', 'w') as fp:
print(*("Just a test case" for i in range(1000000)), sep='n', file=fp)
def a1():
with open('test.txt', 'r') as f:
for _ in f.readlines():
pass
def a2():
with open('test.txt', 'r') as f:
while _ := f.readline():
pass
def a3():
with open('test.txt', 'r') as f:
for _ in iter(f.readline, ''):
pass
print(timeit.timeit(a1, number=50))
print(timeit.timeit(a2, number=50))
print(timeit.timeit(a3, number=50))
вывод:
10.9471131
10.282239
9.3618919
При сравнении в одном и том же for
цикле, очевидно a3
, что way быстрее, чем a1
, хотя это аганистический Дзен python.
Причина этого кроется в исходном коде _pyio.py
и iobase.c
:
Когда iobase.c
недоступно _pyio.py
, будет использоваться чистый python.
def readlines(self, hint=None):
"""Return a list of lines from the stream.
hint can be specified to control the number of lines read: no more
lines will be read if the total size (in bytes/characters) of all
lines so far exceeds hint.
"""
if hint is None or hint <= 0:
return list(self)
n = 0
lines = []
for line in self:
lines.append(line)
n = len(line)
if n >= hint:
break
return lines
Он добавляет каждую прочитанную строку, для которой используется одна и та же механика readline
, — он вообще не загружает весь файл.
То же самое относится и к реализации C iobase.c
:
while (1) {
Py_ssize_t line_length;
PyObject *line = PyIter_Next(it);
if (line == NULL) {
if (PyErr_Occurred()) {
goto error;
}
else
break; /* StopIteration raised */
}
if (PyList_Append(result, line) < 0) {
Py_DECREF(line);
goto error;
}
line_length = PyObject_Size(line);
Py_DECREF(line);
if (line_length < 0) {
goto error;
}
if (line_length > hint - length)
break;
length = line_length;
}
Как вы видите, он вызывает PyList_Append
добавление результатов в список.
PS Просто напоминание, объединение — это полбеды, так как для конкатенации такого количества строк print
рекомендуется использовать sep
параметр with file
, . Не соединяйте строки там, где это не нужно.
Комментарии:
1. Это то, что я ищу. Итак, отсюда мы можем сказать, что только для повышения производительности мы всегда должны использовать readline в Python?
2. Как вы видите, с точки зрения производительности, использование
iter(f.readline, '')
в цикле for является самым быстрым — но это нарушает zen — особые случаи недостаточно особенные, чтобы нарушать правила — путем итерации без итератора с помощьюiter
hack. Только что протестировано на pypy3.6 , и я могу четко сказать, что нет более быстрого способа, если не использоватьC
реализацию.3. Даже в
readlines
документе говорится, что вы можете выполнять итерации, используяreadline
в первую очередь безreadlines
. Поэтому я подозреваю, что это обратная совместимость или что-то в этом роде.
Ответ №2:
Readlines
считывает весь текст в память перед запуском цикла, одновременно readline
автоматически считывая буфер во время цикла. Вот лучшее объяснение сравнения памяти между ними.