Что происходит при запуске команды python main.py
Что проверяют
Представляете ли вы, что делает интерпретатор до первой строки вашего кода. Вопрос открытый: по нему видно, знает ли кандидат про компиляцию в байт-код, про sys.path и про __main__, или считает, что Python читает файл построчно.
Как отвечать
Оболочка находит исполняемый файл python по PATH (в активированном виртуальном окружении это его шим) и запускает, передав имя скрипта аргументом. Интерпретатор инициализируется: читает переменные окружения, собирает sys.path, поднимает встроенные модули.
Дальше исходник читается целиком, разбирается в дерево и компилируется в байт-код, и только потом байт-код исполняет вычислительный цикл CPython. Поэтому синтаксическая ошибка в последней строке не даст выполниться и первой.
Модуль исполняется под именем __main__, отсюда смысл проверки:
if __name__ == "__main__":
main()Тот же файл, импортированный откуда-то ещё, получит обычное имя, и блок не сработает. Импорты по ходу выполняют код модулей сверху вниз ровно один раз и кладут их в sys.modules.
Полезная деталь: в начало sys.path попадает каталог самого скрипта, а не текущий каталог. При запуске через python -m в начале оказывается текущий каталог, и это самая частая причина расхождения «локально импортируется, на сервере нет».
В конце интерпретатор доводит дело до финализации: отрабатывают обработчики atexit, закрываются файлы, код возврата берётся из sys.exit или равен единице при необработанном исключении.
Где спотыкаются
Говорят, что Python не компилируется, а интерпретирует строку за строкой. Компиляция есть, просто она в байт-код и происходит при каждом запуске.
Второе: думают, что __pycache__ ускоряет и сам скрипт. Кэшируются только импортируемые модули, запускаемый файл компилируется заново всегда, поэтому от кэша его старт не зависит.
Третье: путают, какой интерпретатор поедет. Решает PATH и окружение, а не шебанг в первой строке: шебанг учитывается, только когда файл запускают как исполняемый, а при явном python main.py игнорируется.