Поведение from-import с изменяемыми объектами в Python

Поведение from-import с изменяемыми объектами в Python

@python_quiz

Разберем этот квиз

Короткое условие

  • Есть файл util.py с содержимым: VALUE = []
  • В основном скрипте выполняются две операции импорта: from util import VALUE и import util
  • Затем вызывается VALUE.append(1) и печатается len(util.VALUE)

Покажем код явно.

Файлы

python
python

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

Почему именно так — пошаговый разбор

  1. При первом выполнении import util (неважно, какая из форм импорта выполнена раньше на практике), модуль util загружается в sys.modules и в его пространстве имён определяется имя VALUE, указывающее на пустой список.
  2. Оператор from util import VALUE копирует ссылку на объект, связанный с именем VALUE в модуле util, и создаёт в локальном (импортирующем) модуле своё имя VALUE, указывающее на тот же объект (тот же список).

- Важно: это не копирование содержимого списка, а копирование ссылки на объект.

  1. Оператор import util даёт доступ к самому модулю под именем util; внутри util есть своё имя VALUE, указывающее на тот же объект.
  2. Вызов VALUE.append(1) мутирует (изменяет) сам список — без переназначения имени — поэтому и в util.VALUE, и в локальной переменной VALUE виден один и тот же изменённый объект.
  3. Следовательно 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.

Report Page