Поведение import vs from ... import в Python
@python_quizРазберем этот квиз
Кратко: рассматриваем фрагмент, где сначала импортируется модуль, затем меняется его атрибут, а затем выполняется from ... import. Посмотрим, что напечатает и почему.
Условие (исходный код)

Для наглядности предполагаем, что внутри pkg/utils.py изначально было что-то вроде:

(Это обычное предположение, объясняющее, откуда взялась альтернатива "10" в вариантах ответа.)
Что выводится и почему (кратко)
В данном порядке операций в консоль будет выведено число 20. Пояснение: первый импорт получает модуль pkg.utils как объект; затем присваиванием utils.VALUE = 20 мы меняем атрибут модуля. Когда выполняется from pkg.utils import VALUE, Python читаёт текущее значение атрибута в модуле и связывает локальное имя VALUE с этим значением (в момент импорта). Поскольку в модуле сейчас 20, импортированное имя получает значение 20.
Пошаговая расшифровка
from pkg import utils— загружается пакетpkg, его атрибутutils(модуль) становится доступен под именемutils. Это эквивалентноimport pkg.utils as utilsв плане получения объекта модуля.utils.VALUE = 20— изменение атрибутаVALUEв объекте модуляutils. Теперь внутриpkg.utilsимяVALUEуказывает на целое 20.from pkg.utils import VALUE— при этом выполняется отдельное связывание имениVALUEв текущей области видимости: Python берет текущее значениеpkg.utils.VALUE(то есть 20) и присваивает его локальному имениVALUE.print(VALUE)— печатает локальноеVALUE, равное 20.
Важно: from ... import копирует (связывает) объект в текущем момент, а не создаёт "живую ссылку" на атрибут модуля, если затем модуль переопределяет имя (в случае неизменяемых объектов эффект как копирование значения).
Анализ вариантов ответов
- 10 — такое получилось бы, если бы сначала выполнили
from pkg.utils import VALUE, а затем уже изменилиutils.VALUE = 20. В текущем порядке изменение происходит доfrom ... import, поэтому этот вариант не соответствует. - 20 — соответствует описанному порядку операций: изменение атрибута модуля происходит до
from ... import, поэтому импортирует уже обновлённое значение. ImportError— не произойдет, если пакет и модуль существуют и корректно импортируются. В данном фрагменте явных причин дляImportErrorнет.- None — не появится, так как
VALUEявно присвоено числовое значение; печатать будет неNone.
Демонстрация: что меняет порядок
Ниже пара примеров, иллюстрирующих разницу порядка и поведение при мутабельных объектах.
1) Если сначала импортируем значение, затем меняем атрибут модуля:

2) Порядок как в квизе (сначала модуль, потом изменение, потом from-import):

3) Пример с изменяемым объектом (список):

Вывод: при работе с изменяемыми объектами важно различать мутацию (внутри объекта) и переназначение имени в модуле.
Выводы и рекомендации
- Оператор
from module import nameсвязывает имя в момент выполнения инструкции, беря текущее значение из модуля. - Изменение атрибута модуля до
from ... importповлияет на импортируемое значение; изменение после — нет (если это переназначение имени). - Для избежания путаницы и неожиданных рассинхронизированных значений предпочтительнее использовать полные импортные формы (import module) и обращаться через
module.name, особенно если значение может меняться. - При работе с изменяемыми объектами помните:
from ... import objдаёт ссылку на тот же объект, поэтому его внутренняя мутация будет видна в обоих местах; но переназначение имени в модуле не обновит локальное имя.