Часто при разборе чужого кода можно встретить файл __main__.py или внутри самого скрипта увидеть конструкцию типа «if __name__ == __main__
». Сегодня разберёмся, что это за __main__ и зачем он нужен.
Как запускается скрипт на Python
Самый простой способ запустить код на Python — установить интерпретатор, а затем в командной строке написать python3 и указать имя файла со скриптом. Например, если у нас скрипт сохранён в файле script.py, то для запуска кода в терминале пишем такое:
python3 script.py
После этого компьютер возьмёт наш файл и попросит Python его выполнить. Так как Python — это интерпретируемый язык программирования, то код будет выполняться построчно, шаг за шагом.
Давайте это проверим и создадим простой скрипт, который сохраним в файле script.py:
def greet_user(user_name):
print(f'Привет, {user_name}!')
name = 'Паша'
greet_user(name)
Теперь копнём глубже и посмотрим, как именно компьютер начинает выполнять наш код. В каждом скрипте есть верхний уровень среды исполнения — то место, откуда интерпретатор начинает исполнение команд.
В нашем случае компьютер начинает с самой первой строчки и по очереди обрабатывает все остальные, сверху вниз: функция → тело функции → переменная → запуск функции. Чтобы в этом убедиться, поменяем местами последнюю пару строк кода и посмотрим, что получится:
def greet_user(user_name):
print(f'Привет, {user_name}!')
greet_user(name)
name = 'Паша'
Мы сразу получили ошибку: Python встретил переменную раньше, чем мы её объявили, и не знает, что с ней делать. А всё потому, что мы поменяли порядок строк, чтобы убедиться, что он важен и что компьютер обрабатывает и сразу выполняет команды в том порядке, как они написаны.
Посложнее: запуск во время импорта
В разных проектах мало кто обходится только одним скриптом: чаще всего разработчики подключают дополнительные библиотеки и внешние файлы для расширения возможностей программы. Чтобы подключить внешний файл, используют команду import:
import
Так можно подключить любой внешний скрипт, который уже умеет делать то, что нам нужно в новом проекте. Например, мы можем использовать наш файл script.py как внешний файл для импорта и подключить его в новой программе:
import script.py
Создадим новый файл new_script.py, в котором подключим первый скрипт, и посмотрим, что произойдёт при запуске:
import script
def wish_luck(user_name):
print(f'Желаю удачи, {user_name}')
script.greet_user('Катя')
wish_luck('Катя')
Странно, но у нас внезапно появилось приветствие Паши, хотя в основном скрипте этого нет. Давайте разберёмся, что тут происходит:
- Мы импортируем скрипт script.py.
- Компьютер в этой папке и в среде окружения ищет этот файл, находит и начинает построчно его выполнять. Так происходит для того, чтобы при вызове функции из импортированного файла она была уже загружена и компьютер знал, что с ней делать.
- В импортированном файле на третьей строке идёт объявление переменной, а на следующей — вывод приветствия на экран.
- Так как всё выполняется построчно, это приветствие сработает при импорте, и мы поприветствуем Пашу даже до запуска основного кода в new_script.py.
Получается, что при импорте Python переходит к новому файлу и начинает тоже выполнять его шаг за шагом. Тогда как нам подключить первый скрипт, чтобы оттуда взять только функцию приветствия, но без исполнения остального кода? И вот здесь нам понадобится __main__
.
Что такое __main__
Каждый скрипт в Python выполняется на каком-то уровне. При запуске напрямую из командной строки скрипт выполняется на верхнем уровне: мы запустили его напрямую, а не из другого скрипта. Верхний уровень исполнения называется __main__
, и это значение хранится в скрытой переменной __name__
. Её можно посмотреть командой:
print(__name__)
Добавим эту команду в первый скрипт в самое начало и посмотрим на её значение:
print(__name__)
def greet_user(user_name):
print(f'Привет, {user_name}!')
name = 'Паша'
greet_user(name)
Получается, при прямом запуске код запускается на самом верхнем уровне, а само значение уровня хранится в переменной __name__
. Теперь запустим второй файл, где мы импортируем наш скрипт, и посмотрим, какое будет значение этой переменной при импорте, а не при запуске напрямую:
Смотрите, что здесь произошло:
- Компьютер начал работать с файлом new_script.py.
- Первой строчкой он встретил команду импорта и пошёл работать с файлом script.py.
- В этом файле первая команда — вывод значения уровня запуска скрипта.
- Компьютер знает, что он запускает второй скрипт не напрямую, а из файла new_script.py, поэтому значение
__name__
становится уже не__main__
, аscript
. - При этом всё остальное выполняется точно так же, как и раньше, включая приветствие Паши.
Получается, мы в скрипте всегда можем узнать уровень выполнения кода, и, если он верхний, можно выполнить весь код, а если нет — то какую-то его часть. Чтобы это сделать, нам понадобится условный оператор.
Что делает if __name__ == '__main__'
Проверить, на каком уровне работает текущий код, можно командой:
if __name__ == '__main__'
Она смотрит, совпадает ли уровень выполнения с верхним, и в зависимости от ответа можно настроить выполнение разных фрагментов кода.
Сделаем так, чтобы в первом скрипте мы приветствовали Пашу только при верхнеуровневом запуске, из командной строки. Для этого добавим проверку уровня перед выводом приветствия:
print(__name__)
def greet_user(user_name):
print(f'Привет, {user_name}!')
if __name__ == '__main__':
name = 'Паша'
greet_user(name)
Пока всё выглядит так же, как и раньше, не считая вывода уровня исполнения. А теперь запустим второй файл, где мы импортировали script.py, и посмотрим, что изменилось:
Часто конструкция if __name__ == '__main__'
используется для автозапуска основного кода в главном скрипте — компьютер доходит до этой строчки, проверяет уровень запуска и выполняет всё, что идёт внутри. Это позволяет обойтись без главной функции, которую мы обычно вызываем в конце для запуска кода. А ещё это убережёт от автозапуска ненужного кода при импорте этого файла в другой проект.
Какие ещё есть способы запуска кода
Есть ещё один способ запустить код — использовать модуль runpy. Способ пригодится, когда мы хотим что-то запустить внутри основного скрипта, но при этом ничего не импортировать. Добавим нужный код в скрипт new_script.py:
import runpy
import script
def wish_luck(user_name):
print(f'Желаю удачи, {user_name}')
script.greet_user('Катя')
wish_luck('Катя')
runpy.run_path('script.py', run_name='__main__')
Обратите внимание, как менялся уровень запуска script.py:
- При импорте он был равен script.
- Потом выполнилось основное содержимое файла new_script.py.
- Затем мы сказали запустить файл script.py и указали для него уровень выполнения
__main__
. - Первый скрипт увидел, что у него высший уровень выполнения, и вывел значение
__main__
, а потом поприветствовал Пашу, потому что сработало условие уровня.
Что дальше
В следующий раз мы добавим работу с уровнями запуска в новый проект и посмотрим, как это помогает управлять работой скрипта. А пока расскажите в комментариях, часто ли вы пользуетесь такими конструкциями или обходитесь вообще без них.