__name__ == '__main__' и python -m: как запускается модуль

У каждого загруженного модуля есть глобальный атрибут __name__. Если файл выбран как верхняя точка выполнения, Python задаёт ему имя __main__. Если тот же файл импортирован по имени пакета, __name__ содержит это имя. Проверка if __name__ == "__main__" позволяет отделить определение функций от действий командной строки.

Верхнеуровневая среда

Верхнеуровневым может быть файл, переданный как python script.py, модуль, выбранный через python -m package.module, код из -c, стандартный ввод или интерактивная сессия. Во всех этих случаях выполняемая точка получает __name__ == "__main__". Это специальное имя не означает, что файл обязательно называется main.py.

При обычном импорте верхнеуровневые инструкции файла тоже выполняются, но условный блок не срабатывает. Поэтому определения функций и констант остаются доступны тестам и другим модулям без непрошенного запуска CLI.

Функция main и защитная проверка

Удобно передавать аргументы функции и возвращать код завершения, а в защитном блоке делать только один явный вызов:

demo/
└── greeting/
    ├── __init__.py
    ├── cli.py
    └── message.py

greeting/message.py содержит:

def greeting(name):
    return f"Привет, {name}!"

greeting/cli.py содержит:

import sys

from .message import greeting


def main(argv=None):
    args = sys.argv[1:] if argv is None else argv
    name = args[0] if args else "мир"
    print(greeting(name))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Из каталога demo команда python -m greeting.cli Ada печатает Привет, Ada! и завершается с кодом 0. Вызов python -c "import greeting.cli; print('импорт готов')" печатает только импорт готов.

Чем python -m отличается от пути к файлу

Ключ -m просит импортную систему найти модуль по полному имени и выполнить его как __main__. При этом пакетный контекст известен, поэтому относительный импорт from .message import greeting работает. Команду нужно запускать из места, где верхний пакет доступен для импорта, либо после установки пакета.

При python greeting/cli.py интерпретатор получает путь к отдельному файлу, а не полное имя внутри пакета. У него нет того же пакетного контекста. Эти способы запуска не взаимозаменяемы для внутреннего модуля. Для запуска целого пакета через python -m greeting нужен модуль greeting/__main__.py.

Почему относительный импорт ломается при прямом запуске

Характерная ошибка прямого запуска — ImportError: attempted relative import with no known parent package. Не заменяйте относительный импорт на случайный абсолютный только ради одной команды. Перейдите в каталог над greeting и выполните python -m greeting.cli. Для диагностики временно выведите __name__, __package__ и sys.path[0]: при пакетном запуске __package__ будет greeting, а при запуске файла пакетный контекст отсутствует. Отдельно убедитесь, что используется ожидаемое виртуальное окружение.

Собираем два пакетных CLI-входа

Добавьте greeting/__main__.py, который импортирует main из cli и вызывает его через SystemExit. Сравните python -m greeting Ada, python -m greeting.cli Ada и импорт greeting.cli. Затем вызовите main(["Lin"]) из небольшого тестового сценария и проверьте вывод без изменения глобального sys.argv. Самостоятельная проверка завершена, если импорт не печатает приветствие, а оба CLI-входа возвращают ожидаемый код.

Правила запуска импортируемого модуля

Вопросы о main и запуске пакета

Обязательна ли функция с именем main?

Нет, Python не ищет её автоматически. Это соглашение, которое помогает вынести логику из условного блока и сделать точку входа понятной.

Зачем передавать результат main в SystemExit?

SystemExit превращает целое возвращённое значение в код завершения процесса. При обычном импорте и прямом вызове функции исключение не возникает, пока защитный блок не выполняется.

Когда нужен файл __main__.py?

Он нужен, когда сам пакет должен запускаться командой python -m package. Для команды python -m package.module выполняется выбранный модуль, и отдельный __main__.py не требуется.

Источники