Поведение import и from-import в Python
@python_quizРазберем этот квиз
Код из квиза:

Коротко — программа выведет:

Дальше объясню, почему так происходит, разберу все варианты ответов и покажу тонкости import-механизма.
По шагам: что делает каждый оператор
import math as m
- Загружает встроенный модуль math и создаёт локальную привязку m к модулю. Также соответствующий объект модуля оказывается в sys.modules['math'].
m_used = m.pi
- Берёт значение константы pi из модуля m и сохраняет в m_used. Это просто использование ранее импортированного модуля.
math = 42
- Создаёт (или переопределяет) переменную math в текущей области видимости и связывает её с числом 42. Это затеняет имя math в глобальном/локальном пространстве имён, но не трогает sys.modules.
from math import sqrt
- Оператор from <module_name> import <name> использует механизм импорта по имени модуля (через import machinery и sys.modules), а не текущую переменную с именем math. То есть модуль ищется и загружается по строковому имени 'math', а затем из него извлекается sqrt. Полученная функция связывается в текущем пространстве имён как sqrt.
print(math, sqrt(4))
- Выводит значение переменной math (которое равно 42) и результат вызова sqrt(4) (равный 2.0).
Ключевой момент: присвоение имени math = 42 не мешает импорту from math import ..., потому что импорт идёт по имени модуля, а не по текущему значению переменной math.
Разбор вариантов ответов
- "Выведет: 42 2.0"
Обоснование: как показано выше, имя math было перезаписано числом, а sqrt импортирована из модуля math (через import machinery). Именно это и распечатается.
- "Выведет: 3.141592653589793 2.0"
Неверно, потому что к моменту печати имя math уже связано с 42. Если бы не было присвоения math = 42, тогда math действительно ссылался бы на модуль и math.pi дал бы pi. Но в данном коде мы явно переназначили math.
- "
ImportError: cannot import name 'sqrt'"
Неверно в обычных условиях, потому что модуль math входит в стандартную библиотеку и содержит sqrt. Оператор from math import sqrt успешно найдёт и импортирует sqrt. Единственная ситуация, когда такое исключение могло бы возникнуть — если модуль math был бы удалён из sys.modules и поиск не нашёл бы подходящего модуля (или обычное окружение модифицировано нетипичным образом).
- "
NameError: name 'math' is not defined"
Неверно: имя math действительно определено — мы присвоили ему значение 42. NameError возник бы только если бы перед печатью имя math не было бы ни импортировано, ни присвоено.
Дополнительные замечания и примеры
- Оператор
from <module> import nameработает с модулем по его строковому имени. Даже если в текущей области видимости есть переменная с таким именем, это не влияет на то, откуда берутся объекты для импорта. - Пример, иллюстрирующий ту же идею:

Выведет:

- Если же вы вручную удалите запись о модуле из
sys.modulesи помешаете загрузке (и не будет доступных загрузчиков), тогдаfrom math import sqrtможет попытаться заново загрузить модуль или выдать ошибку. В нормальном коде такие манипуляции редки и не типичны.
Вывод
Имя переменной в текущей области видимости и механизм импорта — разные вещи. Переменная math = 42 не мешает from math import sqrt, потому что импорт обращается к модулю по имени в системе модулей Python, а затем привязывает нужные символы в текущем пространстве имён. Поэтому код печатает число, присвоенное переменной, и результат работы импортированной функции.