Разберите механизм: как Python определяет, выполняется модуль напрямую или импортируется?
Python определяет это по значению специальной переменной __name__. При непосредственном запуске файла она равна "__main__", а при импорте содержит имя модуля. Поэтому код, предназначенный только для запуска, помещают под условие проверки __name__.
Python-модуль может одновременно быть библиотекой для импорта и самостоятельной программой. При импорте Python выполняет инструкции верхнего уровня, поэтому без специального разграничения запуск программы мог бы неожиданно происходить уже во время подключения модуля.
Переменная __name__ предоставляет модулю информацию о контексте выполнения без отдельного режима или специального синтаксиса для объявления точки входа.
Если код запуска приложения, демонстрационный пример или разбор аргументов находится на верхнем уровне модуля, он выполнится и при импорте. Это приводит к побочным эффектам: запуску ненужных операций, конфликту с тестами, повторному выводу или завершению процесса во время загрузки библиотеки.
Дополнительная проблема возникает при повторном использовании функций модуля: определения обычно нужны импортирующему коду, а запуск сценария — нет. Эти два поведения необходимо разделить.
При прямом запуске файла Python присваивает его глобальному имени значение "__main__". При импорте того же файла значение обычно соответствует имени модуля, например "package.tool".
При импорте определение main будет создано, но вызов под условием не произойдёт. При прямом запуске условие станет истинным, и функция выполнится.
Проверка управляет только кодом внутри блока: инструкции верхнего уровня до неё всё равно выполняются при импорте. Поэтому создание объектов, регистрация обработчиков и другие побочные эффекты также требуют аккуратного размещения.
Запуск через python -m package.tool также обычно устанавливает для выполняемого модуля __name__ равным "__main__". Это позволяет использовать тот же механизм для модулей, запускаемых как части пакета.
Ограничение подхода в том, что он не является полноценной системой командного интерфейса: обработку аргументов, коды завершения и конфигурацию всё равно нужно проектировать отдельно. Часто блок оставляют минимальным и передают управление функции main, чтобы её можно было тестировать напрямую.
В проекте есть модуль с функциями преобразования данных и режимом запуска из командной строки. Если разбор аргументов и запуск преобразования находятся на верхнем уровне, импорт модуля в тестах немедленно запускает обработку и может завершить процесс из-за отсутствующих аргументов.
Вариант с отдельным файлом запуска устраняет побочные эффекты, но добавляет ещё один модуль и дублирование точки входа. Вариант с безусловным запуском при импорте проще, но непригоден для библиотеки и тестирования.
Выбирают функцию main и вызывают её только при __name__ == "__main__". В результате импорт предоставляет определения без запуска программы, прямой запуск сохраняет ожидаемое поведение, а тесты могут вызывать функции отдельно с подготовленными аргументами.
Что именно выполняется при импорте модуля, если точка входа защищена проверкой __name__?
Python всё равно выполняет инструкции верхнего уровня последовательно: создаёт функции и классы, вычисляет выражения и выполняет незакрытые вызовы. Не выполняется только код, находящийся внутри условного блока с проверкой __name__, если значение не равно "__main__".
Почему вызов функции main обычно помещают внутрь защищённого блока, а не оставляют саму логику на верхнем уровне?
Функция отделяет описание поведения от момента запуска. Это уменьшает побочные эффекты при импорте, позволяет передавать параметры явно и облегчает модульное тестирование. Сам блок становится небольшой декларативной точкой входа, а не местом, где сосредоточена вся логика программы.
Всегда ли прямой запуск файла и запуск модуля через -m дают одинаковое значение __name__?
Для выполняемого модуля в обоих случаях обычно используется значение "__main__", поэтому защищённый блок срабатывает. Однако контекст импорта, значения __package__, пути поиска и способы разрешения относительных импортов могут отличаться. Поэтому для пакетных приложений запуск через -m часто надёжнее прямого запуска файла, но это не отменяет необходимости корректно организовать импорты.