Python: понимание итераторов и `join()` лучше

Функция join() принимает итеративный параметр. Однако мне было интересно, почему:

text = 'asdfqwer'

Это:

''.join([c for c in text])

Значительно быстрее:

''.join(c for c in text)

То же самое происходит с длинными строками (т.е. text * 10000000).

Наблюдая за объемом памяти обоих исполнений с длинными строками, я думаю, что оба они создают один и только один список символов в памяти, а затем присоединяют их к строке. Поэтому я предполагаю, что разница заключается только в том, как join() создает этот список из генератора и как интерпретатор Python делает то же самое, когда видит [c for c in text]. Но, опять же, я просто догадываюсь, поэтому я хотел бы, чтобы кто-то подтвердил/отклонил мои догадки.

Ответ 1

Метод join читает свой ввод дважды; один раз, чтобы определить, сколько памяти выделяется для результирующего строкового объекта, затем снова выполнить фактическое соединение. Передача списка происходит быстрее, чем передача объекта-генератора, что ему нужно сделать копию, чтобы она могла перебирать ее дважды.

Понимание списка - это не просто объект-генератор, завернутый в список, поэтому построение списка извне происходит быстрее, чем join создать его из объекта-генератора. Объекты генератора оптимизированы для эффективности памяти, а не скорости.

Конечно, строка уже является итерируемым объектом, поэтому вы можете просто написать ''.join(text). (Кроме того, это не так быстро, как создание списка явно из строки.)