Модули и пакеты
Импорт в Python — не текстовая подстановка и не «подключение заголовка», как в C. import mymod означает четыре отдельных действия: найти файл по списку sys.path, исполнить его сверху вниз ровно один раз, сложить получившиеся имена в объект модуля и связать этот объект с именем в текущем пространстве имён. Модуль поэтому — полноценный объект первого класса, а не строчка в системе сборки: его можно передать в функцию, положить в список, прочитать через getattr. Пакет — тот же объект модуля, у которого есть атрибут __path__ со списком директорий для поиска вложенных модулей.
Из двух пунктов — «исполнить ровно один раз» и «найти по sys.path» — вырастают почти все практические проблемы темы. Повторный import не перечитывает файл, а достаёт готовый объект из словаря sys.modules, поэтому код верхнего уровня выполняется однократно за процесс, а глобальные переменные модуля работают как синглтон программы. Поиск идёт по директориям по порядку и останавливается на первом совпадении, поэтому собственный queue.py рядом со скриптом молча заменяет стандартный для всего процесса. А from module import name копирует ссылку на объект, а не связь с модулем — после этого чужие присваивания в module.name до вашей копии не доходят. Разберите каждый механизм по слоям ниже.
Карта темы
- Модуль и его выполнение — что создаёт
import, почему файл исполняется один раз и как устроено пространство имён модуля. - Как работает import —
sys.modules, порядокsys.path, правило первого совпадения, finder и loader, циклические импорты. - Пакеты и namespace-пакеты —
__init__.py, атрибут__path__, относительные импорты и пакеты без__init__.pyпо PEP 420. - Точка входа и
__name__— откуда берётся"__main__", зачем нужна защита и чемpython -mотличается от запуска файла.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Считать, что каждый import заново исполняет файл | Файл исполняется один раз за процесс; дальше import лишь достаёт объект из sys.modules и связывает имя |
| Думать, что Python ищет модуль по всей файловой системе | Поиск идёт строго по списку sys.path и заканчивается на первой подходящей директории |
| Называть свой файл именем модуля стандартной библиотеки | Каталог скрипта стоит в sys.path первым, и ваш random.py затеняет стандартный для всей программы, включая чужие библиотеки |
Считать sys.path неизменяемым кортежем | Это обычный изменяемый list, который правят в рантайме, — и именно поэтому порядок записей становится источником трудноуловимых багов |
Ждать, что from config import DEBUG увидит более позднее config.DEBUG = True | Скопировано значение, а не связь с модулем; локальное имя DEBUG продолжает держать старый объект |
Считать, что import package подтягивает вложенные модули | Исполняется только __init__.py; package.sub доступен, лишь если его импортировали явно |
Требовать __init__.py от любого пакета | С PEP 420 директория без __init__.py импортируется как namespace-пакет и может собираться из нескольких путей сразу |
Запускать файл пакета как python pkg/mod.py | __package__ не заполнен, относительные импорты падают, а при последующем import pkg.mod файл исполнится второй раз под другим именем |
Значение для собеседований
Тема считается базовой и спрашивается в основном на junior- и middle-позициях, но отвечают на неё плохо непропорционально часто. Стандартный набор — «что такое module», «что делает директорию пакетом», «что такое __name__». Ответ уровня «module — это файл .py» принимают, но сразу уточняют, что произойдёт при втором импорте того же модуля. Правильный ответ называет sys.modules и однократное выполнение; неправильный — «файл прочитается снова» — переводит разговор в разбор того, почему модульные глобальные переменные ведут себя как синглтоны и почему importlib.reload вообще существует. Второй обязательный вопрос — про __name__, и здесь ждут не заученную формулу, а объяснение, зачем разделять импорт и запуск.
На middle-уровне добавляют вопрос о том, как интерпретатор находит модуль. Ждут sys.path, порядок просмотра и правило первого совпадения — и почти всегда следом просят предсказать, что случится, если положить рядом со скриптом файл с именем модуля стандартной библиотеки. Senior-вариант темы — namespace-пакеты из PEP 420: чем пакет без __init__.py отличается от обычного и зачем разносить один логический пакет по нескольким отдельно устанавливаемым дистрибутивам. Типичная ошибка здесь одна — утверждать, что __init__.py обязателен всегда; вторая по частоте — считать namespace-пакетом обычный пакет с пустым __init__.py, хотя разница принципиальная, ведь только namespace-пакет собирается из нескольких директорий sys.path.