Поведение from-import с изменяемыми объектами в Python
@python_quizРазберем этот квиз
Короткое условие
- Есть файл util.py с содержимым: VALUE = []
- В основном скрипте выполняются две операции импорта:
from util import VALUEиimport util - Затем вызывается
VALUE.append(1)и печатаетсяlen(util.VALUE)
Покажем код явно.
Файлы


Ожидаемый вывод

Почему именно так — пошаговый разбор
- При первом выполнении
import util(неважно, какая из форм импорта выполнена раньше на практике), модуль util загружается в sys.modules и в его пространстве имён определяется имя VALUE, указывающее на пустой список. - Оператор
from util import VALUEкопирует ссылку на объект, связанный с именем VALUE в модуле util, и создаёт в локальном (импортирующем) модуле своё имя VALUE, указывающее на тот же объект (тот же список).
- Важно: это не копирование содержимого списка, а копирование ссылки на объект.
- Оператор
import utilдаёт доступ к самому модулю под именем util; внутри util есть своё имя VALUE, указывающее на тот же объект. - Вызов
VALUE.append(1)мутирует (изменяет) сам список — без переназначения имени — поэтому и в util.VALUE, и в локальной переменной VALUE виден один и тот же изменённый объект. - Следовательно
len(util.VALUE)равен 1.
Анализ вариантов ответов (почему остальные неверны)
- "Выведет 0" — неверно, потому что список был изменён методом append; если бы не было изменений, длина была бы 0, но append добавил элемент.
- "Выведет 2" — неверно; чтобы получить 2, нужно было бы добавить элемент дважды (например, вызвать append два раза) либо работать с двумя разными списками и суммировать их длины. Здесь происходит одно добавление в один список.
- "Ошибка импорта" — неверно, при условии, что файл util.py существует и содержит определение VALUE. Обычные формы импорта корректны и не вызовут ошибку.
Дополнительные замечания и частые ловушки
- Отличие между мутацией объекта и переназначением имени:
- Если вместо VALUE.append(1) сделать VALUE = [1], то локальная переменная VALUE начнёт указывать на новый список, а util.VALUE останется прежним пустым списком.
- Пример:
`python
# main.py
from util import VALUE
import util
VALUE = [1] # локальная NAME указывает на новый объект
print(len(util.VALUE)) # всё ещё 0
`
from module import nameне создаёт «копию значения» для неизменяемых объектов? На самом деле семантика одинакова: импорт копирует ссылку на объект (вне зависимости от того, изменяем он или нет). Разница проявляется только при мутации — только для изменяемых объектов (list, dict, set и т.д.) это будет заметно.- Порядок импорта может влиять при наличии побочных эффектов в модуле, но в данном простом примере ключевую роль играет то, что оба имени ссылаются на один и тот же объект в памяти.
Короткое резюме
- from-import копирует ссылку на объект, а не выполняет глубокое копирование.
- Мутации изменяемых объектов будут видны через все имена, которые ссылаются на этот объект.
- В приведённом коде результатом будет вывод числа 1.