Все статьи

if __name__ == "__main__" в Python: как работает и зачем нужно

if __name__ == "__main__" в Python: как работает и зачем нужно

Строка if __name__ == "__main__": встречается почти в каждом Python-проекте, и почти каждый новичок задаётся вопросом: зачем? Ведь можно просто написать код в файле, и он выполнится. Можно — пока файл никто не импортирует и пока вы не запускаете процессы. Дальше без этой проверки начинаются ошибки, которые можно отлаживать днями.

Разберём, как работает if __name__ == "__main__", откуда берётся __name__, зачем нужна точка входа и какие две ошибки чаще всего возникают без неё.

Точка входа: откуда идея

Во многих языках — C, C++, C#, Java — выполнение программы начинается с функции main. Даже у исполняемого файла есть точка входа, с которой процессор начинает выполнять код. В Python можно писать без этого: интерпретатор просто выполняет файл построчно сверху вниз.

Но у Python тоже есть общепринятый способ обозначить точку входа:

def main() -> None:
    print("Программа запущена")


if __name__ == "__main__":
    main()

Причин две: это стандарт, понятный любому разработчику, и это способ узнать, как именно запущен файл — напрямую или импортирован как модуль.

Что такое __name__

Когда Python загружает модуль, он создаёт для него набор глобальных переменных. Их можно посмотреть через globals() — это словарь, среди ключей которого есть __name__.

Значение зависит от способа запуска:

  • если файл запущен напрямую (python main.py), __name__ == "__main__";
  • если файл импортирован (import tools), __name__ равно имени модуля — "tools".

Проверим на двух файлах:

# tools.py
print(f"tools: __name__ = {__name__}")
# main.py
import tools

print(f"main: __name__ = {__name__}")
$ python main.py
tools: __name__ = tools
main: __name__ = __main__

Именно это значение и проверяет условие if __name__ == "__main__".

Зачем это нужно: файл как программа и как библиотека

Допустим, в модуле poster.py есть функция публикации в Telegram, ещё какая-то функция и точка входа, которая вызывает обе:

# poster.py
def post_to_telegram(text: str) -> None:
    print(f"Публикую: {text}")


def cleanup_old_posts() -> None:
    print("Удаляю старые посты")


def main() -> None:
    post_to_telegram("Новый пост")
    cleanup_old_posts()


if __name__ == "__main__":
    main()

Запустили python poster.py — выполнились обе функции. А в другом файле можно импортировать только нужное:

from poster import post_to_telegram

post_to_telegram("Привет из другого скрипта")

Без проверки __name__ при импорте выполнился бы весь код верхнего уровня — включая удаление постов, которое вам здесь совсем не нужно. Проверка позволяет одному файлу быть и программой, и библиотекой одновременно.

Запретить использование файла как библиотеки

Бывает и наоборот: файл — это законченная программа, например обработчик постов, и импортировать его как модуль не нужно. Можно сообщить об этом явно:

if __name__ == "__main__":
    main()
else:
    raise ImportError("poster.py — это программа, а не библиотека. Запускайте его напрямую.")

Что делать в ветке else, решаете вы: выбросить ошибку, показать предупреждение или просто ничего не делать.

Ошибка 1. Глобальные переменные без точки входа

Если весь код лежит на верхнем уровне файла, все его переменные глобальные. Добавьте сюда импорт через *:

from helpers import *

limit = 10
# ... сотни строк ...
print(limit)  # а если в helpers тоже есть limit или его меняет импортированная функция?

Импортированный модуль может принести переменную или функцию с тем же именем, и ваше значение тихо перезапишется. Ищется такая ошибка очень долго. Если код обёрнут в функцию main или класс, его переменные локальные, и такие конфликты исключены.

Ошибка 2. Процессы и бесконечный запуск

Самая частая просьба о помощи, которую я получаю: «программа с несколькими процессами падает, а на другой системе работает». Почти всегда не хватает одной строки.

from multiprocessing import Process


def work(n: int) -> None:
    print("worker", n)


p = Process(target=work, args=(1,))
p.start()
p.join()

При способе запуска процессов spawn дочерний процесс заново импортирует главный модуль. Без проверки __name__ он снова доходит до Process(...).start() и пытается запустить новый процесс, что привело бы к бесконечной цепочке. Python это пресекает ошибкой:

RuntimeError: An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.

spawn используется по умолчанию в Windows и macOS, а начиная с Python 3.14 в Linux по умолчанию стал forkserver, который требует того же. Поэтому код, который раньше работал только на Linux, теперь может упасть везде. Исправление — одна строка:

if __name__ == "__main__":
    p = Process(target=work, args=(1,))
    p.start()
    p.join()

__main__.py и запуск пакетов

Похожая идея работает для пакетов. Если положить в пакет файл __main__.py, пакет можно запустить командой python -m имя_пакета: Python выполнит именно этот файл, и внутри него __name__ тоже равно "__main__". Так устроены многие утилиты, например python -m venv или python -m http.server.

Итог

Вопрос Ответ
Что в __name__ при прямом запуске "__main__"
Что при импорте Имя модуля
Зачем проверка Разделить запуск как программы и импорт как библиотеки
Что ломается без неё Лишний код при импорте, конфликты глобальных имён, процессы
Как оформлять Логика в main(), вызов под if __name__ == "__main__"

Две строки кода экономят часы отладки. Хорошая привычка — с самого начала держать логику в функциях и классах, а на верхнем уровне оставлять только точку входа. Как разбивать программу на модули, чтобы импорт не приносил сюрпризов, — подробно в моём курсе по Python ООП. А если вам интересно, как Python расходует память на коллекциях, — загляните в статью почему Python list может съесть всю память.

Источники