Как Python связывает имена при import и from import
@python_quizРазберем этот квиз
Коротко: в представленном фрагменте на экран будет выведено 20. Ниже — почему так происходит, разбор вариантов и примеры, которые проясняют поведение импорта в разных сценариях.
Условие (код из квиза)

Предполагается, что внутри модуля pkg.utils изначально было какое-то значение VALUE (например, 10). Важно последовательность операций: сначала импорт модуля под именем utils, затем присвоение utils.VALUE = 20, затем из модуля импортируется имя VALUE в локальную область видимости.
Короткое объяснение поведения
- Операция import pkg.utils as utils загружает модуль и создаёт локальную переменную utils, указывающую на объект модуля.
- Присвоение utils.
VALUE = 20изменяет атрибут VALUE у объекта модуля. - Оператор from pkg.utils import VALUE связывает локальное имя VALUE с тем объектом, на который в данный момент указывает атрибут VALUE в модуле.
- Поэтому локальное VALUE получит значение 20, и print(VALUE) выведет 20.
Главная идея: from ... import привязывает локальное имя к текущему объекту из модуля в момент выполнения импорта.
Разбор вариантов (логика)
- Будет выведено 10 — такое произошло бы, если бы из модуля VALUE был импортирован до того, как utils.VALUE сменили на 20. Здесь же присвоение происходит ДО from import.
- Будет выведено 20 — соответствует ситуации с присвоением до from import (как в коде).
- Будет выведено None — потребовалось бы, чтобы VALUE действительно было None в модуле на момент from import; это не следует из кода.
- Возникнет ошибка импорта — возникнет только если модуль pkg.utils вообще не найден/не загружается. В рамках примера модуль предполагается доступным.
Демонстрация: воспроизвести поведение в одном файле
Чтобы не создавать файловой структуры пакета, можно временно подставить модуль в sys.modules:

Этот пример показывает, что присвоение атрибута модуля до from import влияет на значение локальной переменной VALUE.
Важное уточнение: mutable vs immutable
Поведение зависит не только от времени привязки, но и от того, изменяется ли объект путём присваивания или мутации.
Пример с изменяемым объектом (список):

Выводы:
- Если объект мутируется (методы, изменение содержимого), обе ссылки (в модуле и локальная) указывают на один и тот же объект и изменения видны.
- Если же атрибут модуля переназначается (utils.VALUE = новый_объект), локальная переменная, импортированная ранее через from, не поменяет свою привязку — она всё ещё указывает на старый объект.
Дополнительные замечания
- from ... import фактически выполняет привязку имени в текущую область видимости — не «волшебную связь» с модулем, а обычное присваивание имени.
- import pkg.utils as utils и from pkg.utils import VALUE дают разные способы доступа: первый даёт доступ к самому модулю и его атрибутам динамически, второй — локальную копию ссылки на объект.
- Если модуль действительно отсутствует, то оба импорта вызовут
ImportError/ModuleNotFoundError.
Итог
Поведение в примере определяется порядком операций и семантикой from ... import: локальное имя привязывается к тому объекту, который в модуле доступен на момент импорта. Для неизменяемых объектов (int, str и т.д.) переназначение атрибута модуля после from не повлияет на локальную переменную; для изменяемых объектов видны мутации, но не переназначения.