Скорость понимания списка и генератора с функцией соединения
Итак, я получил эти примеры из официальной документации. https://docs.python.org/2/library/timeit.html
Что именно делает первый пример (выражение генератора) медленнее, чем второй (понимание списка)?
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582
>>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
0.7288308143615723
1 ответ
str.join
Метод преобразует свой итеративный параметр в список, если он уже не является списком или кортежем. Это позволяет логике объединения выполнять итерации по элементам несколько раз (выполняется один проход для вычисления размера строки результата, а затем второй проход для фактического копирования данных).
Вы можете увидеть это в исходном коде CPython:
PyObject *
PyUnicode_Join(PyObject *separator, PyObject *seq)
{
/* lots of variable declarations at the start of the function omitted */
fseq = PySequence_Fast(seq, "can only join an iterable");
/* ... */
}
PySequence_Fast
Функция в C API делает именно то, что я описал. Он преобразует произвольную итерацию в список (по сути, вызывая list
на нем), если это уже не список или кортеж.
Преобразование выражения генератора в список означает, что обычные преимущества генераторов (меньший объем памяти и возможность короткого замыкания) не применяются к str.join
и поэтому (небольшие) дополнительные накладные расходы, которые имеет генератор, ухудшают его производительность.