Что происходит с этим кодом
@python_quizРазберем этот квиз

Коротко: это код, который никогда не завершится корректным результатом — итерация по изменяемому списку с добавлением элементов в процессе приведёт к бесконечному росту списка и зависанию (пока программа не будет прервана или не закончится ошибкой из‑за исчерпания памяти).
Почему так происходит — разбор механики
- list comprehension создаёт итератор по исходному списку nums и последовательно запрашивает следующие элементы по индексу.
- Внутри comprehension вызывается nums.append(i). Метод append меняет сам список nums и возвращает None.
- Поскольку итератор смотрит на текущую длину списка при каждой итерации, добавленные элементы становятся частью последовательности итерации — итератор будет «видеть» вновь добавленные элементы и продолжать их обрабатывать.
- В нашем случае мы добавляем те же значения, которые итерируем, поэтому список будет расти бесконечно: 1 → 1 (добавлен), 2 → 2, 3 → 3, затем вновь добавленный 1 будет итерирован и снова добавлен и т.д.
Дополнительно: метод append возвращает None, поэтому собираемый результат res будет содержать значения None (если бы итерация хотя бы завершилась).
Анализ вариантов вывода (почему они неверны)
- "[1, 2, 3] [None, None, None]"
Такое поведение означало бы, что список nums не изменился, а comprehension выполнил три вызова append и вернул по три None. Но append изменяет nums, поэтому nums не останется прежним.
- "[1, 2, 3, 1, 2, 3] [None, None, None]"
Здесь подразумевается, что nums удвоился, а res записал только три значения None. Но если nums удваивается в процессе итерации, итератор продолжит идти дальше и добавит ещё элементы — res тогда тоже будет расти. Получить ровно три None и одновременно модифицированный nums в этом случае нельзя.
- "[1, 2, 3, 1, 2, 3] [1, 2, 3]"
Этот вариант неверен сразу по двум причинам: append возвращает None, а не добавленное значение, так что res не может быть списком [1,2,3]; и как выше — итерация по добавляемым элементам продолжит выполняться.
- "Бесконечный цикл или ошибка"
Это именно тот случай: итерация по изменяемому списку с добавлением элементов в процессе приводит к бесконечному увеличению и, фактически, к бесконечному циклу (пока не возникнет внешняя остановка или переполнится память).
Демонстрация безопасного поведения (и что делать правильно)
1) Чтобы получить «удвоение» списка — и избежать бесконечной итерации — итерируйтесь по копии:

2) Если вам нужно просто создать новый список на основе существующего — используйте list comprehension без побочных эффектов:

3) Если требуется добавить в список все его элементы ещё раз — проще и эффективней использовать extend:

4) Показываем, что append возвращает None:

Небольшая имитация, чтобы увидеть рост без зависания программы
Если вы хотите «посмотреть» как растёт список при такой ненадёжной конструкции, можно ограничить количество итераций вручную:

Этот пример демонстрирует механизм (nums будет расти, res будет содержать None), но не приведёт к бесконечному ожиданию, потому что мы искусственно ограничили количество шагов.
Вывод
Использование list comprehension для побочных эффектов (вызовов, которые меняют итерируемый объект) — крайне плохая практика. В данном примере итерация по изменяемому списку с append приводит к бесконечному добавлению элементов. Всегда либо итерируйтесь по копии, либо применяйте методы, предназначенные для пакетного изменения (extend), либо используйте явные циклы и контролируемую логику.